X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_keychain/lib/SecItem.cpp diff --git a/libsecurity_keychain/lib/SecItem.cpp b/libsecurity_keychain/lib/SecItem.cpp new file mode 100644 index 00000000..53644f1f --- /dev/null +++ b/libsecurity_keychain/lib/SecItem.cpp @@ -0,0 +1,3700 @@ +/* + * Copyright (c) 2006-2010,2012 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "SecBridge.h" +#include +#include +#include +#include +#include +#include "cssmdatetime.h" +#include "SecItem.h" +#include "SecItemPriv.h" +#include "SecIdentitySearchPriv.h" +#include "SecCertificatePriv.h" +#include "SecCertificatePrivP.h" +#include "TrustAdditions.h" + +#include + +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); + +/* + * _SecProtocolTypeForSecAttrProtocol converts a SecAttrProtocol to a SecProtocolType. + */ +static SecProtocolType +_SecProtocolTypeForSecAttrProtocol( + CFTypeRef protocol) +{ + SecProtocolType result = kSecProtocolTypeAny; + + if (protocol != NULL) { + CFIndex count; + for (count=0; count= infoNumItems) + { + // if we got here, we aren't interested in this item. + valuesPtr[i] = NULL; + } + } + + // now we can make the result array + attrList->count = count; + attrList->attr = (SecKeychainAttribute*) malloc(sizeof(SecKeychainAttribute) * count); + + // fill out the array + int resultPointer = 0; + for (i = 0; i < itemsInDictionary; ++i) + { + if (values[i] != NULL) + { + attrList->attr[resultPointer].tag = tags[i]; + + // we have to clone the data pointer. The caller will need to make sure to throw these away + // with _FreeAttrList when it is done... + 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)); + + // fill out the array. We only need to fill in the tags, since calloc zeros what it returns + 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; + } + + // create the dictionary + dictionaryRef = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + // add the pairs + 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) { + // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant + 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) { + // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant + 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 { + // normal case: attribute contains a string + 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; + } + } + + // cleanup + SecKeychainItemFreeContent(&list, NULL); + free(list.attr); + + return result; +} + + + +// +/* + * _CreateAttributesDictionaryFromGenericPasswordItem creates a CFDictionaryRef using the + * attributes of item. + */ +static OSStatus +_CreateAttributesDictionaryFromGenericPasswordItem( + CFAllocatorRef allocator, + SecKeychainItemRef item, + CFDictionaryRef *dictionary) +{ + // do the basic allocations + CFMutableDictionaryRef dict = NULL; + OSStatus result = _ConvertOldFormatToNewFormat(allocator, gGenericPasswordAttributes, kNumberOfGenericPasswordAttributes, item, dict); + if (result == noErr) // did we complete OK + { + CFDictionaryAddValue(dict, kSecClass, kSecClassGenericPassword); + } + + *dictionary = dict; + + return result; +} + + + +/* + * _CreateAttributesDictionaryFromCertificateItem creates a CFDictionaryRef using the + * attributes of item. + */ +static OSStatus +_CreateAttributesDictionaryFromCertificateItem( + CFAllocatorRef allocator, + SecKeychainItemRef item, + CFDictionaryRef *dictionary) +{ + // do the basic allocations + CFMutableDictionaryRef dict = NULL; + OSStatus result = _ConvertOldFormatToNewFormat(allocator, gCertificateAttributes, kNumberOfCertificateAttributes, item, dict); + if (result == noErr) // did we complete OK + { + CFDictionaryAddValue(dict, kSecClass, kSecClassCertificate); + } + + *dictionary = dict; + + return noErr; +} + +/* + * _CreateAttributesDictionaryFromKeyItem creates a CFDictionaryRef using the + * attributes of item. + */ +static OSStatus +_CreateAttributesDictionaryFromKeyItem( + CFAllocatorRef allocator, + SecKeychainItemRef item, + CFDictionaryRef *dictionary) +{ +#if 0 +//%%%FIXME this ought to work, but the call to SecKeychainCopyContent in _ConvertOldFormatToNewFormat fails. +// Need to rewrite _ConvertOldFormatToNewFormat so that it uses SecKeychainAttributeInfoForItemID and +// SecKeychainItemCopyAttributesAndData to get the attributes, rather than SecKeychainCopyContent. + + if (status) { + goto error_exit; // unable to get the attribute info (i.e. database schema) + } + + status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, NULL, NULL); + + // do the basic allocations + CFMutableDictionaryRef dict = NULL; + OSStatus result = _ConvertOldFormatToNewFormat(allocator, gKeyAttributes, kNumberOfKeyAttributes, item, dict); + if (result == noErr) // did we complete OK + { + 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; // item must have an itemClass + } + + 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; // item must have a keychain, so we can get the attribute info for it + } + + status = SecKeychainAttributeInfoForItemID(keychain, itemID, &info); + if (status) { + goto error_exit; // unable to get the attribute info (i.e. database schema) + } + + status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, NULL, NULL); + if (status) { + goto error_exit; // unable to get the attribute info (i.e. database schema) + } + + 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; jtag[ix]) { + intInfo = &gKeyAttributes[j]; + break; + } + } + if (!intInfo) + continue; + + switch (intInfo->itemRepresentation) + { + case kStringRepresentation: + { + CFStringRef stringRef; + if (intInfo->oldItemType == kSecKeyKeyClass) { + // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant + 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) { + // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant + 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 { + // normal case: attribute contains a string + 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: + { + //%%% FIXME need to convert from a CSSM date string to a CFDateRef here + 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; +} + + +/* + * _CreateAttributesDictionaryFromInternetPasswordItem creates a CFDictionaryRef using the + * attributes of item. + */ +static OSStatus +_CreateAttributesDictionaryFromInternetPasswordItem( + CFAllocatorRef allocator, + SecKeychainItemRef item, + CFDictionaryRef *dictionary) +{ + OSStatus status; + SecKeychainAttribute attr[] = { + { kSecServerItemAttr, 0, NULL }, /* [0] server */ + { kSecSecurityDomainItemAttr, 0, NULL }, /* [1] securityDomain */ + { kSecAccountItemAttr, 0, NULL }, /* [2] account */ + { kSecPathItemAttr, 0, NULL }, /* [3] path */ + { kSecPortItemAttr, 0, NULL }, /* [4] port */ + { kSecProtocolItemAttr, 0, NULL }, /* [5] protocol */ + { kSecAuthenticationTypeItemAttr, 0, NULL }, /* [6] authenticationType */ + { kSecCommentItemAttr, 0, NULL }, /* [7] comment */ + { kSecDescriptionItemAttr, 0, NULL }, /* [8] description */ + { kSecLabelItemAttr, 0, NULL }, /* [9] label */ + { kSecCreationDateItemAttr, 0, NULL }, /* [10] creation date */ + { kSecModDateItemAttr, 0, NULL }, /* [11] modification date */ + { kSecCreatorItemAttr, 0, NULL }, /* [12] creator */ + { kSecTypeItemAttr, 0, NULL }, /* [13] type */ + { kSecInvisibleItemAttr, 0, NULL }, /* [14] invisible */ + { kSecNegativeItemAttr, 0, NULL }, /* [15] negative */ + }; + 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; + + // copy the item's attributes + status = SecKeychainItemCopyContent(item, NULL, &attrList, NULL, NULL); + require_noerr(status, SecKeychainItemCopyContent_failed); + + numValues = 0; + + // add kSecClass + keys[numValues] = kSecClass; + values[numValues] = kSecClassInternetPassword; + ++numValues; + + // add kSecAttrServer + 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; + } + } + + // add kSecAttrSecurityDomain + 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; + } + } + + // add kSecAttrAccount + 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; + } + } + + // add kSecAttrPath + 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; + } + } + + // add kSecAttrPort + if ( attrList.attr[4].length > 0 ) { + keys[numValues] = kSecAttrPort; + values[numValues] = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[4].data); + if ( values[numValues] != NULL ) { + ++numValues; + } + } + + // add kSecAttrProtocol + 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; + } + } + + // add kSecAttrAuthenticationType + 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; + } + } + + // add kSecAttrComment + 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; + } + } + + // add kSecAttrDescription + 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; + } + } + + // add kSecAttrLabel + 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; + } + } + + // add kSecAttrCreationDate + 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; + } + } + + // add kSecAttrModificationDate + 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; + } + } + + // add kSecCreatorItemAttr + 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; + } + } + + // add kSecTypeItemAttr + 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; + } + } + + // add kSecInvisibleItemAttr + 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; + } + } + + // add kSecNegativeItemAttr + 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; + } + } + + // create the dictionary + *dictionary = CFDictionaryCreate(allocator, keys, values, numValues, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + // release the values added to the dictionary + for ( index = 0; index < numValues; ++index ) + { + CFRelease(values[index]); + } + + // and free the attributes + (void) SecKeychainItemFreeContent(&attrList, NULL); + +SecKeychainItemCopyContent_failed: + + return ( status ); +} + + +/* + * _CreateAttributesDictionaryFromItem creates a CFDictionaryRef using the + * attributes of the specified item class and item. + */ +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; +} + + +/* + * _FreeAttrList frees the memory allocated for the SecKeychainAttributeList + * by the _CreateSecKeychainAttributeListFromDictionary function. + */ +static void +_FreeAttrList( + SecKeychainAttributeList *attrListPtr) +{ + UInt32 index; + + if ( attrListPtr != NULL ) { + if ( attrListPtr->attr != NULL ) { + // free any attribute data + for ( index = 0; index < attrListPtr->count; ++index ) { + free(attrListPtr->attr[index].data); + } + // free the attribute array + free(attrListPtr->attr); + } + // free the attribute list + free(attrListPtr); + } +} + +/* + * _CFDataCreateAttribute initializes the SecKeychainAttribute pointed to by + * attr using the data and tag parameters. + * + * The memory for the SecKeychainAttribute's data field is allocated with malloc + * and must be released by the caller (this is normally done by calling _FreeAttrList). + */ +static OSStatus +_CFDataCreateAttribute( + CFDataRef data, + SecKeychainAttrType tag, + SecKeychainAttributePtr attr) +{ + OSStatus status = noErr; + CFRange range; + + // set the attribute tag + attr->tag = tag; + + // determine the attribute length + attr->length = CFDataGetLength(data); + range = CFRangeMake(0, (CFIndex)attr->length); + + // allocate memory for the attribute bytes + attr->data = malloc(attr->length); + require_action(attr->data != NULL, malloc_failed, status = errSecBufferTooSmall); + + // get the attribute bytes + CFDataGetBytes(data, range, (UInt8 *)attr->data); + +malloc_failed: + + return ( status ); +} + +/* + * _CFStringCreateAttribute initializes the SecKeychainAttribute pointed to by + * attr using the string and tag parameters. + * + * The memory for the SecKeychainAttribute's data field is allocated with malloc + * and must be released by the caller (this is normally done by calling _FreeAttrList). + */ +static OSStatus +_CFStringCreateAttribute( + CFStringRef string, + SecKeychainAttrType tag, + SecKeychainAttributePtr attr) +{ + OSStatus status = noErr; + CFRange range; + + // set the attribute tag + attr->tag = tag; + + // determine the attribute length + range = CFRangeMake(0, CFStringGetLength(string)); + CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, FALSE, NULL, 0, (CFIndex *)&attr->length); + + // allocate memory for the attribute bytes + attr->data = malloc(attr->length); + require_action(attr->data != NULL, malloc_failed, status = errSecBufferTooSmall); + + // get the attribute bytes + CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, FALSE, (UInt8 *)attr->data, attr->length, NULL); + +malloc_failed: + + return ( status ); +} + + +/* + * _CreateSecKeychainGenericPasswordAttributeListFromDictionary creates a SecKeychainAttributeList + * from the attribute key/values in attrDictionary. + * + * If this function returns noErr, the pointer to the SecKeychainAttributeList + * must be freed by the caller with _FreeAttrList() + */ +static OSStatus +_CreateSecKeychainGenericPasswordAttributeListFromDictionary( + CFDictionaryRef attrDictionary, + SecKeychainAttributeList **attrList) +{ + return _ConvertNewFormatToOldFormat(NULL, gGenericPasswordAttributes, kNumberOfGenericPasswordAttributes, attrDictionary, *attrList); +} + + +/* + * _CreateSecKeychainCertificateAttributeListFromDictionary creates a SecKeychainAttributeList + * from the attribute key/values in attrDictionary. + * + * If this function returns noErr, the pointer to the SecKeychainAttributeList + * must be freed by the caller with _FreeAttrList() + */ +static OSStatus +_CreateSecKeychainCertificateAttributeListFromDictionary( + CFDictionaryRef attrDictionary, + SecKeychainAttributeList **attrList) +{ + return _ConvertNewFormatToOldFormat(NULL, gCertificateAttributes, kNumberOfCertificateAttributes, attrDictionary, *attrList); +} + + +/* + * _CreateSecKeychainKeyAttributeListFromDictionary creates a SecKeychainAttributeList + * from the attribute key/values in attrDictionary. + * + * If this function returns noErr, the pointer to the SecKeychainAttributeList + * must be freed by the caller with _FreeAttrList() + */ +static OSStatus +_CreateSecKeychainKeyAttributeListFromDictionary( + CFDictionaryRef attrDictionary, + SecKeychainAttributeList **attrList) +{ +#if 0 + //%%%FIXME this function should work for key attributes, but currently doesn't; need to debug + return _ConvertNewFormatToOldFormat(NULL, gKeyAttributes, kNumberOfKeyAttributes, attrDictionary, *attrList); +#else + // explicitly build attribute list for supported key attributes + // NOTE: this code supports only MaxSecKeyAttributes (15) attributes + 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); + + // [0] get the kSecKeyKeyClass value + 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; + + // only use this attribute if we recognize the value! + 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; + } + } + + // [1] get the kSecKeyPrintName string + 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; + } + + // [2] get the kSecKeyPermanent boolean + 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; + } + + // [3] get the kSecKeyLabel string + 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; + } + + // [4] get the kSecKeyApplicationTag data + 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; + } + + // [5] get the kSecKeyKeyType number + 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; + } + } + + // [6] get the kSecKeyKeySizeInBits number + 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; + } + } + + // [7] get the kSecKeyEffectiveKeySize number + 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; + } + } + + // [8] get the kSecKeyEncrypt boolean + 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; + } + } + + // [9] get the kSecKeyDecrypt boolean + 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; + } + } + + // [10] get the kSecKeyDerive boolean + 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; + } + } + + // [11] get the kSecKeySign boolean + 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; + } + } + + // [12] get the kSecKeyVerify boolean + 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; + } + } + + // [13] get the kSecKeyWrap boolean + 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; + } + } + + // [14] get the kSecKeyUnwrap boolean + 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; + } + } + + // return the pointer to the attrList + *attrList = attrListPtr; + + return ( noErr ); + + /***************/ + +malloc_number_failed: +CFDataCreateAttribute_failed: +CFStringCreateAttribute_failed: +malloc_attrPtr_failed: + + // free any attributes + _FreeAttrList(attrListPtr); + +calloc_attrListPtr_failed: + + return ( errSecBufferTooSmall ); + +#endif +} + + +/* + * _CreateSecKeychainInternetPasswordAttributeListFromDictionary creates a SecKeychainAttributeList + * from the attribute key/values in attrDictionary. + * + * If this function returns noErr, the pointer to the SecKeychainAttributeList + * must be freed by the caller with _FreeAttrList() + */ +static OSStatus +_CreateSecKeychainInternetPasswordAttributeListFromDictionary( + CFDictionaryRef attrDictionary, + SecKeychainAttributeList **attrList) +{ + // explicitly build attribute list for supported key attributes + // NOTE: this code supports only MaxSecKeychainAttributes (14) attributes + 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); + + + // [0] get the serverName string + if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrServer, (const void **)&value) ) { + status = _CFStringCreateAttribute((CFStringRef)value, kSecServerItemAttr, &attrListPtr->attr[attrListPtr->count]); + require_noerr_quiet(status, CFStringCreateAttribute_failed); + + ++attrListPtr->count; + } + + // [1] get the securityDomain string + if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrSecurityDomain, (const void **)&value) ) { + status = _CFStringCreateAttribute((CFStringRef)value, kSecSecurityDomainItemAttr, &attrListPtr->attr[attrListPtr->count]); + require_noerr_quiet(status, CFStringCreateAttribute_failed); + + ++attrListPtr->count; + } + + // [2] get the accountName string + if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrAccount, (const void **)&value) ) { + status = _CFStringCreateAttribute((CFStringRef)value, kSecAccountItemAttr, &attrListPtr->attr[attrListPtr->count]); + require_noerr_quiet(status, CFStringCreateAttribute_failed); + + ++attrListPtr->count; + } + + // [3] get the path string + if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrPath, (const void **)&value) ) { + status = _CFStringCreateAttribute((CFStringRef)value, kSecPathItemAttr, &attrListPtr->attr[attrListPtr->count]); + require_noerr_quiet(status, CFStringCreateAttribute_failed); + + ++attrListPtr->count; + } + + // [4] get the port number + 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; + } + + // [5] get the protocol + 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; + } + + // [6] get the authenticationType + 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; + } + + // [7] get the comment string + if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrComment, (const void **)&value) ) { + status = _CFStringCreateAttribute((CFStringRef)value, kSecCommentItemAttr, &attrListPtr->attr[attrListPtr->count]); + require_noerr_quiet(status, CFStringCreateAttribute_failed); + + ++attrListPtr->count; + } + + // [8] get the description string + if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrDescription, (const void **)&value) ) { + status = _CFStringCreateAttribute((CFStringRef)value, kSecDescriptionItemAttr, &attrListPtr->attr[attrListPtr->count]); + require_noerr_quiet(status, CFStringCreateAttribute_failed); + + ++attrListPtr->count; + } + + // [9] get the label string + if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrLabel, (const void **)&value) ) { + status = _CFStringCreateAttribute((CFStringRef)value, kSecLabelItemAttr, &attrListPtr->attr[attrListPtr->count]); + require_noerr_quiet(status, CFStringCreateAttribute_failed); + + ++attrListPtr->count; + } + + // [10] get the creator code + 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; + } + + // [11] get the type code + 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; + } + + // [12] get the invisible flag + 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; + } + + // [13] get the negative flag + 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; + } + + // return the pointer to the attrList + *attrList = attrListPtr; + + return ( noErr ); + + /***************/ + +malloc_authenticationType_failed: +malloc_protocol_failed: +malloc_port_failed: +CFStringCreateAttribute_failed: +malloc_attrPtr_failed: + + // free any attributes + _FreeAttrList(attrListPtr); + +calloc_attrListPtr_failed: + + return ( errSecBufferTooSmall ); +} + + +/* + * _CreateSecKeychainAttributeListFromDictionary creates a SecKeychainAttributeList + * from the attribute key/values in attrDictionary for the specified item class. + * + * If this function returns noErr, the pointer to the SecKeychainAttributeList + * must be freed by the caller with _FreeAttrList() + */ +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; +} + + +/* + * _AppNameFromSecTrustedApplication attempts to pull the name of the + * application/tool from the SecTrustedApplicationRef. + */ +static CFStringRef +_AppNameFromSecTrustedApplication( + CFAllocatorRef alloc, + SecTrustedApplicationRef appRef) +{ + CFStringRef result; + OSStatus status; + CFDataRef appDataRef; + + result = NULL; + + // get the data for item's application/tool + status = SecTrustedApplicationCopyData(appRef, &appDataRef); + if ( status == noErr ) { + CFStringRef path; + + // convert it to a CFString potentially containing the path + path = CFStringCreateWithCString(NULL, (char *)CFDataGetBytePtrVoid(appDataRef), kCFStringEncodingUTF8); + if ( path != NULL ) { + // the path has to start with a "/" and cannot contain "://" + if ( CFStringHasPrefix(path, CFSTR("/")) && (CFStringFind(path, CFSTR("://"), 0).location == kCFNotFound) ) { + CFRange nameRange, compRg; + + nameRange = CFRangeMake(0, CFStringGetLength(path)); + + // remove the trailing slashes (if any) + while ( (nameRange.length > 0) && (CFStringGetCharacterAtIndex(path, nameRange.length - 1) == '/') ) { + nameRange.length --; + } + + if ( nameRange.length > 0 ) { + // find last slash and adjust nameRange be everything after it + 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 ); +} + +/* (This function really belongs in SecIdentity.cpp!) + * + * Returns the public key item corresponding to the identity, if it exists in + * the same keychain as the private key. Note that the public key might not + * exist in the same keychain (e.g. if the identity was imported via PKCS12), + * in which case it will not be found. + */ +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; // identity must have a private key + } + status = SecKeychainItemCopyKeychain(privateKey, &keychain); + if (status) { + goto error_exit; // private key must have a keychain, so we can get the attribute info for it + } + status = SecKeychainAttributeInfoForItemID(keychain, kSecPrivateKeyItemClass, &info); + if (status) { + goto error_exit; // unable to get the attribute info (i.e. database schema) for private keys + } + status = SecKeychainItemCopyAttributesAndData(privateKey, info, NULL, &keyAttrList, NULL, NULL); + if (status) { + goto error_exit; // unable to get the key label attribute for the private key + } + + // use the found kSecKeyLabel attribute from the private key in a separate attribute list for searching + 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; // the private key didn't have the hash of the public key in its kSecKeyLabel + } + status = SecKeychainSearchCreateFromAttributes(keychain, kSecPublicKeyItemClass, &attrList, &search); + if (status) { + goto error_exit; // unable to create the search reference + } + status = SecKeychainSearchCopyNext(search, &publicKey); + if (status) { + goto error_exit; // unable to find the public key + } + + 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; +} + + +/* + * Deletes a keychain item if the current application/tool is the only application/tool + * with decrypt access to that keychain item. If more than one application/tool + * has decrypt access to the keychain item, the item is left on the keychain. + * + * TBD: If more than one app/tool has access to the keychain item, we should remove + * the current app/tool's decrypt access. There's no easy way to do that with + * current keychain APIs without bringing up the security UI. + */ +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)) { + // the item doesn't have any access controls, so delete it normally + return SecKeychainItemDelete(itemRef); + } + + // skip access control checking for web form passwords: + // This permits Safari to manage the removal of all web form passwords, + // regardless of whether they are shared by multiple applications. + 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); + } + } + } + + // copy the access of the keychain item + status = SecKeychainItemCopyAccess(itemRef, &access); + require_noerr(status, SecKeychainItemCopyAccessFailed); + + // copy the decrypt access control lists -- this is what has access to the keychain item + status = SecAccessCopySelectedACLList(access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList); + require_noerr(status, SecAccessCopySelectedACLListFailed); + require_quiet(aclList != NULL, noACLList); + + // get the access control list + acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0); + require_quiet(acl != NULL, noACL); + + // copy the application list, description, and CSSM prompt selector for a given access control list entry + status = SecACLCopySimpleContents(acl, &appList, &description, &promptSelector); + require_noerr(status, SecACLCopySimpleContentsFailed); + require_quiet(appList != NULL, noAppList); + + // does only a single application/tool have decrypt access to this item? + if ( CFArrayGetCount(appList) == 1 ) { + SecTrustedApplicationRef itemAppRef, currentAppRef; + CFStringRef itemAppName, currentAppName; + + // get SecTrustedApplicationRef for item's application/tool + itemAppRef = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(appList, 0); + require(itemAppRef != NULL, noItemAppRef); + + // copy the name out + itemAppName = _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef), itemAppRef); + require(itemAppName != NULL, noAppName); + + // create SecTrustedApplicationRef for current application/tool + status = SecTrustedApplicationCreateFromPath(NULL, ¤tAppRef); + require((status == noErr) && (currentAppRef != NULL), SecTrustedApplicationCreateFromPathFailed); + + // copy the name out + currentAppName = _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef), currentAppRef); + require(currentAppName != NULL, noCurrentAppName); + + // compare the current application/tool's name to this item's application/tool's name to see if we own the decrypt access + if ( CFStringCompare(currentAppName, itemAppName, 0) == kCFCompareEqualTo ) { + // delete the keychain item + 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) +{ + // This function updates a single keychain item, which may be specified as + // a reference, persistent reference or attribute dictionary, with the + // attributes provided. + + 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); + + // validate input item (must be convertible to a SecKeychainItemRef) + if (SecKeychainItemGetTypeID() == itemType || + SecCertificateGetTypeID() == itemType || + SecKeyGetTypeID() == itemType) { + // item is already a reference, retain it + itemToUpdate = (SecKeychainItemRef) CFRetain(item); + } + else if (CFDataGetTypeID() == itemType) { + // item is a persistent reference, must convert it + status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToUpdate); + } + else if (CFDictionaryGetTypeID() == itemType) { + // item is a dictionary + CFTypeRef value = NULL; + if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) { + // kSecValueRef value is a SecKeychainItemRef, retain it + itemToUpdate = (SecKeychainItemRef) CFRetain(value); + } + else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) { + // kSecValuePersistentRef value is a persistent reference, must convert it + status = SecKeychainItemCopyFromPersistentReference((CFDataRef)value, &itemToUpdate); + } + } + else if (SecIdentityGetTypeID() == itemType) { + // item is a certificate + private key; since we can't really change the + // certificate's attributes, assume we want to update the private key + 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); + + // build changeAttrList from changedAttributes dictionary + 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); + } + } + + // get the password + // (if the caller is not updating the password, this value will be NULL) + theData = (CFDataRef)CFDictionaryGetValue(changedAttributes, kSecValueData); + if (theData != NULL) { + require_action(CFDataGetTypeID() == CFGetTypeID(theData), update_failed, status = errSecParam); + } + // update item + status = SecKeychainItemModifyContent(itemToUpdate, + (changeAttrList->count == 0) ? NULL : changeAttrList, + (theData != NULL) ? CFDataGetLength(theData) : 0, + (theData != NULL) ? CFDataGetBytePtrVoid(theData) : NULL); + + // one more thing... update access? + 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) +{ + // This function deletes a single keychain item, which may be specified as + // a reference, persistent reference or attribute dictionary. It will not + // delete non-keychain items or aggregate items (such as a SecIdentityRef); + // it is assumed that the caller will pass identity components separately. + + OSStatus status = noErr; + if (!item) { + return paramErr; + } + + SecKeychainItemRef itemToDelete = NULL; + CFTypeID itemType = CFGetTypeID(item); + if (SecKeychainItemGetTypeID() == itemType || + SecCertificateGetTypeID() == itemType || + SecKeyGetTypeID() == itemType) { + // item is already a reference, retain it + itemToDelete = (SecKeychainItemRef) CFRetain(item); + } + else if (CFDataGetTypeID() == itemType) { + // item is a persistent reference, must convert it + status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToDelete); + } + else if (CFDictionaryGetTypeID() == itemType) { + // item is a dictionary + CFTypeRef value = NULL; + if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) { + // kSecValueRef value is a SecKeychainItemRef, retain it + itemToDelete = (SecKeychainItemRef) CFRetain(value); + } + else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) { + // kSecValuePersistentRef value is a persistent reference, must convert it + 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) +{ + // This function is used when atomically processing multiple items, + // where an overall error result must be returned for the entire operation. + // When newStatus is something other than noErr, we want to keep the "most + // interesting" status (which usually will be newStatus, unless curStatus is + // already set; in that case, newStatus can trump curStatus only by being + // something different than 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) +{ + // CFDictionaryApplierFunction + // This function just takes the given key/value pair, + // and adds it to another dictionary supplied in the context argument. + + 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)) { + // will perform a certificate lookup + itemClass = kSecCertificateItemClass; + if (isIdentity) *isIdentity = true; + } + else if (CFEqual(item, kSecClassKey)) { + // examine second parameter to determine type of key + 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; +} + +// SecItemParams contains a validated set of input parameters, as well as a +// search reference and attribute list built from those parameters. It is +// designed to be allocated with _CreateSecItemParamsFromDictionary, and +// freed with _FreeSecItemParams. + +struct SecItemParams { + CFDictionaryRef query; // caller-supplied query + int numResultTypes; // number of result types requested + int maxMatches; // max number of matches to return + uint32 keyUsage; // key usage(s) requested + Boolean returningAttributes; // true if returning attributes dictionary + Boolean returningData; // true if returning item's data + Boolean returningRef; // true if returning item reference + Boolean returningPersistentRef; // true if returing a persistent reference + Boolean returnAllMatches; // true if we should return all matches + Boolean returnIdentity; // true if we are returning a SecIdentityRef + Boolean trustedOnly; // true if we only return trusted certs + Boolean issuerAndSNToMatch; // true if both issuer and SN were provided + SecItemClass itemClass; // item class for this query + SecPolicyRef policy; // value for kSecMatchPolicy (may be NULL) + SecKeychainRef keychain; // value for kSecUseKeychain (may be NULL) + CFArrayRef useItems; // value for kSecUseItemList (may be NULL) + CFArrayRef itemList; // value for kSecMatchItemList (may be NULL) + CFTypeRef searchList; // value for kSecMatchSearchList (may be NULL) + CFTypeRef matchLimit; // value for kSecMatchLimit (may be NULL) + CFTypeRef emailAddrToMatch; // value for kSecMatchEmailAddressIfPresent (may be NULL) + CFTypeRef validOnDate; // value for kSecMatchValidOnDate (may be NULL) + CFTypeRef keyClass; // value for kSecAttrKeyClass (may be NULL) + CFTypeRef service; // value for kSecAttrService (may be NULL) + CFTypeRef issuer; // value for kSecAttrIssuer (may be NULL) + CFTypeRef serialNumber; // value for kSecAttrSerialNumber (may be NULL) + CFTypeRef search; // search reference for this query (SecKeychainSearchRef or SecIdentitySearchRef) + CFTypeRef assumedKeyClass; // if no kSecAttrKeyClass provided, holds the current class we're searching for + SecKeychainAttributeList *attrList; // attribute list for this query + SecAccessRef access; // access reference (for SecItemAdd only, not used to find items) + CFDataRef itemData; // item data (for SecItemAdd only, not used to find items) + CFTypeRef itemRef; // item reference (to add, update or delete, depending on context) + CFDataRef itemPersistentRef; // item persistent reference (to add, update or delete, depending on context) +}; + +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 was not provided for this key (not an error!) + *value = NULL; + } + else if (!(*value)) { + // provided value is NULL (also not an error!) + return noErr; + } + else { + CFTypeID actualTypeID = CFGetTypeID(*value); + if (!((expectedTypeID == actualTypeID) || (altTypeID && altTypeID == actualTypeID))) { + // provided value does not have the expected (or alternate) CF type ID + if ((expectedTypeID == SecKeychainItemGetTypeID()) && + (actualTypeID == SecKeyGetTypeID() || actualTypeID == SecCertificateGetTypeID())) { + // provided value is a "floating" reference which is not yet in a keychain + CFRetain(*value); + return noErr; + } + return errSecItemInvalidValue; + } + else { + // provided value is OK; retain it + 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); + + // validate input search parameters + 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); + + // validate a subset of input attributes (used to create an appropriate search reference) + 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); + + // must have an item class, unless we have an item list to add + 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; // no key class specified, so start with symmetric key class; will search the others later + } + 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); + + // other input attributes, used for SecItemAdd but not for finding items + require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrAccess, (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit); + if (itemParams->access == NULL) { + // check for the old definition of kSecAttrAccess from SecItem-shim (see ) + require_noerr(status = _ValidateDictionaryEntry(dict, CFSTR("kSecAttrAccess"), (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit); + } + + // validate the payload (password, key or certificate data), used for SecItemAdd but not for finding items + require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueData, (const void **)&itemParams->itemData, CFDataGetTypeID(), CFStringGetTypeID()), error_exit); + + // validate item references + 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) { + // Caller is trying to add or find an item by reference. + // The supported method for doing that is to provide a kSecUseItemList array + // for SecItemAdd, or a kSecMatchItemList array for SecItemCopyMatching et al, + // so add the item reference to those arrays here. + 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); + } + + // determine how to return the result + 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; + + // default is kSecReturnRef if no result types were specified + if (!itemParams->numResultTypes) { + itemParams->returningRef = TRUE; + itemParams->numResultTypes = 1; + } + + // determine if one, some or all matches should be returned (default is kSecMatchLimitOne) + 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 we're returning all matches, then we don't support getting passwords as data (which could require authentication for each) + if ((itemParams->itemClass==kSecInternetPasswordItemClass || itemParams->itemClass==kSecGenericPasswordItemClass) && itemParams->returningData) + status = errSecReturnDataUnsupported; + require_noerr(status, error_exit); + } + + // if useItems was provided, we don't need an attribute list or a search reference for adding, although we definitely need one for searching + if (itemParams->useItems && itemParams->itemClass == 0) { + require_action(false, error_exit, status = noErr); // all done here + } + + // build a SecKeychainAttributeList from the query dictionary for the specified item class + require_noerr(status = _CreateSecKeychainAttributeListFromDictionary(dict, itemParams->itemClass, &itemParams->attrList), error_exit); + + // create a search reference (either a SecKeychainSearchRef or a SecIdentitySearchRef) + if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->emailAddrToMatch) { + // searching for certificates by email address + 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) { + // searching for certificates by issuer and serial number + status = SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(itemParams->searchList, + (CFDataRef)itemParams->issuer, + (CFDataRef)itemParams->serialNumber, + (SecKeychainSearchRef*)&itemParams->search); + } + else if (itemParams->returnIdentity && itemParams->policy) { + // searching for identities by policy + status = SecIdentitySearchCreateWithPolicy(itemParams->policy, + (CFStringRef)itemParams->service, + itemParams->keyUsage, + itemParams->searchList, + itemParams->trustedOnly, + (SecIdentitySearchRef*)&itemParams->search); + } + else if (itemParams->returnIdentity) { + // searching for identities + status = SecIdentitySearchCreate(itemParams->searchList, + itemParams->keyUsage, + (SecIdentitySearchRef*)&itemParams->search); + } + else { + // normal keychain item search + 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 + + // We must specify the access, since a free-floating key won't have one yet by default + SecPointer 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("")); + } + 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) +{ + /* Check for ignorable status codes in leaf certificate's 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) { + // the TP has rejected this CA cert because it's in the leaf position + 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; + + /* Check whether this is the X509 Basic policy, which means chain building */ + props = SecPolicyCopyProperties(policy); + if (props) { + CFTypeRef oid = (CFTypeRef) CFDictionaryGetValue(props, kSecPolicyOid); + if (oid && CFEqual(oid, kSecPolicyAppleX509Basic)) { + needChain = true; + } + } + + if (!needChain) { + /* To make the evaluation as lightweight as possible, specify an empty array + * of keychains which will be searched for certificates. + */ + keychains = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); + status = SecTrustSetKeychains(trust, keychains); + if(status) goto cleanup; + + /* To make the evaluation as lightweight as possible, specify an empty array + * of trusted anchors. + */ + anchors = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); + status = SecTrustSetAnchorCertificates(trust, anchors); + if(status) goto cleanup; + } + + /* All parameters are locked and loaded, ready to evaluate! */ + status = SecTrustEvaluate(trust, &trustResult); + if(status) goto cleanup; + + /* If we didn't provide trust anchors or a way to look for them, + * the evaluation will fail with kSecTrustResultRecoverableTrustFailure. + * However, we can tell whether the policy evaluation succeeded by + * looking at the per-cert status codes in the returned evidence. + */ + status = SecTrustGetResult(trust, &trustResult, &chain, &evidence); + if(status) goto cleanup; + + if (!(trustResult == kSecTrustResultProceed || + trustResult == kSecTrustResultUnspecified || + trustResult == kSecTrustResultRecoverableTrustFailure)) { + /* The evaluation failed in a non-recoverable way */ + status = errSecCertificateCannotOperate; + goto cleanup; + } + + /* If there are no per-cert policy status codes, + * and the cert has not expired, consider it valid for the policy. + */ + 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) +{ + // This function refreshes the search parameters in the specific case where + // the caller is searching for kSecClassKey items but did not provide the + // kSecAttrKeyClass. In that case, params->assumedKeyClass will be set, and + // we must perform separate searches to obtain all results. + + OSStatus status = errSecItemNotFound; + if (!params || !params->assumedKeyClass || !params->query || !item) + return status; + + // Free the previous search reference and attribute list. + if (params->search) + CFRelease(params->search); + params->search = NULL; + _FreeAttrList(params->attrList); + params->attrList = NULL; + + // Make a copy of the query dictionary so we can set the key class parameter. + CFMutableDictionaryRef dict = CFDictionaryCreateMutableCopy(NULL, 0, params->query); + CFRelease(params->query); + params->query = dict; + CFDictionarySetValue(dict, kSecAttrKeyClass, params->assumedKeyClass); + + // Determine the current item class for this search, and the next assumed key class. + 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; + } + + // Rebuild the attribute list for the new key class. + if (_CreateSecKeychainAttributeListFromDictionary(dict, params->itemClass, ¶ms->attrList) == noErr) { + // Create a new search reference for the new attribute list. + if (SecKeychainSearchCreateFromAttributes(params->searchList, + params->itemClass, + (params->attrList->count == 0) ? NULL : params->attrList, + (SecKeychainSearchRef*)¶ms->search) == noErr) { + // Return the first matching item from the new search. + // We won't come back here again until there are no more matching items for this search. + status = SecKeychainSearchCopyNext((SecKeychainSearchRef)params->search, (SecKeychainItemRef*)item); + } + } + return status; +} + + +OSStatus +SecItemSearchCopyNext(SecItemParams *params, CFTypeRef *item) +{ + // Generic "copy next match" function for SecKeychainSearchRef or SecIdentitySearchRef. + // Returns either a SecKeychainItemRef or a SecIdentityRef in the output parameter, + // depending on the type of search reference. + + 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); + // Check if we need to refresh the search for the next key class + 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()) { + // we found a SecIdentityRef, rather than a SecKeychainItemRef; + // replace the found "item" with its associated certificate (which is the + // item we actually want for purposes of getting attributes, data, or a + // persistent data reference), and return the identity separately. + 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) { + // perform string comparisons first + 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; + // certificate item contains string; proceed to next check + } + if (nameStarts) { + CFRange range = CFStringFind(commonName, nameStarts, flags); + if (range.length < 1 || range.location > 1) + goto filterOut; + // certificate item starts with string; proceed to next check + } + if (nameEnds) { + CFRange range = CFStringFind(commonName, nameEnds, flags); + if (range.length < 1 || range.location != (CFStringGetLength(commonName) - CFStringGetLength(nameEnds))) + goto filterOut; + // certificate item ends with string; proceed to next check + } + if (nameExact) { + CFRange range = CFStringFind(commonName, nameExact, flags); + if (range.length < 1 || (CFStringGetLength(commonName) != CFStringGetLength(nameExact))) + goto filterOut; + // certificate item exactly matches string; proceed to next check + } + if (itemParams->returnIdentity) { + // if we already found and returned the identity, we can skip this + if (!foundIdentity) { + status = SecIdentityCreateWithCertificate(itemParams->searchList, (SecCertificateRef) *item, identity); + if (status) goto filterOut; + } + // certificate item is part of an identity; proceed to next check + } + if (itemParams->policy) { + status = _FilterWithPolicy(itemParams->policy, (SecCertificateRef) *item); + if (status) goto filterOut; + // certificate item is valid for specified policy + } + if (itemParams->validOnDate) { + status = _FilterWithDate(itemParams->validOnDate, (SecCertificateRef) *item); + if (status) goto filterOut; + // certificate item is valid for specified date + } + if (itemParams->trustedOnly) { + // if we are getting candidate items from a SecIdentitySearchCreateWithPolicy search, + // their trust has already been validated and we can skip this part. + if (!(foundIdentity && itemParams->returnIdentity && itemParams->policy)) { + status = _FilterWithTrust(itemParams->trustedOnly, (SecCertificateRef) *item); + if (status) goto filterOut; + } + // certificate item is trusted on this system + } + } + if (itemParams->itemList) { + Boolean foundMatch = FALSE; + CFIndex idx, count = CFArrayGetCount(itemParams->itemList); + for (idx=0; idxitemList, 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; + // item was found on provided list + } + + if (foundIdentity && !identity) { + CFRelease(foundIdentity); + } + if (commonName) { + CFRelease(commonName); + } + + // if we get here, consider the item a match + 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) +{ + // Given a found item (which may also be an identity), this function adds + // the requested result types (specified in itemParams) to the appropriate + // container as follows: + // + // 1. If there is only one result type (numResultTypes == 1) and only one + // match requested (maxMatches == 1), set *result directly. + // + // 2. If there are multiple result types (numResultTypes > 1), and only one + // match requested (maxMatches == 1), add each result type to itemDict + // and set itemDict as the value of *result. + // + // 3. If there is only one result type (numResultTypes == 1) and multiple + // possible matches (maxMatches > 1), add the result type to *items + // and set *items as the value of *result. + // + // 4. If there are multiple result types (numResultTypes > 1) and multiple + // possible matches (maxMatches > 1), add each result type to itemDict, + // add itemDict to *items, and set *items as the value of *result. + // + // Note that we allocate *items if needed. + + if (!item || !itemParams || !result) + return paramErr; + + if (itemParams->maxMatches > 1) { + // if we can return more than one item, we must have an array + 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) { + // if we're returning more than one result type, each item we return must be a dictionary + 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; + // since we have an item, allow its actual class to override the query-specified item class + tmpStatus = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL); + if (tmpStatus) { + itemClass = itemParams->itemClass; + } + tmpStatus = _CreateAttributesDictionaryFromItem(allocator, itemClass, item, &attrsDict); + if (attrsDict) { + if (itemDict) { + // add all keys and values from attrsDict to the item dictionary + 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; + + //sudo defaults write /Library/Preferences/com.apple.security SecItemSynchronizable -bool YES + 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; + + // validate input query parameters and create the search reference + SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(query, &status); + require_action(itemParams != NULL, error_exit, itemParams = NULL); + + // find the next match until we hit maxMatches, or no more matches found + while ( !(!itemParams->returnAllMatches && matchCount >= itemParams->maxMatches) && + SecItemSearchCopyNext(itemParams, (CFTypeRef*)&item) == noErr) { + + if (FilterCandidateItem((CFTypeRef*)&item, itemParams, &identity)) + continue; // move on to next item + + ++matchCount; // we have a match + + 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); + //%%%TBI + 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; + + // validate input attribute parameters + SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(attributes, &status); + require_action(itemParams != NULL, error_exit, itemParams = NULL); + + // currently, we don't support adding SecIdentityRef items (an aggregate item class), + // since the private key should already be in a keychain by definition. We could support + // this as a copy operation for the private key if a different keychain is specified, + // but in any case it should try to add the certificate. See . + require_action(!itemParams->returnIdentity, error_exit, status = errSecItemInvalidValue); + + if (!itemParams->useItems) { + // create a single keychain item specified by the input attributes + 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); + + // return results (if requested) + if (result) { + itemParams->maxMatches = 1; // in case kSecMatchLimit was set to > 1 + tmpStatus = AddItemResults(item, NULL, itemParams, allocator, &itemArray, result); + if (tmpStatus && (status == noErr)) + status = tmpStatus; + } + CFRelease(item); + } + else { + // add multiple items which are specified in the itemParams->useItems array. + // -- SecCertificateRef or SecKeyRef items may or may not be in a keychain. + // -- SecKeychainItemRef items are in a keychain (by definition), but may be copied to another keychain. + // -- CFDataRef items are a persistent reference; the represented item may be copied to another keychain. + // + OSStatus aggregateStatus = noErr; + CFIndex ix, count = CFArrayGetCount(itemParams->useItems); + itemParams->maxMatches = (count > 1) ? count : 2; // force results to always be returned as an array + for (ix=0; ix < count; ix++) { + CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->useItems, ix); + if (anItem) { + if (SecCertificateGetTypeID() == CFGetTypeID(anItem)) { + // SecCertificateRef item + 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)) { + // SecKeyRef item + SecKeychainRef itemKeychain = NULL; + tmpStatus = SecKeychainItemCopyKeychain((SecKeychainItemRef)anItem, &itemKeychain); + if (tmpStatus == noErr) { + // key was in a keychain, so we can attempt to copy it + 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 { + // key was not in any keychain, so must be imported + 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 item + 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)) { + // CFDataRef item (persistent reference) + SecKeychainItemRef realItem = NULL; + tmpStatus = SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem); + if (tmpStatus == noErr) { + // persistent reference resolved to a keychain item, so we can attempt to copy it + 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); + } + } + } // end of itemList array loop + status = aggregateStatus; + } // end processing multiple items + +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; + + // run the provided query to get a list of items to update + CFTypeRef results = NULL; + OSStatus status = SecItemCopyMatching(query, &results); + if (status != noErr) + return status; // nothing was matched, or the query was bad + + 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; + + // run the provided query to get a list of items to delete + CFTypeRef results = NULL; + OSStatus status = SecItemCopyMatching(query, &results); + if (status != noErr) + return status; // nothing was matched, or the query was bad + + 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; +}