#include "SecBridge.h"
#include <security_utilities/cfutilities.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/SecKeychainItem.h>
#include <Security/SecCertificate.h>
#include <sys/param.h>
#include "cssmdatetime.h"
#include "SecItem.h"
#include "SecItemPriv.h"
#include "SecIdentitySearchPriv.h"
#include "SecCertificatePriv.h"
#include "SecCertificatePrivP.h"
#include "TrustAdditions.h"
#include <AssertMacros.h>
OSStatus SecItemAdd_osx(CFDictionaryRef attributes, CFTypeRef *result);
OSStatus SecItemCopyMatching_osx(CFDictionaryRef query, CFTypeRef *result);
OSStatus SecItemUpdate_osx(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
OSStatus SecItemDelete_osx(CFDictionaryRef query);
extern "C" {
OSStatus SecItemAdd_ios(CFDictionaryRef attributes, CFTypeRef *result);
OSStatus SecItemCopyMatching_ios(CFDictionaryRef query, CFTypeRef *result);
OSStatus SecItemUpdate_ios(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
OSStatus SecItemDelete_ios(CFDictionaryRef query);
}
#define CFDataGetBytePtrVoid CFDataGetBytePtr
#pragma mark SecItem private utility functions
struct ProtocolAttributeInfo {
const CFTypeRef *protocolValue;
SecProtocolType protocolType;
};
static ProtocolAttributeInfo gProtocolTypes[] = {
{ &kSecAttrProtocolFTP, kSecProtocolTypeFTP },
{ &kSecAttrProtocolFTPAccount, kSecProtocolTypeFTPAccount },
{ &kSecAttrProtocolHTTP, kSecProtocolTypeHTTP },
{ &kSecAttrProtocolIRC, kSecProtocolTypeIRC },
{ &kSecAttrProtocolNNTP, kSecProtocolTypeNNTP },
{ &kSecAttrProtocolPOP3, kSecProtocolTypePOP3 },
{ &kSecAttrProtocolSMTP, kSecProtocolTypeSMTP },
{ &kSecAttrProtocolSOCKS, kSecProtocolTypeSOCKS },
{ &kSecAttrProtocolIMAP, kSecProtocolTypeIMAP },
{ &kSecAttrProtocolLDAP, kSecProtocolTypeLDAP },
{ &kSecAttrProtocolAppleTalk, kSecProtocolTypeAppleTalk },
{ &kSecAttrProtocolAFP, kSecProtocolTypeAFP },
{ &kSecAttrProtocolTelnet, kSecProtocolTypeTelnet },
{ &kSecAttrProtocolSSH, kSecProtocolTypeSSH },
{ &kSecAttrProtocolFTPS, kSecProtocolTypeFTPS },
{ &kSecAttrProtocolHTTPS, kSecProtocolTypeHTTPS },
{ &kSecAttrProtocolHTTPProxy, kSecProtocolTypeHTTPProxy },
{ &kSecAttrProtocolHTTPSProxy, kSecProtocolTypeHTTPSProxy },
{ &kSecAttrProtocolFTPProxy, kSecProtocolTypeFTPProxy },
{ &kSecAttrProtocolSMB, kSecProtocolTypeSMB },
{ &kSecAttrProtocolRTSP, kSecProtocolTypeRTSP },
{ &kSecAttrProtocolRTSPProxy, kSecProtocolTypeRTSPProxy },
{ &kSecAttrProtocolDAAP, kSecProtocolTypeDAAP },
{ &kSecAttrProtocolEPPC, kSecProtocolTypeEPPC },
{ &kSecAttrProtocolIPP, kSecProtocolTypeIPP },
{ &kSecAttrProtocolNNTPS, kSecProtocolTypeNNTPS },
{ &kSecAttrProtocolLDAPS, kSecProtocolTypeLDAPS },
{ &kSecAttrProtocolTelnetS, kSecProtocolTypeTelnetS },
{ &kSecAttrProtocolIMAPS, kSecProtocolTypeIMAPS },
{ &kSecAttrProtocolIRCS, kSecProtocolTypeIRCS },
{ &kSecAttrProtocolPOP3S, kSecProtocolTypePOP3S }
};
static const int kNumberOfProtocolTypes = sizeof(gProtocolTypes) / sizeof(ProtocolAttributeInfo);
static SecProtocolType
_SecProtocolTypeForSecAttrProtocol(
CFTypeRef protocol)
{
SecProtocolType result = kSecProtocolTypeAny;
if (protocol != NULL) {
CFIndex count;
for (count=0; count<kNumberOfProtocolTypes; count++) {
if (CFEqual(protocol, *(gProtocolTypes[count].protocolValue))) {
result = gProtocolTypes[count].protocolType;
break;
}
}
}
return result;
}
static CFTypeRef
_SecAttrProtocolForSecProtocolType(
SecProtocolType protocolType)
{
CFTypeRef result = NULL;
CFIndex count;
for (count=0; count<kNumberOfProtocolTypes; count++) {
if (gProtocolTypes[count].protocolType == protocolType) {
result = *(gProtocolTypes[count].protocolValue);
break;
}
}
return result;
}
struct AuthenticationAttributeInfo {
const CFTypeRef *authValue;
SecAuthenticationType authType;
};
static AuthenticationAttributeInfo gAuthTypes[] = {
{ &kSecAttrAuthenticationTypeNTLM, kSecAuthenticationTypeNTLM },
{ &kSecAttrAuthenticationTypeMSN, kSecAuthenticationTypeMSN },
{ &kSecAttrAuthenticationTypeDPA, kSecAuthenticationTypeDPA },
{ &kSecAttrAuthenticationTypeRPA, kSecAuthenticationTypeRPA },
{ &kSecAttrAuthenticationTypeHTTPBasic, kSecAuthenticationTypeHTTPBasic },
{ &kSecAttrAuthenticationTypeHTTPDigest, kSecAuthenticationTypeHTTPDigest },
{ &kSecAttrAuthenticationTypeHTMLForm, kSecAuthenticationTypeHTMLForm },
{ &kSecAttrAuthenticationTypeDefault, kSecAuthenticationTypeDefault }
};
static const int kNumberOfAuthenticationTypes = sizeof(gAuthTypes) / sizeof(AuthenticationAttributeInfo);
static SecAuthenticationType
_SecAuthenticationTypeForSecAttrAuthenticationType(
CFTypeRef authenticationType)
{
SecAuthenticationType result = kSecAuthenticationTypeAny;
if (authenticationType != NULL) {
CFIndex count;
for (count=0; count<kNumberOfAuthenticationTypes; count++) {
if (CFEqual(authenticationType, *(gAuthTypes[count].authValue))) {
result = gAuthTypes[count].authType;
break;
}
}
}
return result;
}
static CFTypeRef
_SecAttrAuthenticationTypeForSecAuthenticationType(
SecAuthenticationType authenticationType)
{
CFTypeRef result = NULL;
CFIndex count;
for (count=0; count<kNumberOfAuthenticationTypes; count++) {
if (gAuthTypes[count].authType == authenticationType) {
result = *(gAuthTypes[count].authValue);
break;
}
}
return result;
}
struct KeyAlgorithmInfo {
const CFTypeRef *keyType;
UInt32 keyValue;
};
static KeyAlgorithmInfo gKeyTypes[] = {
{ &kSecAttrKeyTypeRSA, CSSM_ALGID_RSA },
{ &kSecAttrKeyTypeDSA, CSSM_ALGID_DSA },
{ &kSecAttrKeyTypeAES, CSSM_ALGID_AES },
{ &kSecAttrKeyTypeDES, CSSM_ALGID_DES },
{ &kSecAttrKeyType3DES, CSSM_ALGID_3DES },
{ &kSecAttrKeyTypeRC4, CSSM_ALGID_RC4 },
{ &kSecAttrKeyTypeRC2, CSSM_ALGID_RC2 },
{ &kSecAttrKeyTypeCAST, CSSM_ALGID_CAST },
{ &kSecAttrKeyTypeECDSA, CSSM_ALGID_ECDSA }
};
static const int kNumberOfKeyTypes = sizeof(gKeyTypes) / sizeof (KeyAlgorithmInfo);
static UInt32 _SecAlgorithmTypeFromSecAttrKeyType(
CFTypeRef keyTypeRef)
{
UInt32 keyAlgValue = 0;
if (CFStringGetTypeID() != CFGetTypeID(keyTypeRef))
return keyAlgValue;
int ix;
for (ix=0; ix<kNumberOfKeyTypes; ix++) {
if (CFEqual(keyTypeRef, *(gKeyTypes[ix].keyType))) {
keyAlgValue = gKeyTypes[ix].keyValue;
return keyAlgValue;
}
}
return keyAlgValue;
}
enum ItemRepresentation
{
kStringRepresentation,
kDataRepresentation,
kNumberRepresentation,
kBooleanRepresentation,
kDateRepresentation
};
struct InternalAttributeListInfo
{
UInt32 oldItemType;
const CFTypeRef *newItemType;
ItemRepresentation itemRepresentation;
};
static InternalAttributeListInfo gGenericPasswordAttributes[] =
{
{ kSecCreationDateItemAttr, &kSecAttrCreationDate, kDateRepresentation },
{ kSecModDateItemAttr, &kSecAttrModificationDate, kDateRepresentation },
{ kSecDescriptionItemAttr, &kSecAttrDescription, kStringRepresentation },
{ kSecCommentItemAttr, &kSecAttrComment, kStringRepresentation },
{ kSecCreatorItemAttr, &kSecAttrCreator, kNumberRepresentation }, { kSecTypeItemAttr, &kSecAttrType, kNumberRepresentation }, { kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation },
{ kSecInvisibleItemAttr, &kSecAttrIsInvisible, kBooleanRepresentation },
{ kSecNegativeItemAttr, &kSecAttrIsNegative, kBooleanRepresentation },
{ kSecAccountItemAttr, &kSecAttrAccount, kStringRepresentation },
{ kSecServiceItemAttr, &kSecAttrService, kStringRepresentation },
{ kSecGenericItemAttr, &kSecAttrGeneric, kDataRepresentation }
};
static const int kNumberOfGenericPasswordAttributes = sizeof(gGenericPasswordAttributes) / sizeof (InternalAttributeListInfo);
static InternalAttributeListInfo gInternetPasswordAttributes[] =
{
{ kSecCreationDateItemAttr, &kSecAttrCreationDate, kDateRepresentation },
{ kSecModDateItemAttr, &kSecAttrModificationDate, kDateRepresentation },
{ kSecDescriptionItemAttr, &kSecAttrDescription, kStringRepresentation },
{ kSecCommentItemAttr, &kSecAttrComment, kStringRepresentation },
{ kSecCreatorItemAttr, &kSecAttrCreator, kNumberRepresentation }, { kSecTypeItemAttr, &kSecAttrType, kNumberRepresentation }, { kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation },
{ kSecInvisibleItemAttr, &kSecAttrIsInvisible, kBooleanRepresentation },
{ kSecNegativeItemAttr, &kSecAttrIsNegative, kBooleanRepresentation },
{ kSecAccountItemAttr, &kSecAttrAccount, kStringRepresentation },
{ kSecSecurityDomainItemAttr, &kSecAttrSecurityDomain, kStringRepresentation },
{ kSecServerItemAttr, &kSecAttrServer, kStringRepresentation },
{ kSecAuthenticationTypeItemAttr, &kSecAttrAuthenticationType, kStringRepresentation }, { kSecPortItemAttr, &kSecAttrPort, kNumberRepresentation },
{ kSecPathItemAttr, &kSecAttrPath, kStringRepresentation }
};
static const int kNumberOfInternetPasswordAttributes = sizeof(gInternetPasswordAttributes) / sizeof (InternalAttributeListInfo);
static InternalAttributeListInfo gCertificateAttributes[] =
{
{ kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation },
{ kSecSubjectItemAttr, &kSecAttrSubject, kDataRepresentation },
{ kSecIssuerItemAttr, &kSecAttrIssuer, kDataRepresentation },
{ kSecSerialNumberItemAttr, &kSecAttrSerialNumber, kDataRepresentation },
{ kSecPublicKeyHashItemAttr, &kSecAttrPublicKeyHash, kDataRepresentation },
{ kSecSubjectKeyIdentifierItemAttr, &kSecAttrSubjectKeyID, kDataRepresentation },
{ kSecCertTypeItemAttr, &kSecAttrCertificateType, kDataRepresentation },
{ kSecCertEncodingItemAttr, &kSecAttrCertificateEncoding, kDataRepresentation }
};
static const int kNumberOfCertificateAttributes = sizeof(gCertificateAttributes) / sizeof(InternalAttributeListInfo);
static InternalAttributeListInfo gKeyAttributes[] =
{
{ kSecKeyKeyClass, &kSecAttrKeyClass, kStringRepresentation }, { kSecKeyPrintName, &kSecAttrLabel, kStringRepresentation }, { kSecKeyPermanent, &kSecAttrIsPermanent, kBooleanRepresentation },
{ kSecKeyLabel, &kSecAttrApplicationLabel, kStringRepresentation }, { kSecKeyApplicationTag, &kSecAttrApplicationTag, kDataRepresentation },
{ kSecKeyKeyType, &kSecAttrKeyType, kStringRepresentation }, { kSecKeyKeySizeInBits, &kSecAttrKeySizeInBits, kNumberRepresentation },
{ kSecKeyEffectiveKeySize, &kSecAttrEffectiveKeySize, kNumberRepresentation },
{ kSecKeyEncrypt, &kSecAttrCanEncrypt, kBooleanRepresentation },
{ kSecKeyDecrypt, &kSecAttrCanDecrypt, kBooleanRepresentation },
{ kSecKeyDerive, &kSecAttrCanDerive, kBooleanRepresentation },
{ kSecKeySign, &kSecAttrCanSign, kBooleanRepresentation },
{ kSecKeyVerify, &kSecAttrCanVerify, kBooleanRepresentation },
{ kSecKeyWrap, &kSecAttrCanWrap, kBooleanRepresentation },
{ kSecKeyUnwrap, &kSecAttrCanUnwrap, kBooleanRepresentation }
};
static const int kNumberOfKeyAttributes = sizeof(gKeyAttributes) / sizeof(InternalAttributeListInfo);
static void* CloneDataByType(ItemRepresentation type, CFTypeRef value, UInt32& length)
{
switch (type)
{
case kStringRepresentation:
{
if (CFStringGetTypeID() != CFGetTypeID(value)) {
length = 0;
return NULL;
}
CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef) value), kCFStringEncodingUTF8) + 1;
char* buffer = (char*) malloc(maxLength);
Boolean converted = CFStringGetCString((CFStringRef) value, buffer, maxLength, kCFStringEncodingUTF8);
if (converted) {
length = strlen(buffer);
}
else {
length = 0;
free(buffer);
buffer = NULL;
}
return buffer;
}
case kDataRepresentation:
{
if (CFDataGetTypeID() != CFGetTypeID(value)) {
length = 0;
return NULL;
}
length = CFDataGetLength((CFDataRef) value);
uint8_t* buffer = (uint8_t*) malloc(length);
CFDataGetBytes((CFDataRef) value, CFRangeMake(0, length), buffer);
return buffer;
}
case kNumberRepresentation:
{
if (CFNumberGetTypeID() != CFGetTypeID(value)) {
length = 0;
return NULL;
}
uint32_t* buffer = (uint32_t*) malloc(sizeof(uint32_t));
Boolean converted = CFNumberGetValue((CFNumberRef) value, kCFNumberSInt32Type, buffer);
if (converted) {
length = sizeof(uint32_t);
}
else {
length = 0;
free(buffer);
buffer = NULL;
}
return buffer;
}
case kBooleanRepresentation:
{
if (CFBooleanGetTypeID() != CFGetTypeID(value)) {
length = 0;
return NULL;
}
uint32_t* buffer = (uint32_t*) malloc(sizeof(uint32_t));
*buffer = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
length = sizeof(uint32_t);
return buffer;
}
case kDateRepresentation:
{
if (CFDateGetTypeID() != CFGetTypeID(value)) {
length = 0;
return NULL;
}
char* buffer = (char*) calloc(1, 32); CSSMDateTimeUtils::CFDateToCssmDate((CFDateRef) value, buffer);
length = strlen(buffer);
return buffer;
}
default:
{
length = 0;
return NULL;
}
}
}
static OSStatus
_ConvertNewFormatToOldFormat(
CFAllocatorRef allocator,
const InternalAttributeListInfo* info,
int infoNumItems,
CFDictionaryRef dictionaryRef,
SecKeychainAttributeList* &attrList
)
{
attrList = (SecKeychainAttributeList*) calloc(1, sizeof(SecKeychainAttributeList));
int itemsInDictionary = CFDictionaryGetCount(dictionaryRef);
CFTypeRef keys[itemsInDictionary];
CFTypeRef values[itemsInDictionary];
CFTypeRef *keysPtr = keys;
CFTypeRef *valuesPtr = values;
CFDictionaryGetKeysAndValues(dictionaryRef, keys, values);
int count = 0;
int i;
SecKeychainAttrType tags[itemsInDictionary];
ItemRepresentation types[itemsInDictionary];
for (i = 0; i < itemsInDictionary; ++i)
{
CFTypeRef key = keysPtr[i];
int j;
for (j = 0; j < infoNumItems; ++j)
{
if (CFEqual(*(info[j].newItemType), key))
{
tags[i] = info[j].oldItemType;
types[i] = info[j].itemRepresentation;
count += 1;
break;
}
}
if (j >= infoNumItems)
{
valuesPtr[i] = NULL;
}
}
attrList->count = count;
attrList->attr = (SecKeychainAttribute*) malloc(sizeof(SecKeychainAttribute) * count);
int resultPointer = 0;
for (i = 0; i < itemsInDictionary; ++i)
{
if (values[i] != NULL)
{
attrList->attr[resultPointer].tag = tags[i];
attrList->attr[resultPointer].data = CloneDataByType(types[i], valuesPtr[i], attrList->attr[resultPointer].length);
resultPointer += 1;
}
}
return noErr;
}
static OSStatus
_ConvertOldFormatToNewFormat(
CFAllocatorRef allocator,
const InternalAttributeListInfo* info,
int infoNumItems,
SecKeychainItemRef itemRef,
CFMutableDictionaryRef& dictionaryRef)
{
SecKeychainAttributeList list;
list.count = infoNumItems;
list.attr = (SecKeychainAttribute*) calloc(infoNumItems, sizeof(SecKeychainAttribute));
int i;
for (i = 0; i < infoNumItems; ++i)
{
list.attr[i].tag = info[i].oldItemType;
}
OSStatus result = SecKeychainItemCopyContent(itemRef, NULL, &list, NULL, NULL);
if (result != noErr)
{
dictionaryRef = NULL;
free(list.attr);
return result;
}
dictionaryRef = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
for (i = 0; i < infoNumItems; ++i)
{
if (list.attr[i].data == NULL)
continue;
switch (info[i].itemRepresentation)
{
case kStringRepresentation:
{
CFStringRef stringRef;
if (info[i].oldItemType == kSecKeyKeyClass) {
uint32_t keyRecordValue = *((uint32_t*)list.attr[i].data);
bool retainString = true;
switch (keyRecordValue) {
case CSSM_DL_DB_RECORD_PUBLIC_KEY :
stringRef = (CFStringRef) kSecAttrKeyClassPublic;
break;
case CSSM_DL_DB_RECORD_PRIVATE_KEY:
stringRef = (CFStringRef) kSecAttrKeyClassPrivate;
break;
case CSSM_DL_DB_RECORD_SYMMETRIC_KEY:
stringRef = (CFStringRef) kSecAttrKeyClassSymmetric;
break;
default:
stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyRecordValue);
break;
}
if (stringRef) {
if (retainString) CFRetain(stringRef);
CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef);
CFRelease(stringRef);
}
}
else if (info[i].oldItemType == kSecKeyKeyType) {
uint32_t keyAlgValue = *((uint32_t*)list.attr[i].data);
bool retainString = true;
switch (keyAlgValue) {
case CSSM_ALGID_RSA :
stringRef = (CFStringRef) kSecAttrKeyTypeRSA;
break;
case CSSM_ALGID_DSA :
stringRef = (CFStringRef) kSecAttrKeyTypeDSA;
break;
case CSSM_ALGID_AES :
stringRef = (CFStringRef) kSecAttrKeyTypeAES;
break;
case CSSM_ALGID_DES :
stringRef = (CFStringRef) kSecAttrKeyTypeDES;
break;
case CSSM_ALGID_3DES :
stringRef = (CFStringRef) kSecAttrKeyType3DES;
break;
case CSSM_ALGID_RC4 :
stringRef = (CFStringRef) kSecAttrKeyTypeRC4;
break;
case CSSM_ALGID_RC2 :
stringRef = (CFStringRef) kSecAttrKeyTypeRC2;
break;
case CSSM_ALGID_CAST :
stringRef = (CFStringRef) kSecAttrKeyTypeCAST;
break;
case CSSM_ALGID_ECDSA :
stringRef = (CFStringRef) kSecAttrKeyTypeECDSA;
break;
default :
stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyAlgValue);
retainString = false;
break;
}
if (stringRef) {
if (retainString) CFRetain(stringRef);
CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef);
CFRelease(stringRef);
}
}
else {
stringRef = CFStringCreateWithBytes(allocator, (UInt8*)list.attr[i].data, list.attr[i].length, kCFStringEncodingUTF8, FALSE);
if (stringRef == NULL)
stringRef = (CFStringRef) CFRetain(kCFNull);
CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef);
CFRelease(stringRef);
}
}
break;
case kDataRepresentation:
{
CFDataRef dataRef = CFDataCreate(allocator, (UInt8*) list.attr[i].data, list.attr[i].length);
if (dataRef == NULL)
dataRef = (CFDataRef) CFRetain(kCFNull);
CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), dataRef);
CFRelease(dataRef);
}
break;
case kNumberRepresentation:
{
CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, list.attr[i].data);
if (numberRef == NULL)
numberRef = (CFNumberRef) CFRetain(kCFNull);
CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), numberRef);
CFRelease(numberRef);
}
break;
case kBooleanRepresentation:
{
uint32_t value = *((uint32_t*)list.attr[i].data);
CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse;
CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), boolRef);
}
break;
case kDateRepresentation:
{
CFDateRef dateRef = NULL;
CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)list.attr[i].data, list.attr[i].length, &dateRef);
if (dateRef == NULL)
dateRef = (CFDateRef) CFRetain(kCFNull);
CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), dateRef);
CFRelease(dateRef);
}
break;
}
}
SecKeychainItemFreeContent(&list, NULL);
free(list.attr);
return result;
}
static OSStatus
_CreateAttributesDictionaryFromGenericPasswordItem(
CFAllocatorRef allocator,
SecKeychainItemRef item,
CFDictionaryRef *dictionary)
{
CFMutableDictionaryRef dict = NULL;
OSStatus result = _ConvertOldFormatToNewFormat(allocator, gGenericPasswordAttributes, kNumberOfGenericPasswordAttributes, item, dict);
if (result == noErr) {
CFDictionaryAddValue(dict, kSecClass, kSecClassGenericPassword);
}
*dictionary = dict;
return result;
}
static OSStatus
_CreateAttributesDictionaryFromCertificateItem(
CFAllocatorRef allocator,
SecKeychainItemRef item,
CFDictionaryRef *dictionary)
{
CFMutableDictionaryRef dict = NULL;
OSStatus result = _ConvertOldFormatToNewFormat(allocator, gCertificateAttributes, kNumberOfCertificateAttributes, item, dict);
if (result == noErr) {
CFDictionaryAddValue(dict, kSecClass, kSecClassCertificate);
}
*dictionary = dict;
return noErr;
}
static OSStatus
_CreateAttributesDictionaryFromKeyItem(
CFAllocatorRef allocator,
SecKeychainItemRef item,
CFDictionaryRef *dictionary)
{
#if 0
if (status) {
goto error_exit; }
status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, NULL, NULL);
CFMutableDictionaryRef dict = NULL;
OSStatus result = _ConvertOldFormatToNewFormat(allocator, gKeyAttributes, kNumberOfKeyAttributes, item, dict);
if (result == noErr) {
CFDictionaryAddValue(dict, kSecClass, kSecClassKey);
}
*dictionary = dict;
return noErr;
#endif
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
unsigned int ix;
SecItemClass itemClass = 0;
UInt32 itemID;
SecKeychainAttributeList *attrList = NULL;
SecKeychainAttributeInfo *info = NULL;
SecKeychainRef keychain = NULL;
OSStatus status = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL);
if (status) {
goto error_exit; }
switch (itemClass)
{
case kSecInternetPasswordItemClass:
itemID = CSSM_DL_DB_RECORD_INTERNET_PASSWORD;
break;
case kSecGenericPasswordItemClass:
itemID = CSSM_DL_DB_RECORD_GENERIC_PASSWORD;
break;
case kSecAppleSharePasswordItemClass:
itemID = CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD;
break;
default:
itemID = itemClass;
break;
}
status = SecKeychainItemCopyKeychain(item, &keychain);
if (status) {
goto error_exit; }
status = SecKeychainAttributeInfoForItemID(keychain, itemID, &info);
if (status) {
goto error_exit; }
status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, NULL, NULL);
if (status) {
goto error_exit; }
for (ix = 0; ix < info->count; ++ix)
{
SecKeychainAttribute *attribute = &attrList->attr[ix];
if (!attribute->length && !attribute->data)
continue;
UInt32 j, count = kNumberOfKeyAttributes;
InternalAttributeListInfo *intInfo = NULL;
for (j=0; j<count; j++) {
if (gKeyAttributes[j].oldItemType == info->tag[ix]) {
intInfo = &gKeyAttributes[j];
break;
}
}
if (!intInfo)
continue;
switch (intInfo->itemRepresentation)
{
case kStringRepresentation:
{
CFStringRef stringRef;
if (intInfo->oldItemType == kSecKeyKeyClass) {
UInt32 keyRecordValue = *((UInt32*)attribute->data);
bool retainString = true;
switch (keyRecordValue) {
case CSSM_DL_DB_RECORD_PUBLIC_KEY :
stringRef = (CFStringRef) kSecAttrKeyClassPublic;
break;
case CSSM_DL_DB_RECORD_PRIVATE_KEY:
stringRef = (CFStringRef) kSecAttrKeyClassPrivate;
break;
case CSSM_DL_DB_RECORD_SYMMETRIC_KEY:
stringRef = (CFStringRef) kSecAttrKeyClassSymmetric;
break;
default:
stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyRecordValue);
break;
}
if (stringRef) {
if (retainString) CFRetain(stringRef);
CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef);
CFRelease(stringRef);
}
}
else if (intInfo->oldItemType == kSecKeyKeyType) {
UInt32 keyAlgValue = *((UInt32*)attribute->data);
bool retainString = true;
switch (keyAlgValue) {
case CSSM_ALGID_RSA :
stringRef = (CFStringRef) kSecAttrKeyTypeRSA;
break;
case CSSM_ALGID_DSA :
stringRef = (CFStringRef) kSecAttrKeyTypeDSA;
break;
case CSSM_ALGID_AES :
stringRef = (CFStringRef) kSecAttrKeyTypeAES;
break;
case CSSM_ALGID_DES :
stringRef = (CFStringRef) kSecAttrKeyTypeDES;
break;
case CSSM_ALGID_3DES :
stringRef = (CFStringRef) kSecAttrKeyType3DES;
break;
case CSSM_ALGID_RC4 :
stringRef = (CFStringRef) kSecAttrKeyTypeRC4;
break;
case CSSM_ALGID_RC2 :
stringRef = (CFStringRef) kSecAttrKeyTypeRC2;
break;
case CSSM_ALGID_CAST :
stringRef = (CFStringRef) kSecAttrKeyTypeCAST;
break;
case CSSM_ALGID_ECDSA :
stringRef = (CFStringRef) kSecAttrKeyTypeECDSA;
break;
default :
stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyAlgValue);
retainString = false;
break;
}
if (stringRef) {
if (retainString) CFRetain(stringRef);
CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef);
CFRelease(stringRef);
}
}
else {
stringRef = CFStringCreateWithBytes(allocator, (UInt8*)attribute->data, attribute->length, kCFStringEncodingUTF8, FALSE);
if (stringRef == NULL)
stringRef = (CFStringRef) CFRetain(kCFNull);
CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef);
CFRelease(stringRef);
}
}
break;
case kDataRepresentation:
{
CFDataRef dataRef = CFDataCreate(allocator, (UInt8*)attribute->data, attribute->length);
if (dataRef == NULL)
dataRef = (CFDataRef) CFRetain(kCFNull);
CFDictionaryAddValue(dict, *(intInfo->newItemType), dataRef);
CFRelease(dataRef);
}
break;
case kNumberRepresentation:
{
CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, attribute->data);
if (numberRef == NULL)
numberRef = (CFNumberRef) CFRetain(kCFNull);
CFDictionaryAddValue(dict, *(intInfo->newItemType), numberRef);
CFRelease(numberRef);
}
break;
case kBooleanRepresentation:
{
UInt32 value = *((UInt32*)attribute->data);
CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse;
CFDictionaryAddValue(dict, *(intInfo->newItemType), boolRef);
}
break;
case kDateRepresentation:
{
CFDateRef dateRef = NULL;
if (dateRef == NULL)
dateRef = (CFDateRef) CFRetain(kCFNull);
CFDictionaryAddValue(dict, *(intInfo->newItemType), dateRef);
CFRelease(dateRef);
}
break;
}
}
CFDictionaryAddValue(dict, kSecClass, kSecClassKey);
error_exit:
if (attrList)
SecKeychainItemFreeAttributesAndData(attrList, NULL);
if (info)
SecKeychainFreeAttributeInfo(info);
if (keychain)
CFRelease(keychain);
*dictionary = dict;
return status;
}
static OSStatus
_CreateAttributesDictionaryFromInternetPasswordItem(
CFAllocatorRef allocator,
SecKeychainItemRef item,
CFDictionaryRef *dictionary)
{
OSStatus status;
SecKeychainAttribute attr[] = {
{ kSecServerItemAttr, 0, NULL },
{ kSecSecurityDomainItemAttr, 0, NULL },
{ kSecAccountItemAttr, 0, NULL },
{ kSecPathItemAttr, 0, NULL },
{ kSecPortItemAttr, 0, NULL },
{ kSecProtocolItemAttr, 0, NULL },
{ kSecAuthenticationTypeItemAttr, 0, NULL },
{ kSecCommentItemAttr, 0, NULL },
{ kSecDescriptionItemAttr, 0, NULL },
{ kSecLabelItemAttr, 0, NULL },
{ kSecCreationDateItemAttr, 0, NULL },
{ kSecModDateItemAttr, 0, NULL },
{ kSecCreatorItemAttr, 0, NULL },
{ kSecTypeItemAttr, 0, NULL },
{ kSecInvisibleItemAttr, 0, NULL },
{ kSecNegativeItemAttr, 0, NULL },
};
SecKeychainAttributeList attrList = { sizeof(attr) / sizeof(SecKeychainAttribute), attr };
CFIndex numValues;
CFIndex index;
CFTypeRef keys[(sizeof(attr) / sizeof(SecKeychainAttribute)) + 2];
CFTypeRef values[(sizeof(attr) / sizeof(SecKeychainAttribute)) + 2];
*dictionary = NULL;
status = SecKeychainItemCopyContent(item, NULL, &attrList, NULL, NULL);
require_noerr(status, SecKeychainItemCopyContent_failed);
numValues = 0;
keys[numValues] = kSecClass;
values[numValues] = kSecClassInternetPassword;
++numValues;
if ( attrList.attr[0].length > 0 ) {
keys[numValues] = kSecAttrServer;
values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[0].data, attrList.attr[0].length, kCFStringEncodingUTF8, FALSE);
if ( values[numValues] != NULL ) {
++numValues;
}
}
if ( attrList.attr[1].length > 0 ) {
keys[numValues] = kSecAttrSecurityDomain;
values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[1].data, attrList.attr[1].length, kCFStringEncodingUTF8, FALSE);
if ( values[numValues] != NULL ) {
++numValues;
}
}
if ( attrList.attr[2].length > 0 ) {
keys[numValues] = kSecAttrAccount;
values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[2].data, attrList.attr[2].length, kCFStringEncodingUTF8, FALSE);
if ( values[numValues] != NULL ) {
++numValues;
}
}
if ( attrList.attr[3].length > 0 ) {
keys[numValues] = kSecAttrPath;
values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[3].data, attrList.attr[3].length, kCFStringEncodingUTF8, FALSE);
if ( values[numValues] != NULL ) {
++numValues;
}
}
if ( attrList.attr[4].length > 0 ) {
keys[numValues] = kSecAttrPort;
values[numValues] = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[4].data);
if ( values[numValues] != NULL ) {
++numValues;
}
}
if ( attrList.attr[5].length > 0 ) {
keys[numValues] = kSecAttrProtocol;
values[numValues] = _SecAttrProtocolForSecProtocolType(*(SecProtocolType*)attrList.attr[5].data);
if ( values[numValues] != NULL ) {
CFRetain(values[numValues]);
++numValues;
}
}
if ( attrList.attr[6].length > 0 ) {
keys[numValues] = kSecAttrAuthenticationType;
values[numValues] = _SecAttrAuthenticationTypeForSecAuthenticationType(*(SecProtocolType*)attrList.attr[6].data);
if ( values[numValues] != NULL ) {
CFRetain(values[numValues]);
++numValues;
}
}
if ( attrList.attr[7].length > 0 ) {
keys[numValues] = kSecAttrComment;
values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[7].data, attrList.attr[7].length, kCFStringEncodingUTF8, FALSE);
if ( values[numValues] != NULL ) {
++numValues;
}
}
if ( attrList.attr[8].length > 0 ) {
keys[numValues] = kSecAttrDescription;
values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[8].data, attrList.attr[8].length, kCFStringEncodingUTF8, FALSE);
if ( values[numValues] != NULL ) {
++numValues;
}
}
if ( attrList.attr[9].length > 0 ) {
keys[numValues] = kSecAttrLabel;
values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[9].data, attrList.attr[9].length, kCFStringEncodingUTF8, FALSE);
if ( values[numValues] != NULL ) {
++numValues;
}
}
if ( attrList.attr[10].length > 0 ) {
CFDateRef creationDate = NULL;
CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList.attr[10].data, attrList.attr[10].length, &creationDate);
keys[numValues] = kSecAttrCreationDate;
values[numValues] = creationDate;
if ( values[numValues] != NULL ) {
++numValues;
}
}
if ( attrList.attr[11].length > 0 ) {
CFDateRef modDate = NULL;
CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList.attr[11].data, attrList.attr[11].length, &modDate);
keys[numValues] = kSecAttrModificationDate;
values[numValues] = modDate;
if ( values[numValues] != NULL ) {
++numValues;
}
}
if ( attrList.attr[12].length > 0 ) {
CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[12].data);
keys[numValues] = kSecAttrCreator;
values[numValues] = numberRef;
if ( values[numValues] != NULL ) {
CFRetain(values[numValues]);
++numValues;
}
}
if ( attrList.attr[13].length > 0 ) {
CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[13].data);
keys[numValues] = kSecAttrType;
values[numValues] = numberRef;
if ( values[numValues] != NULL ) {
CFRetain(values[numValues]);
++numValues;
}
}
if ( attrList.attr[14].length > 0 ) {
uint32_t value = *((uint32_t*)attrList.attr[14].data);
CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse;
keys[numValues] = kSecAttrIsInvisible;
values[numValues] = boolRef;
if ( values[numValues] != NULL ) {
CFRetain(values[numValues]);
++numValues;
}
}
if ( attrList.attr[15].length > 0 ) {
uint32_t value = *((uint32_t*)attrList.attr[15].data);
CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse;
keys[numValues] = kSecAttrIsNegative;
values[numValues] = boolRef;
if ( values[numValues] != NULL ) {
CFRetain(values[numValues]);
++numValues;
}
}
*dictionary = CFDictionaryCreate(allocator, keys, values, numValues, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
for ( index = 0; index < numValues; ++index )
{
CFRelease(values[index]);
}
(void) SecKeychainItemFreeContent(&attrList, NULL);
SecKeychainItemCopyContent_failed:
return ( status );
}
static OSStatus
_CreateAttributesDictionaryFromItem(
CFAllocatorRef allocator,
SecItemClass itemClass,
SecKeychainItemRef item,
CFDictionaryRef *dictionary)
{
switch (itemClass)
{
case kSecInternetPasswordItemClass:
return _CreateAttributesDictionaryFromInternetPasswordItem(allocator, item, dictionary);
case kSecGenericPasswordItemClass:
return _CreateAttributesDictionaryFromGenericPasswordItem(allocator, item, dictionary);
case kSecCertificateItemClass:
return _CreateAttributesDictionaryFromCertificateItem(allocator, item, dictionary);
case kSecPublicKeyItemClass:
case kSecPrivateKeyItemClass:
case kSecSymmetricKeyItemClass:
return _CreateAttributesDictionaryFromKeyItem(allocator, item, dictionary);
default:
*dictionary = NULL;
break;
}
return paramErr;
}
static void
_FreeAttrList(
SecKeychainAttributeList *attrListPtr)
{
UInt32 index;
if ( attrListPtr != NULL ) {
if ( attrListPtr->attr != NULL ) {
for ( index = 0; index < attrListPtr->count; ++index ) {
free(attrListPtr->attr[index].data);
}
free(attrListPtr->attr);
}
free(attrListPtr);
}
}
static OSStatus
_CFDataCreateAttribute(
CFDataRef data,
SecKeychainAttrType tag,
SecKeychainAttributePtr attr)
{
OSStatus status = noErr;
CFRange range;
attr->tag = tag;
attr->length = CFDataGetLength(data);
range = CFRangeMake(0, (CFIndex)attr->length);
attr->data = malloc(attr->length);
require_action(attr->data != NULL, malloc_failed, status = errSecBufferTooSmall);
CFDataGetBytes(data, range, (UInt8 *)attr->data);
malloc_failed:
return ( status );
}
static OSStatus
_CFStringCreateAttribute(
CFStringRef string,
SecKeychainAttrType tag,
SecKeychainAttributePtr attr)
{
OSStatus status = noErr;
CFRange range;
attr->tag = tag;
range = CFRangeMake(0, CFStringGetLength(string));
CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, FALSE, NULL, 0, (CFIndex *)&attr->length);
attr->data = malloc(attr->length);
require_action(attr->data != NULL, malloc_failed, status = errSecBufferTooSmall);
CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, FALSE, (UInt8 *)attr->data, attr->length, NULL);
malloc_failed:
return ( status );
}
static OSStatus
_CreateSecKeychainGenericPasswordAttributeListFromDictionary(
CFDictionaryRef attrDictionary,
SecKeychainAttributeList **attrList)
{
return _ConvertNewFormatToOldFormat(NULL, gGenericPasswordAttributes, kNumberOfGenericPasswordAttributes, attrDictionary, *attrList);
}
static OSStatus
_CreateSecKeychainCertificateAttributeListFromDictionary(
CFDictionaryRef attrDictionary,
SecKeychainAttributeList **attrList)
{
return _ConvertNewFormatToOldFormat(NULL, gCertificateAttributes, kNumberOfCertificateAttributes, attrDictionary, *attrList);
}
static OSStatus
_CreateSecKeychainKeyAttributeListFromDictionary(
CFDictionaryRef attrDictionary,
SecKeychainAttributeList **attrList)
{
#if 0
return _ConvertNewFormatToOldFormat(NULL, gKeyAttributes, kNumberOfKeyAttributes, attrDictionary, *attrList);
#else
const int MaxSecKeyAttributes = 15;
OSStatus status;
CFTypeRef value;
SecKeychainAttributeList *attrListPtr;
attrListPtr = (SecKeychainAttributeList*)calloc(1, sizeof(SecKeychainAttributeList));
require_action(attrListPtr != NULL, calloc_attrListPtr_failed, status = errSecBufferTooSmall);
attrListPtr->attr = (SecKeychainAttribute*)calloc(MaxSecKeyAttributes, sizeof(SecKeychainAttribute));
require_action(attrListPtr->attr != NULL, malloc_attrPtr_failed, status = errSecBufferTooSmall);
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeyClass, (const void **)&value) && value) {
UInt32 keyRecordValue = 0;
if (CFEqual(kSecAttrKeyClassPublic, value))
keyRecordValue = CSSM_DL_DB_RECORD_PUBLIC_KEY;
else if (CFEqual(kSecAttrKeyClassPrivate, value))
keyRecordValue = CSSM_DL_DB_RECORD_PRIVATE_KEY;
else if (CFEqual(kSecAttrKeyClassSymmetric, value))
keyRecordValue = CSSM_DL_DB_RECORD_SYMMETRIC_KEY;
if (keyRecordValue != 0) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecKeyKeyClass;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = keyRecordValue;
++attrListPtr->count;
}
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrLabel, (const void **)&value) && value) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecKeyPrintName, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrIsPermanent, (const void **)&value) && value) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecKeyPermanent;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
++attrListPtr->count;
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrApplicationLabel, (const void **)&value) && value) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecKeyLabel, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrApplicationTag, (const void **)&value) && value) {
if (CFStringGetTypeID() == CFGetTypeID(value))
status = _CFStringCreateAttribute((CFStringRef)value, kSecKeyApplicationTag, &attrListPtr->attr[attrListPtr->count]);
else if (CFDataGetTypeID() == CFGetTypeID(value))
status = _CFDataCreateAttribute((CFDataRef)value, kSecKeyApplicationTag, &attrListPtr->attr[attrListPtr->count]);
else
status = paramErr;
require_noerr_quiet(status, CFDataCreateAttribute_failed);
++attrListPtr->count;
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeyType, (const void **)&value) && value) {
UInt32 keyAlgValue = _SecAlgorithmTypeFromSecAttrKeyType(kSecAttrKeyType);
if (keyAlgValue != 0) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecKeyKeyType;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = keyAlgValue;
++attrListPtr->count;
}
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeySizeInBits, (const void **)&value) && value) {
if (CFNumberGetTypeID() == CFGetTypeID(value)) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecKeyKeySizeInBits;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data);
++attrListPtr->count;
}
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrEffectiveKeySize, (const void **)&value) && value) {
if (CFNumberGetTypeID() == CFGetTypeID(value)) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecKeyEffectiveKeySize;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data);
++attrListPtr->count;
}
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanEncrypt, (const void **)&value) && value) {
if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecKeyEncrypt;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
++attrListPtr->count;
}
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanDecrypt, (const void **)&value) && value) {
if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecKeyDecrypt;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
++attrListPtr->count;
}
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanDerive, (const void **)&value) && value) {
if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecKeyDerive;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
++attrListPtr->count;
}
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanSign, (const void **)&value) && value) {
if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecKeySign;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
++attrListPtr->count;
}
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanVerify, (const void **)&value) && value) {
if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecKeyVerify;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
++attrListPtr->count;
}
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanWrap, (const void **)&value) && value) {
if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecKeyWrap;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
++attrListPtr->count;
}
}
if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanUnwrap, (const void **)&value) && value) {
if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecKeyUnwrap;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
++attrListPtr->count;
}
}
*attrList = attrListPtr;
return ( noErr );
malloc_number_failed:
CFDataCreateAttribute_failed:
CFStringCreateAttribute_failed:
malloc_attrPtr_failed:
_FreeAttrList(attrListPtr);
calloc_attrListPtr_failed:
return ( errSecBufferTooSmall );
#endif
}
static OSStatus
_CreateSecKeychainInternetPasswordAttributeListFromDictionary(
CFDictionaryRef attrDictionary,
SecKeychainAttributeList **attrList)
{
const int MaxSecKeychainAttributes = 14;
OSStatus status;
CFTypeRef value;
SecKeychainAttributeList *attrListPtr;
attrListPtr = (SecKeychainAttributeList*)calloc(1, sizeof(SecKeychainAttributeList));
require_action(attrListPtr != NULL, calloc_attrListPtr_failed, status = errSecBufferTooSmall);
attrListPtr->attr = (SecKeychainAttribute*)calloc(MaxSecKeychainAttributes, sizeof(SecKeychainAttribute));
require_action(attrListPtr->attr != NULL, malloc_attrPtr_failed, status = errSecBufferTooSmall);
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrServer, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecServerItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrSecurityDomain, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecSecurityDomainItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrAccount, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecAccountItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrPath, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecPathItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrPort, (const void **)&value) ) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt16));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecPortItemAttr;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt16);
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt16Type, attrListPtr->attr[attrListPtr->count].data);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrProtocol, (const void **)&value) ) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(SecProtocolType));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_protocol_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecProtocolItemAttr;
attrListPtr->attr[attrListPtr->count].length = sizeof(SecProtocolType);
*(SecProtocolType *)(attrListPtr->attr[attrListPtr->count].data) = _SecProtocolTypeForSecAttrProtocol(value);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrAuthenticationType, (const void **)&value) ) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(SecAuthenticationType));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_authenticationType_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecAuthenticationTypeItemAttr;
attrListPtr->attr[attrListPtr->count].length = sizeof(SecAuthenticationType);
*(SecAuthenticationType *)(attrListPtr->attr[attrListPtr->count].data) = _SecAuthenticationTypeForSecAttrAuthenticationType(value);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrComment, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecCommentItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrDescription, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecDescriptionItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrLabel, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecLabelItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCreator, (const void **)&value) ) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecCreatorItemAttr;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrType, (const void **)&value) ) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecTypeItemAttr;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrIsInvisible, (const void **)&value) ) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecInvisibleItemAttr;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
*(UInt32 *)(attrListPtr->attr[attrListPtr->count].data) = (CFBooleanGetValue((CFBooleanRef)value)) ? 1 : 0;
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrIsNegative, (const void **)&value) ) {
attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
attrListPtr->attr[attrListPtr->count].tag = kSecNegativeItemAttr;
attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
*(UInt32 *)(attrListPtr->attr[attrListPtr->count].data) = (CFBooleanGetValue((CFBooleanRef)value)) ? 1 : 0;
++attrListPtr->count;
}
*attrList = attrListPtr;
return ( noErr );
malloc_authenticationType_failed:
malloc_protocol_failed:
malloc_port_failed:
CFStringCreateAttribute_failed:
malloc_attrPtr_failed:
_FreeAttrList(attrListPtr);
calloc_attrListPtr_failed:
return ( errSecBufferTooSmall );
}
static OSStatus
_CreateSecKeychainAttributeListFromDictionary(
CFDictionaryRef attrDictionary,
SecItemClass itemClass,
SecKeychainAttributeList **attrList)
{
switch (itemClass)
{
case kSecInternetPasswordItemClass:
return _CreateSecKeychainInternetPasswordAttributeListFromDictionary(attrDictionary, attrList);
case kSecGenericPasswordItemClass:
return _CreateSecKeychainGenericPasswordAttributeListFromDictionary(attrDictionary, attrList);
case kSecCertificateItemClass:
return _CreateSecKeychainCertificateAttributeListFromDictionary(attrDictionary, attrList);
case kSecPublicKeyItemClass:
case kSecPrivateKeyItemClass:
case kSecSymmetricKeyItemClass:
return _CreateSecKeychainKeyAttributeListFromDictionary(attrDictionary, attrList);
default:
break;
}
return paramErr;
}
static CFStringRef
_AppNameFromSecTrustedApplication(
CFAllocatorRef alloc,
SecTrustedApplicationRef appRef)
{
CFStringRef result;
OSStatus status;
CFDataRef appDataRef;
result = NULL;
status = SecTrustedApplicationCopyData(appRef, &appDataRef);
if ( status == noErr ) {
CFStringRef path;
path = CFStringCreateWithCString(NULL, (char *)CFDataGetBytePtrVoid(appDataRef), kCFStringEncodingUTF8);
if ( path != NULL ) {
if ( CFStringHasPrefix(path, CFSTR("/")) && (CFStringFind(path, CFSTR("://"), 0).location == kCFNotFound) ) {
CFRange nameRange, compRg;
nameRange = CFRangeMake(0, CFStringGetLength(path));
while ( (nameRange.length > 0) && (CFStringGetCharacterAtIndex(path, nameRange.length - 1) == '/') ) {
nameRange.length --;
}
if ( nameRange.length > 0 ) {
if ( CFStringFindWithOptions(path, CFSTR("/"), nameRange, kCFCompareBackwards, &compRg) ) {
nameRange.length = nameRange.location + nameRange.length - (compRg.location + 1);
nameRange.location = compRg.location + 1;
}
result = CFStringCreateWithSubstring(alloc, path, nameRange);
}
}
CFRelease(path);
}
CFRelease(appDataRef);
}
return ( result );
}
static OSStatus
_SecIdentityCopyPublicKey(
SecIdentityRef identityRef,
SecKeyRef *publicKeyRef)
{
OSStatus status;
UInt32 count;
SecKeychainAttribute attr = { kSecKeyLabel, 0, NULL };
SecKeychainAttributeList attrList = { 1, &attr };
SecKeychainAttributeList *keyAttrList = NULL;
SecKeychainAttributeInfo *info = NULL;
SecKeychainSearchRef search = NULL;
SecKeychainRef keychain = NULL;
SecKeychainItemRef privateKey = NULL;
SecKeychainItemRef publicKey = NULL;
status = SecIdentityCopyPrivateKey(identityRef, (SecKeyRef *)&privateKey);
if (status) {
goto error_exit; }
status = SecKeychainItemCopyKeychain(privateKey, &keychain);
if (status) {
goto error_exit; }
status = SecKeychainAttributeInfoForItemID(keychain, kSecPrivateKeyItemClass, &info);
if (status) {
goto error_exit; }
status = SecKeychainItemCopyAttributesAndData(privateKey, info, NULL, &keyAttrList, NULL, NULL);
if (status) {
goto error_exit; }
for (count = 0; count < keyAttrList->count; count++) {
if (keyAttrList->attr[count].tag == kSecKeyLabel) {
attr.length = keyAttrList->attr[count].length;
attr.data = keyAttrList->attr[count].data;
break;
}
}
if (!attr.length || !attr.data) {
status = errSecNoSuchAttr;
goto error_exit; }
status = SecKeychainSearchCreateFromAttributes(keychain, kSecPublicKeyItemClass, &attrList, &search);
if (status) {
goto error_exit; }
status = SecKeychainSearchCopyNext(search, &publicKey);
if (status) {
goto error_exit; }
if (publicKeyRef)
*publicKeyRef = (SecKeyRef)publicKey;
else
CFRelease(publicKey);
error_exit:
if (status != noErr) {
if (publicKeyRef)
*publicKeyRef = NULL;
if (publicKey)
CFRelease(publicKey);
}
if (search)
CFRelease(search);
if (keyAttrList)
SecKeychainItemFreeAttributesAndData(keyAttrList, NULL);
if (info)
SecKeychainFreeAttributeInfo(info);
if (keychain)
CFRelease(keychain);
if (privateKey)
CFRelease(privateKey);
return status;
}
static OSStatus
_SafeSecKeychainItemDelete(
SecKeychainItemRef itemRef)
{
OSStatus status;
SecAccessRef access;
CFArrayRef aclList;
SecACLRef acl;
CFArrayRef appList;
CFStringRef description;
CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector;
SecItemClass itemClass;
status = SecKeychainItemCopyAttributesAndData(itemRef, NULL, &itemClass, NULL, NULL, NULL);
if (!status && (itemClass == kSecCertificateItemClass || itemClass == kSecPublicKeyItemClass)) {
return SecKeychainItemDelete(itemRef);
}
if (itemClass == kSecInternetPasswordItemClass) {
UInt32 tags[1] = { kSecAuthenticationTypeItemAttr };
SecKeychainAttributeInfo attrInfo = { 1, tags, NULL };
SecKeychainAttributeList *attrs = NULL;
status = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrs, NULL, NULL);
if (!status && attrs) {
bool webFormPassword = (attrs->attr[0].length == 4 && (!memcmp(attrs->attr[0].data, "form", 4)));
SecKeychainItemFreeAttributesAndData(attrs, NULL);
if (webFormPassword) {
return SecKeychainItemDelete(itemRef);
}
}
}
status = SecKeychainItemCopyAccess(itemRef, &access);
require_noerr(status, SecKeychainItemCopyAccessFailed);
status = SecAccessCopySelectedACLList(access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList);
require_noerr(status, SecAccessCopySelectedACLListFailed);
require_quiet(aclList != NULL, noACLList);
acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0);
require_quiet(acl != NULL, noACL);
status = SecACLCopySimpleContents(acl, &appList, &description, &promptSelector);
require_noerr(status, SecACLCopySimpleContentsFailed);
require_quiet(appList != NULL, noAppList);
if ( CFArrayGetCount(appList) == 1 ) {
SecTrustedApplicationRef itemAppRef, currentAppRef;
CFStringRef itemAppName, currentAppName;
itemAppRef = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(appList, 0);
require(itemAppRef != NULL, noItemAppRef);
itemAppName = _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef), itemAppRef);
require(itemAppName != NULL, noAppName);
status = SecTrustedApplicationCreateFromPath(NULL, ¤tAppRef);
require((status == noErr) && (currentAppRef != NULL), SecTrustedApplicationCreateFromPathFailed);
currentAppName = _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef), currentAppRef);
require(currentAppName != NULL, noCurrentAppName);
if ( CFStringCompare(currentAppName, itemAppName, 0) == kCFCompareEqualTo ) {
SecKeychainItemDelete(itemRef);
}
CFRelease(currentAppName);
noCurrentAppName:
CFRelease(currentAppRef);
SecTrustedApplicationCreateFromPathFailed:
CFRelease(itemAppName);
noAppName:
noItemAppRef:
;
}
if ( description ) {
CFRelease(description);
}
CFRelease(appList);
noAppList:
SecACLCopySimpleContentsFailed:
noACL:
CFRelease(aclList);
noACLList:
SecAccessCopySelectedACLListFailed:
CFRelease(access);
SecKeychainItemCopyAccessFailed:
return status;
}
OSStatus
_UpdateKeychainItem(CFTypeRef item, CFDictionaryRef changedAttributes)
{
OSStatus status = noErr;
if (!item) {
return paramErr;
}
SecItemClass itemClass;
SecAccessRef access = NULL;
SecKeychainAttributeList *changeAttrList = NULL;
SecKeychainItemRef itemToUpdate = NULL;
CFDataRef theData = NULL;
CFTypeID itemType = CFGetTypeID(item);
if (SecKeychainItemGetTypeID() == itemType ||
SecCertificateGetTypeID() == itemType ||
SecKeyGetTypeID() == itemType) {
itemToUpdate = (SecKeychainItemRef) CFRetain(item);
}
else if (CFDataGetTypeID() == itemType) {
status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToUpdate);
}
else if (CFDictionaryGetTypeID() == itemType) {
CFTypeRef value = NULL;
if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) {
itemToUpdate = (SecKeychainItemRef) CFRetain(value);
}
else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) {
status = SecKeychainItemCopyFromPersistentReference((CFDataRef)value, &itemToUpdate);
}
}
else if (SecIdentityGetTypeID() == itemType) {
status = SecIdentityCopyPrivateKey((SecIdentityRef)item, (SecKeyRef*)&itemToUpdate);
}
require_action(itemToUpdate != NULL, update_failed, status = errSecInvalidItemRef);
require_noerr(status, update_failed);
status = SecKeychainItemCopyContent(itemToUpdate, &itemClass, NULL, NULL, NULL);
require_noerr(status, update_failed);
switch (itemClass)
{
case kSecInternetPasswordItemClass:
{
status = _CreateSecKeychainInternetPasswordAttributeListFromDictionary(changedAttributes, &changeAttrList);
require_noerr(status, update_failed);
}
break;
case kSecGenericPasswordItemClass:
{
status = _CreateSecKeychainGenericPasswordAttributeListFromDictionary(changedAttributes, &changeAttrList);
require_noerr(status, update_failed);
}
break;
case kSecCertificateItemClass:
{
status = _CreateSecKeychainCertificateAttributeListFromDictionary(changedAttributes, &changeAttrList);
require_noerr(status, update_failed);
}
break;
case kSecPublicKeyItemClass:
case kSecPrivateKeyItemClass:
case kSecSymmetricKeyItemClass:
{
status = _CreateSecKeychainKeyAttributeListFromDictionary(changedAttributes, &changeAttrList);
require_noerr(status, update_failed);
}
}
theData = (CFDataRef)CFDictionaryGetValue(changedAttributes, kSecValueData);
if (theData != NULL) {
require_action(CFDataGetTypeID() == CFGetTypeID(theData), update_failed, status = errSecParam);
}
status = SecKeychainItemModifyContent(itemToUpdate,
(changeAttrList->count == 0) ? NULL : changeAttrList,
(theData != NULL) ? CFDataGetLength(theData) : 0,
(theData != NULL) ? CFDataGetBytePtrVoid(theData) : NULL);
if (CFDictionaryGetValueIfPresent(changedAttributes, kSecAttrAccess, (const void **)&access)) {
status = SecKeychainItemSetAccess(itemToUpdate, access);
}
update_failed:
if (itemToUpdate)
CFRelease(itemToUpdate);
_FreeAttrList(changeAttrList);
return status;
}
OSStatus
_DeleteKeychainItem(CFTypeRef item)
{
OSStatus status = noErr;
if (!item) {
return paramErr;
}
SecKeychainItemRef itemToDelete = NULL;
CFTypeID itemType = CFGetTypeID(item);
if (SecKeychainItemGetTypeID() == itemType ||
SecCertificateGetTypeID() == itemType ||
SecKeyGetTypeID() == itemType) {
itemToDelete = (SecKeychainItemRef) CFRetain(item);
}
else if (CFDataGetTypeID() == itemType) {
status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToDelete);
}
else if (CFDictionaryGetTypeID() == itemType) {
CFTypeRef value = NULL;
if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) {
itemToDelete = (SecKeychainItemRef) CFRetain(value);
}
else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) {
status = SecKeychainItemCopyFromPersistentReference((CFDataRef)value, &itemToDelete);
}
}
if (itemToDelete) {
if (!status) {
status = _SafeSecKeychainItemDelete(itemToDelete);
}
CFRelease(itemToDelete);
}
return status;
}
OSStatus
_DeleteIdentity(SecIdentityRef identity)
{
OSStatus status, result = noErr;
SecKeyRef privateKey = NULL;
SecCertificateRef certificate = NULL;
status = SecIdentityCopyPrivateKey(identity, &privateKey);
if (!status) {
SecKeyRef publicKey = NULL;
status = _SecIdentityCopyPublicKey(identity, &publicKey);
if (!status) {
status = _DeleteKeychainItem(publicKey);
CFRelease(publicKey);
}
status = _DeleteKeychainItem(privateKey);
}
if (privateKey) CFRelease(privateKey);
if (status) result = status;
status = SecIdentityCopyCertificate(identity, &certificate);
if (!status) {
status = _DeleteKeychainItem(certificate);
}
if (certificate) CFRelease(certificate);
if (status) result = status;
return result;
}
OSStatus
_UpdateAggregateStatus(OSStatus newStatus, OSStatus curStatus, OSStatus baseStatus)
{
OSStatus result = curStatus;
if (newStatus != noErr) {
result = newStatus;
if (curStatus != noErr) {
result = (newStatus != baseStatus) ? newStatus : curStatus;
}
}
return result;
}
void
_AddDictValueToOtherDict(const void *key, const void *value, void *context)
{
CFMutableDictionaryRef dict = *((CFMutableDictionaryRef*) context);
if (key && value) {
CFDictionaryAddValue(dict, key, value);
}
}
static CFStringCompareFlags
_StringCompareFlagsFromQuery(CFDictionaryRef query)
{
CFTypeRef value;
CFStringCompareFlags flags = 0;
if (!query) return flags;
if (CFDictionaryGetValueIfPresent(query, kSecMatchSubjectStartsWith, (const void **)&value) ||
CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&value))
flags |= kCFCompareAnchored;
if (CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&value))
flags |= kCFCompareBackwards;
if (CFDictionaryGetValueIfPresent(query, kSecMatchCaseInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
flags |= kCFCompareCaseInsensitive;
if (CFDictionaryGetValueIfPresent(query, kSecMatchDiacriticInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
flags |= kCFCompareDiacriticInsensitive;
if (CFDictionaryGetValueIfPresent(query, kSecMatchWidthInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
flags |= kCFCompareWidthInsensitive;
return flags;
}
static uint32
_CssmKeyUsageFromQuery(CFDictionaryRef query)
{
CFTypeRef value;
uint32 keyUsage = 0;
if (!query) return keyUsage;
if (CFDictionaryGetValueIfPresent(query, kSecAttrCanEncrypt, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
keyUsage |= CSSM_KEYUSE_ENCRYPT;
if (CFDictionaryGetValueIfPresent(query, kSecAttrCanDecrypt, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
keyUsage |= CSSM_KEYUSE_DECRYPT;
if (CFDictionaryGetValueIfPresent(query, kSecAttrCanSign, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
keyUsage |= CSSM_KEYUSE_SIGN;
if (CFDictionaryGetValueIfPresent(query, kSecAttrCanVerify, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
keyUsage |= CSSM_KEYUSE_VERIFY;
if (CFDictionaryGetValueIfPresent(query, kSecAttrCanWrap, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
keyUsage |= CSSM_KEYUSE_WRAP;
if (CFDictionaryGetValueIfPresent(query, kSecAttrCanUnwrap, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
keyUsage |= CSSM_KEYUSE_UNWRAP;
if (CFDictionaryGetValueIfPresent(query, kSecAttrCanDerive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
keyUsage |= CSSM_KEYUSE_DERIVE;
return keyUsage;
}
static SecItemClass
_ConvertItemClass(const void* item, const void* keyClass, Boolean *isIdentity)
{
SecItemClass itemClass = (SecItemClass) 0;
if (isIdentity) *isIdentity = false;
if (CFEqual(item, kSecClassGenericPassword)) {
itemClass = kSecGenericPasswordItemClass;
}
else if (CFEqual(item, kSecClassInternetPassword)) {
itemClass = kSecInternetPasswordItemClass;
}
else if (CFEqual(item, kSecClassCertificate)) {
itemClass = kSecCertificateItemClass;
}
else if (CFEqual(item, kSecClassIdentity)) {
itemClass = kSecCertificateItemClass;
if (isIdentity) *isIdentity = true;
}
else if (CFEqual(item, kSecClassKey)) {
if (!keyClass || CFEqual(keyClass, kSecAttrKeyClassSymmetric)) {
itemClass = kSecSymmetricKeyItemClass;
}
else if (keyClass && CFEqual(keyClass, kSecAttrKeyClassPublic)) {
itemClass = kSecPublicKeyItemClass;
}
else if (keyClass && CFEqual(keyClass, kSecAttrKeyClassPrivate)) {
itemClass = kSecPrivateKeyItemClass;
}
}
return itemClass;
}
struct SecItemParams {
CFDictionaryRef query; int numResultTypes; int maxMatches; uint32 keyUsage; Boolean returningAttributes; Boolean returningData; Boolean returningRef; Boolean returningPersistentRef; Boolean returnAllMatches; Boolean returnIdentity; Boolean trustedOnly; Boolean issuerAndSNToMatch; SecItemClass itemClass; SecPolicyRef policy; SecKeychainRef keychain; CFArrayRef useItems; CFArrayRef itemList; CFTypeRef searchList; CFTypeRef matchLimit; CFTypeRef emailAddrToMatch; CFTypeRef validOnDate; CFTypeRef keyClass; CFTypeRef service; CFTypeRef issuer; CFTypeRef serialNumber; CFTypeRef search; CFTypeRef assumedKeyClass; SecKeychainAttributeList *attrList; SecAccessRef access; CFDataRef itemData; CFTypeRef itemRef; CFDataRef itemPersistentRef; };
static OSStatus
_ValidateDictionaryEntry(CFDictionaryRef dict, CFTypeRef key, const void **value, CFTypeID expectedTypeID, CFTypeID altTypeID)
{
if (!dict || !key || !value || !expectedTypeID)
return paramErr;
if (!CFDictionaryGetValueIfPresent(dict, key, value)) {
*value = NULL;
}
else if (!(*value)) {
return noErr;
}
else {
CFTypeID actualTypeID = CFGetTypeID(*value);
if (!((expectedTypeID == actualTypeID) || (altTypeID && altTypeID == actualTypeID))) {
if ((expectedTypeID == SecKeychainItemGetTypeID()) &&
(actualTypeID == SecKeyGetTypeID() || actualTypeID == SecCertificateGetTypeID())) {
CFRetain(*value);
return noErr;
}
return errSecItemInvalidValue;
}
else {
CFRetain(*value);
}
}
return noErr;
}
static void
_FreeSecItemParams(SecItemParams *itemParams)
{
if (!itemParams)
return;
if (itemParams->query) CFRelease(itemParams->query);
if (itemParams->policy) CFRelease(itemParams->policy);
if (itemParams->keychain) CFRelease(itemParams->keychain);
if (itemParams->useItems) CFRelease(itemParams->useItems);
if (itemParams->itemList) CFRelease(itemParams->itemList);
if (itemParams->searchList) CFRelease(itemParams->searchList);
if (itemParams->matchLimit) CFRelease(itemParams->matchLimit);
if (itemParams->emailAddrToMatch) CFRelease(itemParams->emailAddrToMatch);
if (itemParams->validOnDate) CFRelease(itemParams->validOnDate);
if (itemParams->keyClass) CFRelease(itemParams->keyClass);
if (itemParams->service) CFRelease(itemParams->service);
if (itemParams->issuer) CFRelease(itemParams->issuer);
if (itemParams->serialNumber) CFRelease(itemParams->serialNumber);
if (itemParams->search) CFRelease(itemParams->search);
if (itemParams->access) CFRelease(itemParams->access);
if (itemParams->itemData) CFRelease(itemParams->itemData);
if (itemParams->itemRef) CFRelease(itemParams->itemRef);
if (itemParams->itemPersistentRef) CFRelease(itemParams->itemPersistentRef);
_FreeAttrList(itemParams->attrList);
free(itemParams);
}
static SecItemParams*
_CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error)
{
OSStatus status;
CFTypeRef value = NULL;
SecItemParams *itemParams = (SecItemParams *) malloc(sizeof(SecItemParams));
require_action(itemParams != NULL, error_exit, status = memFullErr);
require_action(dict && (CFDictionaryGetTypeID() == CFGetTypeID(dict)), error_exit, status = paramErr);
memset(itemParams, 0, sizeof(SecItemParams));
itemParams->query = (CFDictionaryRef) CFRetain(dict);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchPolicy, (const void **)&itemParams->policy, SecPolicyGetTypeID(), NULL), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchSearchList, (const void **)&itemParams->searchList, CFArrayGetTypeID(), NULL), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchItemList, (const void **)&itemParams->itemList, CFArrayGetTypeID(), NULL), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchEmailAddressIfPresent, (const void **)&itemParams->emailAddrToMatch, CFStringGetTypeID(), NULL), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchValidOnDate, (const void **)&itemParams->validOnDate, CFDateGetTypeID(), CFNullGetTypeID()), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchLimit, (const void **)&itemParams->matchLimit, CFStringGetTypeID(), CFNumberGetTypeID()), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecUseItemList, (const void **)&itemParams->useItems, CFArrayGetTypeID(), NULL), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecUseKeychain, (const void **)&itemParams->keychain, SecKeychainGetTypeID(), NULL), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrIssuer, (const void **)&itemParams->issuer, CFDataGetTypeID(), NULL), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrSerialNumber, (const void **)&itemParams->serialNumber, CFDataGetTypeID(), NULL), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrService, (const void **)&itemParams->service, CFStringGetTypeID(), NULL), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrKeyClass, (const void **)&itemParams->keyClass, CFStringGetTypeID(), NULL), error_exit);
if (!CFDictionaryGetValueIfPresent(dict, kSecClass, (const void**) &value) && !itemParams->useItems)
require_action(false, error_exit, status = errSecItemClassMissing);
else if (value) {
itemParams->itemClass = _ConvertItemClass(value, itemParams->keyClass, &itemParams->returnIdentity);
if (itemParams->itemClass == kSecSymmetricKeyItemClass && !itemParams->keyClass) {
itemParams->assumedKeyClass = kSecAttrKeyClassSymmetric; }
require_action(!(itemParams->itemClass == 0), error_exit, status = errSecItemClassMissing);
}
itemParams->keyUsage = _CssmKeyUsageFromQuery(dict);
itemParams->trustedOnly = CFDictionaryGetValueIfPresent(dict, kSecMatchTrustedOnly, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
itemParams->issuerAndSNToMatch = (itemParams->issuer != NULL && itemParams->serialNumber != NULL);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrAccess, (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit);
if (itemParams->access == NULL) {
require_noerr(status = _ValidateDictionaryEntry(dict, CFSTR("kSecAttrAccess"), (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit);
}
require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueData, (const void **)&itemParams->itemData, CFDataGetTypeID(), CFStringGetTypeID()), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueRef, (const void **)&itemParams->itemRef, SecKeychainItemGetTypeID(), NULL), error_exit);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecValuePersistentRef, (const void **)&itemParams->itemPersistentRef, CFDataGetTypeID(), NULL), error_exit);
if (itemParams->itemRef || itemParams->itemPersistentRef) {
if (itemParams->useItems) {
CFArrayRef tmpItems = itemParams->useItems;
itemParams->useItems = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpItems);
CFRelease(tmpItems);
} else {
itemParams->useItems = (CFArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
}
if (itemParams->itemRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->useItems, itemParams->itemRef);
if (itemParams->itemPersistentRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->useItems, itemParams->itemPersistentRef);
if (itemParams->itemList) {
CFArrayRef tmpItems = itemParams->itemList;
itemParams->itemList = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpItems);
CFRelease(tmpItems);
} else {
itemParams->itemList = (CFArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
}
if (itemParams->itemRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->itemList, itemParams->itemRef);
if (itemParams->itemPersistentRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->itemList, itemParams->itemPersistentRef);
}
itemParams->numResultTypes = 0;
itemParams->returningRef = CFDictionaryGetValueIfPresent(dict, kSecReturnRef, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
if (itemParams->returningRef) ++itemParams->numResultTypes;
itemParams->returningPersistentRef = CFDictionaryGetValueIfPresent(dict, kSecReturnPersistentRef, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
if (itemParams->returningPersistentRef) ++itemParams->numResultTypes;
itemParams->returningAttributes = CFDictionaryGetValueIfPresent(dict, kSecReturnAttributes, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
if (itemParams->returningAttributes) ++itemParams->numResultTypes;
itemParams->returningData = CFDictionaryGetValueIfPresent(dict, kSecReturnData, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
if (itemParams->returningData) ++itemParams->numResultTypes;
if (!itemParams->numResultTypes) {
itemParams->returningRef = TRUE;
itemParams->numResultTypes = 1;
}
itemParams->maxMatches = 1;
itemParams->returnAllMatches = FALSE;
if (itemParams->matchLimit) {
if (CFStringGetTypeID() == CFGetTypeID(itemParams->matchLimit)) {
itemParams->returnAllMatches = CFEqual(kSecMatchLimitAll, itemParams->matchLimit);
}
else if (CFNumberGetTypeID() == CFGetTypeID(itemParams->matchLimit)) {
CFNumberGetValue((CFNumberRef)itemParams->matchLimit, kCFNumberIntType, &itemParams->maxMatches);
require_action(!(itemParams->maxMatches < 0), error_exit, status = errSecMatchLimitUnsupported);
}
}
if (itemParams->returnAllMatches) {
itemParams->maxMatches = INT32_MAX;
if ((itemParams->itemClass==kSecInternetPasswordItemClass || itemParams->itemClass==kSecGenericPasswordItemClass) && itemParams->returningData)
status = errSecReturnDataUnsupported;
require_noerr(status, error_exit);
}
if (itemParams->useItems && itemParams->itemClass == 0) {
require_action(false, error_exit, status = noErr); }
require_noerr(status = _CreateSecKeychainAttributeListFromDictionary(dict, itemParams->itemClass, &itemParams->attrList), error_exit);
if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->emailAddrToMatch) {
char *nameBuf = (char*)malloc(MAXPATHLEN);
if (!nameBuf) {
status = memFullErr;
}
else if (CFStringGetCString((CFStringRef)itemParams->emailAddrToMatch, nameBuf, (CFIndex)MAXPATHLEN-1, kCFStringEncodingUTF8)) {
status = SecKeychainSearchCreateForCertificateByEmail(itemParams->searchList, (const char *)nameBuf, (SecKeychainSearchRef*)&itemParams->search);
}
else {
status = errSecItemInvalidValue;
}
if (nameBuf) free(nameBuf);
}
else if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->issuerAndSNToMatch) {
status = SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(itemParams->searchList,
(CFDataRef)itemParams->issuer,
(CFDataRef)itemParams->serialNumber,
(SecKeychainSearchRef*)&itemParams->search);
}
else if (itemParams->returnIdentity && itemParams->policy) {
status = SecIdentitySearchCreateWithPolicy(itemParams->policy,
(CFStringRef)itemParams->service,
itemParams->keyUsage,
itemParams->searchList,
itemParams->trustedOnly,
(SecIdentitySearchRef*)&itemParams->search);
}
else if (itemParams->returnIdentity) {
status = SecIdentitySearchCreate(itemParams->searchList,
itemParams->keyUsage,
(SecIdentitySearchRef*)&itemParams->search);
}
else {
status = SecKeychainSearchCreateFromAttributes(itemParams->searchList,
itemParams->itemClass,
(itemParams->attrList->count == 0) ? NULL : itemParams->attrList,
(SecKeychainSearchRef*)&itemParams->search);
}
error_exit:
if (status) {
_FreeSecItemParams(itemParams);
itemParams = NULL;
}
if (error) {
*error = status;
}
return itemParams;
}
OSStatus
_ImportKey(
SecKeyRef keyRef,
SecKeychainRef keychainRef,
SecAccessRef accessRef,
SecKeychainAttributeList *attrList,
SecKeychainItemRef *outItemRef)
{
BEGIN_SECAPI
SecPointer<Access> access;
if (accessRef) {
access = Access::required(accessRef);
}
else {
CFStringRef descriptor = NULL;
if (attrList) {
for (UInt32 index=0; index < attrList->count; index++) {
SecKeychainAttribute attr = attrList->attr[index];
if (attr.tag == kSecKeyPrintName) {
descriptor = CFStringCreateWithBytes(NULL, (const UInt8 *)attr.data, attr.length, kCFStringEncodingUTF8, FALSE);
break;
}
}
}
if (descriptor == NULL) {
descriptor = (CFStringRef) CFRetain(CFSTR("<unknown>"));
}
access = new Access(cfString(descriptor));
CFRelease(descriptor);
}
KeyItem *key = KeyItem::required(keyRef);
Item item = key->importTo(Keychain::optional(keychainRef), access, attrList);
if (outItemRef)
*outItemRef = item->handle();
END_SECAPI
}
Boolean
_CanIgnoreLeafStatusCodes(CSSM_TP_APPLE_EVIDENCE_INFO *evidence)
{
Boolean result = true;
unsigned int i;
for (i=0; i < evidence->NumStatusCodes; i++) {
CSSM_RETURN scode = evidence->StatusCodes[i];
if (scode == CSSMERR_APPLETP_INVALID_CA) {
result = true;
}
else if (ignorableRevocationStatusCode(scode)) {
result = true;
}
else {
result = false;
break;
}
}
return result;
}
OSStatus
_FilterWithPolicy(SecPolicyRef policy, SecCertificateRef cert)
{
CFDictionaryRef props = NULL;
CFArrayRef keychains = NULL;
CFArrayRef anchors = NULL;
CFArrayRef certs = NULL;
CFArrayRef chain = NULL;
SecTrustRef trust = NULL;
SecTrustResultType trustResult;
CSSM_TP_APPLE_EVIDENCE_INFO *evidence = NULL;
Boolean needChain = false;
OSStatus status;
if (!policy || !cert) return paramErr;
certs = CFArrayCreate(NULL, (const void **)&cert, (CFIndex)1, &kCFTypeArrayCallBacks);
status = SecTrustCreateWithCertificates(certs, policy, &trust);
if(status) goto cleanup;
props = SecPolicyCopyProperties(policy);
if (props) {
CFTypeRef oid = (CFTypeRef) CFDictionaryGetValue(props, kSecPolicyOid);
if (oid && CFEqual(oid, kSecPolicyAppleX509Basic)) {
needChain = true;
}
}
if (!needChain) {
keychains = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
status = SecTrustSetKeychains(trust, keychains);
if(status) goto cleanup;
anchors = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
status = SecTrustSetAnchorCertificates(trust, anchors);
if(status) goto cleanup;
}
status = SecTrustEvaluate(trust, &trustResult);
if(status) goto cleanup;
status = SecTrustGetResult(trust, &trustResult, &chain, &evidence);
if(status) goto cleanup;
if (!(trustResult == kSecTrustResultProceed ||
trustResult == kSecTrustResultUnspecified ||
trustResult == kSecTrustResultRecoverableTrustFailure)) {
status = errSecCertificateCannotOperate;
goto cleanup;
}
if((evidence != NULL) && _CanIgnoreLeafStatusCodes(evidence) &&
((evidence[0].StatusBits & CSSM_CERT_STATUS_EXPIRED) == 0) &&
((evidence[0].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET) == 0)) {
status = noErr;
}
else {
status = errSecCertificateCannotOperate;
}
cleanup:
if(props) CFRelease(props);
if(chain) CFRelease(chain);
if(anchors) CFRelease(anchors);
if(keychains) CFRelease(keychains);
if(certs) CFRelease(certs);
if(trust) CFRelease(trust);
return status;
}
OSStatus
_FilterWithDate(CFTypeRef validOnDate, SecCertificateRef cert)
{
if (!validOnDate || !cert) return paramErr;
CFAbsoluteTime at, nb, na;
if (CFGetTypeID(validOnDate) == CFDateGetTypeID())
at = CFDateGetAbsoluteTime((CFDateRef)validOnDate);
else
at = CFAbsoluteTimeGetCurrent();
OSStatus status = noErr;
SecCertificateRefP certP = NULL;
CFDataRef certData = SecCertificateCopyData(cert);
if (certData) {
certP = SecCertificateCreateWithDataP(kCFAllocatorDefault, certData);
}
if (certP) {
nb = SecCertificateNotValidBefore(certP);
na = SecCertificateNotValidAfter(certP);
if(at < nb)
status = errSecCertificateNotValidYet;
else if (at > na)
status = errSecCertificateExpired;
}
else {
status = errSecCertificateCannotOperate;
}
if(certData) CFRelease(certData);
if(certP) CFRelease(certP);
return status;
}
OSStatus
_FilterWithTrust(Boolean trustedOnly, SecCertificateRef cert)
{
if (!cert) return paramErr;
if (!trustedOnly) return noErr;
CFArrayRef certArray = CFArrayCreate(NULL, (const void**)&cert, 1, &kCFTypeArrayCallBacks);
SecPolicyRef policy = SecPolicyCreateWithOID(kSecPolicyAppleX509Basic);
OSStatus status = (policy == NULL) ? errSecPolicyNotFound : errSecSuccess;
if (!status) {
SecTrustRef trust = NULL;
status = SecTrustCreateWithCertificates(certArray, policy, &trust);
if (!status) {
SecTrustResultType trustResult;
status = SecTrustEvaluate(trust, &trustResult);
if (!status) {
if (!(trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified)) {
status = (trustResult == kSecTrustResultDeny) ? errSecTrustSettingDeny : errSecNotTrusted;
}
}
CFRelease(trust);
}
CFRelease(policy);
}
if (certArray) {
CFRelease(certArray);
}
return status;
}
OSStatus
UpdateKeychainSearchAndCopyNext(SecItemParams *params, CFTypeRef *item)
{
OSStatus status = errSecItemNotFound;
if (!params || !params->assumedKeyClass || !params->query || !item)
return status;
if (params->search)
CFRelease(params->search);
params->search = NULL;
_FreeAttrList(params->attrList);
params->attrList = NULL;
CFMutableDictionaryRef dict = CFDictionaryCreateMutableCopy(NULL, 0, params->query);
CFRelease(params->query);
params->query = dict;
CFDictionarySetValue(dict, kSecAttrKeyClass, params->assumedKeyClass);
if (CFEqual(params->assumedKeyClass, kSecAttrKeyClassSymmetric)) {
params->itemClass = kSecSymmetricKeyItemClass;
params->assumedKeyClass = kSecAttrKeyClassPublic;
} else if (CFEqual(params->assumedKeyClass, kSecAttrKeyClassPublic)) {
params->itemClass = kSecPublicKeyItemClass;
params->assumedKeyClass = kSecAttrKeyClassPrivate;
} else {
params->itemClass = kSecPrivateKeyItemClass;
params->assumedKeyClass = NULL;
}
if (_CreateSecKeychainAttributeListFromDictionary(dict, params->itemClass, ¶ms->attrList) == noErr) {
if (SecKeychainSearchCreateFromAttributes(params->searchList,
params->itemClass,
(params->attrList->count == 0) ? NULL : params->attrList,
(SecKeychainSearchRef*)¶ms->search) == noErr) {
status = SecKeychainSearchCopyNext((SecKeychainSearchRef)params->search, (SecKeychainItemRef*)item);
}
}
return status;
}
OSStatus
SecItemSearchCopyNext(SecItemParams *params, CFTypeRef *item)
{
OSStatus status;
CFTypeRef search = (params) ? params->search : NULL;
CFTypeID typeID = (search) ? CFGetTypeID(search) : 0;
if (typeID == SecIdentitySearchGetTypeID()) {
status = SecIdentitySearchCopyNext((SecIdentitySearchRef)search, (SecIdentityRef*)item);
}
else if (typeID == SecKeychainSearchGetTypeID()) {
status = SecKeychainSearchCopyNext((SecKeychainSearchRef)search, (SecKeychainItemRef*)item);
while (status == errSecItemNotFound && params->assumedKeyClass != NULL)
status = UpdateKeychainSearchAndCopyNext(params, item);
}
else {
status = errSecItemNotFound;
}
return status;
}
OSStatus
FilterCandidateItem(CFTypeRef *item, SecItemParams *itemParams, SecIdentityRef *identity)
{
if (!item || *item == NULL || !itemParams)
return errSecItemNotFound;
OSStatus status;
CFStringRef commonName = NULL;
SecIdentityRef foundIdentity = NULL;
if (CFGetTypeID(*item) == SecIdentityGetTypeID()) {
SecCertificateRef certificate;
status = SecIdentityCopyCertificate((SecIdentityRef) *item, &certificate);
if (itemParams->returnIdentity) {
foundIdentity = (SecIdentityRef) *item;
if (identity) {
*identity = foundIdentity;
}
}
else {
CFRelease(*item);
}
*item = (CFTypeRef)certificate;
}
CFDictionaryRef query = itemParams->query;
if (itemParams->itemClass == kSecCertificateItemClass) {
CFStringCompareFlags flags = _StringCompareFlagsFromQuery(query);
CFStringRef nameContains, nameStarts, nameEnds, nameExact;
if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectContains, (const void **)&nameContains))
nameContains = NULL;
if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectStartsWith, (const void **)&nameStarts))
nameStarts = NULL;
if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&nameEnds))
nameEnds = NULL;
if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectWholeString, (const void **)&nameExact))
nameExact = NULL;
if (nameContains || nameStarts || nameEnds || nameExact) {
status = SecCertificateCopyCommonName((SecCertificateRef)*item, &commonName);
if (status || !commonName) goto filterOut;
}
if (nameContains) {
CFRange range = CFStringFind(commonName, nameContains, flags);
if (range.length < 1)
goto filterOut;
}
if (nameStarts) {
CFRange range = CFStringFind(commonName, nameStarts, flags);
if (range.length < 1 || range.location > 1)
goto filterOut;
}
if (nameEnds) {
CFRange range = CFStringFind(commonName, nameEnds, flags);
if (range.length < 1 || range.location != (CFStringGetLength(commonName) - CFStringGetLength(nameEnds)))
goto filterOut;
}
if (nameExact) {
CFRange range = CFStringFind(commonName, nameExact, flags);
if (range.length < 1 || (CFStringGetLength(commonName) != CFStringGetLength(nameExact)))
goto filterOut;
}
if (itemParams->returnIdentity) {
if (!foundIdentity) {
status = SecIdentityCreateWithCertificate(itemParams->searchList, (SecCertificateRef) *item, identity);
if (status) goto filterOut;
}
}
if (itemParams->policy) {
status = _FilterWithPolicy(itemParams->policy, (SecCertificateRef) *item);
if (status) goto filterOut;
}
if (itemParams->validOnDate) {
status = _FilterWithDate(itemParams->validOnDate, (SecCertificateRef) *item);
if (status) goto filterOut;
}
if (itemParams->trustedOnly) {
if (!(foundIdentity && itemParams->returnIdentity && itemParams->policy)) {
status = _FilterWithTrust(itemParams->trustedOnly, (SecCertificateRef) *item);
if (status) goto filterOut;
}
}
}
if (itemParams->itemList) {
Boolean foundMatch = FALSE;
CFIndex idx, count = CFArrayGetCount(itemParams->itemList);
for (idx=0; idx<count; idx++) {
CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->itemList, idx);
SecKeychainItemRef realItem = NULL;
SecCertificateRef aCert = NULL;
if (anItem == NULL) {
continue;
}
if (CFDataGetTypeID() == CFGetTypeID(anItem) &&
noErr == SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem)) {
anItem = realItem;
}
if (SecIdentityGetTypeID() == CFGetTypeID(anItem) &&
noErr == SecIdentityCopyCertificate((SecIdentityRef)anItem, &aCert)) {
anItem = aCert;
}
if (CFEqual(anItem, (CFTypeRef) *item)) {
foundMatch = TRUE;
}
if (aCert) {
CFRelease(aCert);
}
if (realItem) {
CFRelease(realItem);
}
if (foundMatch) {
break;
}
}
if (!foundMatch) goto filterOut;
}
if (foundIdentity && !identity) {
CFRelease(foundIdentity);
}
if (commonName) {
CFRelease(commonName);
}
return noErr;
filterOut:
if (commonName) {
CFRelease(commonName);
}
CFRelease(*item);
*item = NULL;
if (foundIdentity) {
CFRelease(foundIdentity);
if (identity) {
*identity = NULL;
}
}
return errSecItemNotFound;
}
OSStatus
AddItemResults(SecKeychainItemRef item,
SecIdentityRef identity,
SecItemParams *itemParams,
CFAllocatorRef allocator,
CFMutableArrayRef *items,
CFTypeRef *result)
{
if (!item || !itemParams || !result)
return paramErr;
if (itemParams->maxMatches > 1) {
if (!items)
return paramErr;
else if (*items == NULL)
*items = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
}
OSStatus tmpStatus, status = noErr;
CFMutableArrayRef itemArray = (items) ? *items : NULL;
CFMutableDictionaryRef itemDict = NULL;
if (itemParams->numResultTypes > 1) {
itemDict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
if (itemParams->returningRef) {
const void* itemRef = (identity) ? (const void*)identity : (const void*)item;
if (itemDict) {
CFDictionaryAddValue(itemDict, kSecValueRef, itemRef);
}
else if (itemArray) {
CFArrayAppendValue(itemArray, itemRef);
}
else {
*result = CFRetain((CFTypeRef)itemRef);
}
}
if (itemParams->returningPersistentRef) {
CFDataRef persistentRef;
tmpStatus = SecKeychainItemCreatePersistentReference(item, &persistentRef);
if (tmpStatus == noErr) {
if (itemDict) {
CFDictionaryAddValue(itemDict, kSecValuePersistentRef, persistentRef);
}
else if (itemArray) {
CFArrayAppendValue(itemArray, persistentRef);
}
else {
*result = CFRetain(persistentRef);
}
CFRelease(persistentRef);
}
else if (status == noErr) {
status = tmpStatus;
}
}
if (itemParams->returningData) {
UInt32 length;
void *data;
tmpStatus = SecKeychainItemCopyContent(item, NULL, NULL, &length, &data);
if (tmpStatus == noErr) {
CFDataRef dataRef = CFDataCreate(allocator, (UInt8 *)data, length);
if (itemDict) {
CFDictionaryAddValue(itemDict, kSecValueData, dataRef);
}
else if (itemArray) {
CFArrayAppendValue(itemArray, dataRef);
}
else {
*result = CFRetain(dataRef);
}
CFRelease(dataRef);
(void) SecKeychainItemFreeContent(NULL, data);
}
else if (status == noErr) {
status = tmpStatus;
}
}
if (itemParams->returningAttributes) {
CFDictionaryRef attrsDict = NULL;
SecItemClass itemClass;
tmpStatus = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL);
if (tmpStatus) {
itemClass = itemParams->itemClass;
}
tmpStatus = _CreateAttributesDictionaryFromItem(allocator, itemClass, item, &attrsDict);
if (attrsDict) {
if (itemDict) {
CFDictionaryApplyFunction(attrsDict, _AddDictValueToOtherDict, &itemDict);
}
else if (itemArray) {
CFArrayAppendValue(itemArray, attrsDict);
}
else {
*result = CFRetain(attrsDict);
}
CFRelease(attrsDict);
}
if (tmpStatus && (status == noErr)) {
status = tmpStatus;
}
}
if (itemDict) {
if (itemArray) {
CFArrayAppendValue(itemArray, itemDict);
CFRelease(itemDict);
*result = itemArray;
}
else {
*result = itemDict;
}
}
else if (itemArray) {
*result = itemArray;
}
return status;
}
#pragma mark SecItem API functions
static Boolean SecItemSynchronizable(CFDictionaryRef query)
{
static dispatch_once_t onceToken;
static Boolean synchronizable = false;
dispatch_once(&onceToken, ^{
CFTypeRef sync = (CFNumberRef)CFPreferencesCopyValue(CFSTR("SecItemSynchronizable"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
if (sync && CFGetTypeID(sync) == CFBooleanGetTypeID()) {
synchronizable = CFBooleanGetValue((CFBooleanRef)sync);
CFRelease(sync);
}
});
if (synchronizable) {
CFTypeRef value = NULL;
return (_ValidateDictionaryEntry(query, kSecAttrSynchronizable, (const void**)&value, CFBooleanGetTypeID(), NULL) == noErr && value && CFEqual(kCFBooleanTrue, value));
}
return synchronizable;
}
OSStatus
SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result)
{
if (!query || !result)
return paramErr;
if (SecItemSynchronizable(query)) {
return SecItemCopyMatching_ios(query, result);
}
return SecItemCopyMatching_osx(query, result);
}
OSStatus
SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result)
{
if (!attributes)
return paramErr;
else if (result)
*result = NULL;
if (SecItemSynchronizable(attributes)) {
return SecItemAdd_ios(attributes, result);
}
return SecItemAdd_osx(attributes, result);
}
OSStatus
SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
{
if (!query || !attributesToUpdate)
return paramErr;
if (SecItemSynchronizable(query)) {
return SecItemUpdate_ios(query, attributesToUpdate);
}
return SecItemUpdate_osx(query, attributesToUpdate);
}
OSStatus
SecItemDelete(CFDictionaryRef query)
{
if (!query)
return paramErr;
if (SecItemSynchronizable(query)) {
return SecItemDelete_ios(query);
}
return SecItemDelete_osx(query);
}
OSStatus
SecItemCopyMatching_osx(
CFDictionaryRef query,
CFTypeRef *result)
{
if (!query || !result)
return paramErr;
else
*result = NULL;
CFAllocatorRef allocator = CFGetAllocator(query);
CFIndex matchCount = 0;
CFMutableArrayRef itemArray = NULL;
SecKeychainItemRef item = NULL;
SecIdentityRef identity = NULL;
OSStatus tmpStatus, status = noErr;
SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(query, &status);
require_action(itemParams != NULL, error_exit, itemParams = NULL);
while ( !(!itemParams->returnAllMatches && matchCount >= itemParams->maxMatches) &&
SecItemSearchCopyNext(itemParams, (CFTypeRef*)&item) == noErr) {
if (FilterCandidateItem((CFTypeRef*)&item, itemParams, &identity))
continue;
++matchCount;
tmpStatus = AddItemResults(item, identity, itemParams, allocator, &itemArray, result);
if (tmpStatus && (status == noErr))
status = tmpStatus;
if (item) {
CFRelease(item);
item = NULL;
}
if (identity) {
CFRelease(identity);
identity = NULL;
}
}
if (status == noErr)
status = (matchCount > 0) ? errSecSuccess : errSecItemNotFound;
error_exit:
if (status != noErr && result != NULL && *result != NULL) {
CFRelease(*result);
*result = NULL;
}
_FreeSecItemParams(itemParams);
return status;
}
OSStatus
SecItemCopyDisplayNames(
CFArrayRef items,
CFArrayRef *displayNames)
{
BEGIN_SECAPI
Required(items);
Required(displayNames);
return unimpErr;
END_SECAPI
}
OSStatus
SecItemAdd_osx(
CFDictionaryRef attributes,
CFTypeRef *result)
{
if (!attributes)
return paramErr;
else if (result)
*result = NULL;
CFAllocatorRef allocator = CFGetAllocator(attributes);
CFMutableArrayRef itemArray = NULL;
SecKeychainItemRef item = NULL;
OSStatus tmpStatus, status = noErr;
SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(attributes, &status);
require_action(itemParams != NULL, error_exit, itemParams = NULL);
require_action(!itemParams->returnIdentity, error_exit, status = errSecItemInvalidValue);
if (!itemParams->useItems) {
status = SecKeychainItemCreateFromContent(itemParams->itemClass,
itemParams->attrList,
(itemParams->itemData) ? CFDataGetLength(itemParams->itemData) : 0,
(itemParams->itemData) ? CFDataGetBytePtrVoid(itemParams->itemData) : NULL,
itemParams->keychain,
itemParams->access,
&item);
require_noerr(status, error_exit);
if (result) {
itemParams->maxMatches = 1; tmpStatus = AddItemResults(item, NULL, itemParams, allocator, &itemArray, result);
if (tmpStatus && (status == noErr))
status = tmpStatus;
}
CFRelease(item);
}
else {
OSStatus aggregateStatus = noErr;
CFIndex ix, count = CFArrayGetCount(itemParams->useItems);
itemParams->maxMatches = (count > 1) ? count : 2; for (ix=0; ix < count; ix++) {
CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->useItems, ix);
if (anItem) {
if (SecCertificateGetTypeID() == CFGetTypeID(anItem)) {
tmpStatus = SecCertificateAddToKeychain((SecCertificateRef)anItem, itemParams->keychain);
if (!tmpStatus && result) {
tmpStatus = AddItemResults((SecKeychainItemRef)anItem, NULL, itemParams, allocator, &itemArray, result);
}
aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
}
else if (SecKeyGetTypeID() == CFGetTypeID(anItem)) {
SecKeychainRef itemKeychain = NULL;
tmpStatus = SecKeychainItemCopyKeychain((SecKeychainItemRef)anItem, &itemKeychain);
if (tmpStatus == noErr) {
SecKeychainItemRef itemCopy = NULL;
tmpStatus = SecKeychainItemCreateCopy((SecKeychainItemRef)anItem, itemParams->keychain, itemParams->access, &itemCopy);
if (!tmpStatus && result) {
tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
}
if (itemCopy) {
CFRelease(itemCopy);
}
}
else {
SecKeychainItemRef keyItem = NULL;
tmpStatus = _ImportKey((SecKeyRef)anItem, itemParams->keychain, itemParams->access, itemParams->attrList, &keyItem);
if (!tmpStatus && result) {
tmpStatus = AddItemResults(keyItem, NULL, itemParams, allocator, &itemArray, result);
}
if (keyItem) {
CFRelease(keyItem);
}
}
if (itemKeychain) {
CFRelease(itemKeychain);
}
aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
}
else if (SecKeychainItemGetTypeID() == CFGetTypeID(anItem)) {
SecKeychainItemRef itemCopy = NULL;
tmpStatus = SecKeychainItemCreateCopy((SecKeychainItemRef)anItem, itemParams->keychain, itemParams->access, &itemCopy);
if (!tmpStatus && result) {
tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
}
if (itemCopy) {
CFRelease(itemCopy);
}
aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
}
else if (CFDataGetTypeID() == CFGetTypeID(anItem)) {
SecKeychainItemRef realItem = NULL;
tmpStatus = SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem);
if (tmpStatus == noErr) {
SecKeychainItemRef itemCopy = NULL;
tmpStatus = SecKeychainItemCreateCopy(realItem, itemParams->keychain, itemParams->access, &itemCopy);
if (!tmpStatus && result) {
tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
}
if (itemCopy) {
CFRelease(itemCopy);
}
}
if (realItem) {
CFRelease(realItem);
}
aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
}
}
} status = aggregateStatus;
}
error_exit:
if (status != noErr && result != NULL && *result != NULL) {
CFRelease(*result);
*result = NULL;
}
_FreeSecItemParams(itemParams);
return status;
}
OSStatus
SecItemUpdate_osx(
CFDictionaryRef query,
CFDictionaryRef attributesToUpdate)
{
if (!query || !attributesToUpdate)
return paramErr;
CFTypeRef results = NULL;
OSStatus status = SecItemCopyMatching(query, &results);
if (status != noErr)
return status;
CFArrayRef items = NULL;
if (CFArrayGetTypeID() == CFGetTypeID(results)) {
items = (CFArrayRef) results;
}
else {
items = CFArrayCreate(NULL, &results, 1, &kCFTypeArrayCallBacks);
CFRelease(results);
}
OSStatus result = noErr;
CFIndex ix, count = CFArrayGetCount(items);
for (ix=0; ix < count; ix++) {
CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(items, ix);
if (anItem) {
status = _UpdateKeychainItem(anItem, attributesToUpdate);
result = _UpdateAggregateStatus(status, result, noErr);
}
}
if (items) {
CFRelease(items);
}
return result;
}
OSStatus
SecItemDelete_osx(
CFDictionaryRef query)
{
if (!query)
return paramErr;
CFTypeRef results = NULL;
OSStatus status = SecItemCopyMatching(query, &results);
if (status != noErr)
return status;
CFArrayRef items = NULL;
if (CFArrayGetTypeID() == CFGetTypeID(results)) {
items = (CFArrayRef) results;
}
else {
items = CFArrayCreate(NULL, &results, 1, &kCFTypeArrayCallBacks);
CFRelease(results);
}
OSStatus result = noErr;
CFIndex ix, count = CFArrayGetCount(items);
for (ix=0; ix < count; ix++) {
CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(items, ix);
if (anItem) {
if (SecIdentityGetTypeID() == CFGetTypeID(anItem)) {
status = _DeleteIdentity((SecIdentityRef)anItem);
}
else {
status = _DeleteKeychainItem(anItem);
}
result = _UpdateAggregateStatus(status, result, noErr);
}
}
if (items)
CFRelease(items);
return result;
}