X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/e3d460c9de4426da6c630c3ae3f46173a99f82d8..07691282a056c4efea71e1e505527601e8cc166b:/OSX/libsecurity_keychain/lib/SecItem.cpp diff --git a/OSX/libsecurity_keychain/lib/SecItem.cpp b/OSX/libsecurity_keychain/lib/SecItem.cpp index 173eb892..ad88d453 100644 --- a/OSX/libsecurity_keychain/lib/SecItem.cpp +++ b/OSX/libsecurity_keychain/lib/SecItem.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved. + * Copyright (c) 2006-2017 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "cssmdatetime.h" #include "SecItem.h" @@ -35,11 +36,14 @@ #include "SecIdentitySearchPriv.h" #include "SecKeychainPriv.h" #include "SecCertificatePriv.h" -#include "SecCertificatePrivP.h" #include "TrustAdditions.h" +#include "TrustSettingsSchema.h" +#include +#include "utilities/array_size.h" #include #include +#include #include #include @@ -47,6 +51,12 @@ #include #include +#include +#include +#include +#include + + const uint8_t kUUIDStringLength = 36; OSStatus SecItemAdd_osx(CFDictionaryRef attributes, CFTypeRef *result); @@ -59,15 +69,19 @@ 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); +OSStatus SecItemUpdateTokenItems_ios(CFTypeRef tokenID, CFArrayRef tokenItemsAttributes); + -CFTypeRef SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes); -CFTypeRef SecItemCopyMergedResults(CFDictionaryRef query, CFTypeRef result_osx, CFTypeRef result_ios); OSStatus SecItemValidateAppleApplicationGroupAccess(CFStringRef group); CFDictionaryRef SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass, bool iOSOut, bool pruneMatch, bool pruneSync, bool pruneReturn, bool pruneData, bool pruneAccess); + +bool _SecItemParsePersistentRef(CFDataRef persistent_ref, CFStringRef *return_class, + long long int *return_rowid, CFDictionaryRef *return_token_attrs); } static Boolean SecItemSynchronizable(CFDictionaryRef query); +static CFArrayRef _CopyMatchingIssuers(CFArrayRef issuers); static void secitemlog(int priority, const char *format, ...) { @@ -534,13 +548,10 @@ _ConvertNewFormatToOldFormat( // make storage to extract the dictionary items CFIndex itemsInDictionary = CFDictionaryGetCount(dictionaryRef); - CFTypeRef keys[itemsInDictionary]; - CFTypeRef values[itemsInDictionary]; + std::vector keys(itemsInDictionary); + std::vector values(itemsInDictionary); - CFTypeRef *keysPtr = keys; - CFTypeRef *valuesPtr = values; - - CFDictionaryGetKeysAndValues(dictionaryRef, keys, values); + CFDictionaryGetKeysAndValues(dictionaryRef, keys.data(), values.data()); // count the number of items we are interested in CFIndex count = 0; @@ -548,12 +559,12 @@ _ConvertNewFormatToOldFormat( // since this is one of those nasty order n^2 loops, we cache as much stuff as possible so that // we don't pay the price for this twice - SecKeychainAttrType tags[itemsInDictionary]; - ItemRepresentation types[itemsInDictionary]; + std::vector tags(itemsInDictionary); + std::vector types(itemsInDictionary); for (i = 0; i < itemsInDictionary; ++i) { - CFTypeRef key = keysPtr[i]; + CFTypeRef key = keys[i]; int j; for (j = 0; j < infoNumItems; ++j) @@ -570,13 +581,13 @@ _ConvertNewFormatToOldFormat( if (j >= infoNumItems) { // if we got here, we aren't interested in this item. - valuesPtr[i] = NULL; + values[i] = NULL; } } // now we can make the result array attrList->count = (UInt32)count; - attrList->attr = (SecKeychainAttribute*) malloc(sizeof(SecKeychainAttribute) * count); + attrList->attr = (count > 0) ? (SecKeychainAttribute*) malloc(sizeof(SecKeychainAttribute) * count) : NULL; // fill out the array int resultPointer = 0; @@ -588,7 +599,7 @@ _ConvertNewFormatToOldFormat( // 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); + attrList->attr[resultPointer].data = CloneDataByType(types[i], values[i], attrList->attr[resultPointer].length); resultPointer += 1; } } @@ -655,6 +666,7 @@ _ConvertOldFormatToNewFormat( break; default: stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyRecordValue); + retainString = false; break; } if (stringRef) { @@ -861,7 +873,7 @@ _CreateAttributesDictionaryFromKeyItem( CFMutableDictionaryRef dict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); unsigned int ix; - SecItemClass itemClass = 0; + SecItemClass itemClass = (SecItemClass) 0; UInt32 itemID; SecKeychainAttributeList *attrList = NULL; SecKeychainAttributeInfo *info = NULL; @@ -941,6 +953,7 @@ _CreateAttributesDictionaryFromKeyItem( break; default: stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%u"), (unsigned int)keyRecordValue); + retainString = false; break; } if (stringRef) { @@ -1179,7 +1192,7 @@ _CreateAttributesDictionaryFromInternetPasswordItem( // add kSecAttrAuthenticationType if ( attrList.attr[6].length > 0 ) { keys[numValues] = kSecAttrAuthenticationType; - values[numValues] = _SecAttrAuthenticationTypeForSecAuthenticationType(*(SecProtocolType*)attrList.attr[6].data); + values[numValues] = _SecAttrAuthenticationTypeForSecAuthenticationType( (SecAuthenticationType) (*(SecProtocolType*)attrList.attr[6].data)); if ( values[numValues] != NULL ) { CFRetain(values[numValues]); ++numValues; @@ -1997,7 +2010,7 @@ _CreateSecKeychainAttributeListFromDictionary( * _AppNameFromSecTrustedApplication attempts to pull the name of the * application/tool from the SecTrustedApplicationRef. */ -static CFStringRef +static CFStringRef CF_RETURNS_RETAINED _AppNameFromSecTrustedApplication( CFAllocatorRef alloc, SecTrustedApplicationRef appRef) @@ -2225,7 +2238,7 @@ _SafeSecKeychainItemDelete( } // create SecTrustedApplicationRef for current application/tool - CFReleaseSafe(currentAppRef); + CFReleaseNull(currentAppRef); status = SecTrustedApplicationCreateFromPath(NULL, ¤tAppRef); require_noerr(status, finish); require_quiet(currentAppRef != NULL, finish); @@ -2348,19 +2361,6 @@ _ReplaceKeychainItem( for (i=0, newCount=0; i < attrList->count; i++) { if (attrList->attr[i].length > 0) { newAttrList.attr[newCount++] = attrList->attr[i]; - #if 0 - // debugging code to log item attributes - SecKeychainAttrType tag = attrList->attr[i].tag; - SecKeychainAttrType htag=(SecKeychainAttrType)OSSwapConstInt32(tag); - char tmp[sizeof(SecKeychainAttrType) + 1]; - char tmpdata[attrList->attr[i].length + 1]; - memcpy(tmp, &htag, sizeof(SecKeychainAttrType)); - tmp[sizeof(SecKeychainAttrType)]=0; - memcpy(tmpdata, attrList->attr[i].data, attrList->attr[i].length); - tmpdata[attrList->attr[i].length]=0; - secitemlog(priority, "item attr '%s' = %d bytes: \"%s\"", - tmp, (int)attrList->attr[i].length, tmpdata); - #endif } } newAttrList.count = newCount; @@ -2412,7 +2412,7 @@ _UpdateKeychainItem(CFTypeRef item, CFDictionaryRef changedAttributes) return errSecParam; } - SecItemClass itemClass; + SecItemClass itemClass = (SecItemClass) 0; SecAccessRef access = NULL; SecKeychainAttributeList *changeAttrList = NULL; SecKeychainItemRef itemToUpdate = NULL; @@ -2484,6 +2484,13 @@ _UpdateKeychainItem(CFTypeRef item, CFDictionaryRef changedAttributes) status = _CreateSecKeychainKeyAttributeListFromDictionary(changedAttributes, &changeAttrList); require_noerr(status, update_failed); } + break; + case kSecAppleSharePasswordItemClass: + { + // do nothing (legacy behavior). + } + break; + } // get the password @@ -2494,7 +2501,7 @@ _UpdateKeychainItem(CFTypeRef item, CFDictionaryRef changedAttributes) } // update item status = SecKeychainItemModifyContent(itemToUpdate, - (changeAttrList->count == 0) ? NULL : changeAttrList, + (!changeAttrList || changeAttrList->count == 0) ? NULL : changeAttrList, (theData != NULL) ? (UInt32)CFDataGetLength(theData) : 0, (theData != NULL) ? CFDataGetBytePtrVoid(theData) : NULL); require_noerr(status, update_failed); @@ -2509,7 +2516,7 @@ update_failed: (itemClass == kSecInternetPasswordItemClass || itemClass == kSecGenericPasswordItemClass)) { // if we got a cryptographic failure updating a password item, it needs to be replaced status = _ReplaceKeychainItem(itemToUpdate, - (changeAttrList->count == 0) ? NULL : changeAttrList, + (!changeAttrList || changeAttrList->count == 0) ? NULL : changeAttrList, theData); } if (itemToUpdate) @@ -2731,7 +2738,7 @@ _ItemClassFromItemList(CFArrayRef itemList) // Given a list of items (standard or persistent references), // determine whether they all have the same item class. Returns // the item class, or 0 if multiple classes in list. - SecItemClass result = 0; + SecItemClass result = (SecItemClass) 0; CFIndex index, count = (itemList) ? CFArrayGetCount(itemList) : 0; for (index=0; index < count; index++) { CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(itemList, index); @@ -2746,7 +2753,7 @@ _ItemClassFromItemList(CFArrayRef itemList) itemRef = (SecKeychainItemRef) CFRetain(item); } if (itemRef) { - SecItemClass itemClass = 0; + SecItemClass itemClass = (SecItemClass) 0; CFTypeID itemTypeID = CFGetTypeID(itemRef); if (itemTypeID == SecIdentityGetTypeID() || itemTypeID == SecCertificateGetTypeID()) { // Identities and certificates have the same underlying item class @@ -2773,7 +2780,7 @@ _ItemClassFromItemList(CFArrayRef itemList) CFRelease(itemRef); if (itemClass != 0) { if (result != 0 && result != itemClass) { - return 0; // different item classes in list; bail out + return (SecItemClass) 0; // different item classes in list; bail out } result = itemClass; } @@ -2813,6 +2820,7 @@ struct SecItemParams { 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 matchIssuers; // value for kSecMatchIssuers (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 @@ -2924,6 +2932,7 @@ _FreeSecItemParams(SecItemParams *itemParams) if (itemParams->keyClass) CFRelease(itemParams->keyClass); if (itemParams->service) CFRelease(itemParams->service); if (itemParams->issuer) CFRelease(itemParams->issuer); + if (itemParams->matchIssuers) CFRelease(itemParams->matchIssuers); if (itemParams->serialNumber) CFRelease(itemParams->serialNumber); if (itemParams->search) CFRelease(itemParams->search); if (itemParams->access) CFRelease(itemParams->access); @@ -2942,12 +2951,12 @@ _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error) { OSStatus status; CFTypeRef value = NULL; - SecItemParams *itemParams = (SecItemParams *) malloc(sizeof(SecItemParams)); + CFDictionaryRef policyDict = NULL; + SecItemParams *itemParams = (SecItemParams *)calloc(1, sizeof(struct SecItemParams)); require_action(itemParams != NULL, error_exit, status = errSecAllocate); require_action(dict && (CFDictionaryGetTypeID() == CFGetTypeID(dict)), error_exit, status = errSecParam); - memset(itemParams, 0, sizeof(SecItemParams)); itemParams->query = (CFDictionaryRef) CFRetain(dict); // validate input search parameters @@ -2977,6 +2986,25 @@ _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error) // 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); + if (itemParams->itemData && CFGetTypeID(itemParams->itemData) == CFStringGetTypeID()) { + /* If we got a string, convert it into a data object */ + CFStringRef string = (CFStringRef)itemParams->itemData; + CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8) + 1; + CFMutableDataRef data = CFDataCreateMutable(NULL, maxLength); + require_action(data, error_exit, status = errSecAllocate); + + CFDataSetLength(data, maxLength); + + if (!CFStringGetCString(string, (char *)CFDataGetMutableBytePtr(data), maxLength, kCFStringEncodingUTF8)) { + CFRelease(data); + status = errSecAllocate; + goto error_exit; + } + + CFDataSetLength(data, strlen((const char *)CFDataGetBytePtr(data))); /* dont include NUL in string */ + itemParams->itemData = data; + CFRelease(string); + } // validate item references require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueRef, (const void **)&itemParams->itemRef, SecKeychainItemGetTypeID(), SecIdentityGetTypeID()), error_exit); @@ -3032,6 +3060,33 @@ _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error) require_action(!(itemParams->itemClass == 0 && !itemParams->useItems), error_exit, status = errSecItemClassMissing); } + // kSecMatchIssuers is only permitted with identities or certificates. + // Convert the input issuers to normalized form. + require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchIssuers, (const void **)&itemParams->matchIssuers, CFArrayGetTypeID(), NULL), error_exit); + if (itemParams->matchIssuers) { + CFTypeRef allowCerts = CFDictionaryGetValue(itemParams->query, kSecUseCertificatesWithMatchIssuers); + require_action(itemParams->returnIdentity || (allowCerts && CFEqual(allowCerts, kCFBooleanTrue)), error_exit, status = errSecParam); + CFMutableArrayRef canonical_issuers = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFArrayRef issuers = (CFArrayRef)itemParams->matchIssuers; + if (canonical_issuers) { + CFIndex i, count = CFArrayGetCount(issuers); + for (i = 0; i < count; i++) { + CFTypeRef issuer_data = CFArrayGetValueAtIndex(issuers, i); + CFDataRef issuer_canonical = NULL; + if (CFDataGetTypeID() == CFGetTypeID(issuer_data)) + issuer_canonical = SecDistinguishedNameCopyNormalizedSequence((CFDataRef)issuer_data); + if (issuer_canonical) { + CFArrayAppendValue(canonical_issuers, issuer_canonical); + CFRelease(issuer_canonical); + } + } + if (CFArrayGetCount(canonical_issuers) > 0) { + CFAssignRetained(itemParams->matchIssuers, canonical_issuers); + } else + CFRelease(canonical_issuers); + } + } + itemParams->keyUsage = _CssmKeyUsageFromQuery(dict); itemParams->trustedOnly = CFDictionaryGetValueIfPresent(dict, kSecMatchTrustedOnly, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value); itemParams->issuerAndSNToMatch = (itemParams->issuer != NULL && itemParams->serialNumber != NULL); @@ -3080,12 +3135,18 @@ _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error) require_noerr(status, error_exit); } - // if we already have an item list (to add or find items in), we don't need an item class, attribute list or a search reference + // if we already have an item list (to add or find items in), we don't need a search reference if (itemParams->useItems) { if (itemParams->itemClass == 0) { itemParams->itemClass = _ItemClassFromItemList(itemParams->useItems); } - status = errSecSuccess; + + // build a SecKeychainAttributeList from the query dictionary for the specified item class + if (itemParams->itemClass != 0) { + status = _CreateSecKeychainAttributeListFromDictionary(dict, itemParams->itemClass, &itemParams->attrList); + } else { + status = errSecSuccess; + } goto error_exit; // all done here } @@ -3094,12 +3155,12 @@ _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error) // if policy is a SMIME policy, copy email address in policy into emailAddrToMatch parameter if(itemParams->policy) { - CFDictionaryRef policyDict = SecPolicyCopyProperties(itemParams->policy); + policyDict = SecPolicyCopyProperties(itemParams->policy); CFStringRef oidStr = (CFStringRef) CFDictionaryGetValue(policyDict, kSecPolicyOid); if(oidStr && CFStringCompare(kSecPolicyAppleSMIME,oidStr,0) == 0) { require_noerr(status = _ValidateDictionaryEntry(policyDict, kSecPolicyName, (const void **)&itemParams->emailAddrToMatch, CFStringGetTypeID(), NULL), error_exit); } - CFRelease(policyDict); + CFReleaseNull(policyDict); } // create a search reference (either a SecKeychainSearchRef or a SecIdentitySearchRef) @@ -3148,6 +3209,7 @@ _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error) } error_exit: + CFReleaseNull(policyDict); if (status) { _FreeSecItemParams(itemParams); itemParams = NULL; @@ -3200,31 +3262,6 @@ _ImportKey( END_SECAPI } -#if !SECTRUST_OSX -static 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; -} -#endif - static OSStatus _FilterWithPolicy(SecPolicyRef policy, CFDateRef date, SecCertificateRef cert) { @@ -3236,7 +3273,6 @@ _FilterWithPolicy(SecPolicyRef policy, CFDateRef date, SecCertificateRef cert) SecTrustRef trust = NULL; SecTrustResultType trustResult; - CSSM_TP_APPLE_EVIDENCE_INFO *evidence = NULL; Boolean needChain = false; OSStatus status; if (!policy || !cert) return errSecParam; @@ -3255,39 +3291,18 @@ _FilterWithPolicy(SecPolicyRef policy, CFDateRef date, SecCertificateRef cert) props = SecPolicyCopyProperties(policy); if (props) { CFTypeRef oid = (CFTypeRef) CFDictionaryGetValue(props, kSecPolicyOid); - if (oid && CFEqual(oid, kSecPolicyAppleX509Basic)) { + if (oid && (CFEqual(oid, kSecPolicyAppleX509Basic) || + CFEqual(oid, kSecPolicyAppleRevocation))) { 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; + status = SecTrustEvaluateLeafOnly(trust, &trustResult); + } else { + status = SecTrustEvaluate(trust, &trustResult); } - /* 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)) { @@ -3299,18 +3314,8 @@ _FilterWithPolicy(SecPolicyRef policy, CFDateRef date, SecCertificateRef cert) /* If there are no per-cert policy status codes, * and the cert has not expired, consider it valid for the policy. */ -#if SECTRUST_OSX if (true) { (void)SecTrustGetCssmResultCode(trust, &status); -#else - if((evidence != NULL) && _CanIgnoreLeafStatusCodes(evidence) && - ((evidence[0].StatusBits & CSSM_CERT_STATUS_EXPIRED) == 0) && - ((evidence[0].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET) == 0)) { - status = errSecSuccess; -#endif - } - else { - status = errSecCertificateCannotOperate; } cleanup: @@ -3381,11 +3386,82 @@ _FilterWithTrust(Boolean trustedOnly, SecCertificateRef cert) return status; } +static bool items_matching_issuer_parent(CFDataRef issuer, CFArrayRef issuers, int recurse) { + if (!issuers || CFArrayGetCount(issuers) == 0) { return false; } + + /* We found a match, we're done. */ + if (CFArrayContainsValue(issuers, CFRangeMake(0, CFArrayGetCount(issuers)), issuer)) { return true; } + + /* Prevent infinite recursion */ + if (recurse <= 0) { return false; } + recurse--; + + /* Query for parents */ + CFMutableDictionaryRef query = NULL; + CFTypeRef parents = NULL; + bool found = false; + + require_quiet(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks), out); + CFDictionaryAddValue(query, kSecClass, kSecClassCertificate); + CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue); + CFDictionaryAddValue(query, kSecAttrSubject, issuer); + CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll); + require_noerr_quiet(SecItemCopyMatching(query, &parents), out); + + if (parents && CFArrayGetTypeID() == CFGetTypeID(parents)) { + CFIndex i, count = CFArrayGetCount((CFArrayRef)parents); + for (i = 0; i < count; i++) { + SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex((CFArrayRef)parents, i); + CFDataRef cert_issuer = SecCertificateCopyNormalizedIssuerSequence(cert); + if (CFEqual(cert_issuer, issuer)) { + // Self-issued cert, don't look for parents. + CFReleaseNull(cert_issuer); + continue; + } + found = items_matching_issuer_parent(cert_issuer, issuers, recurse); + CFReleaseNull(cert_issuer); + if (found) { break; } + } + } else if (parents && SecCertificateGetTypeID() == CFGetTypeID(parents)) { + SecCertificateRef cert = (SecCertificateRef)parents; + CFDataRef cert_issuer = SecCertificateCopyNormalizedIssuerSequence(cert); + require_action_quiet(!CFEqual(cert_issuer, issuer), out, CFReleaseNull(cert_issuer)); + found = items_matching_issuer_parent(cert_issuer, issuers, recurse); + CFReleaseNull(cert_issuer); + } + +out: + CFReleaseNull(query); + CFReleaseNull(parents); + return found; +} + +static OSStatus +_FilterWithIssuers(CFArrayRef issuers, SecCertificateRef cert) +{ + if (!issuers || CFArrayGetCount(issuers) == 0) return errSecParam; + if (!cert) return errSecParam; + + OSStatus status = errSecInternalError; + + /* kSecMatchIssuers matches certificates where ANY certificate in the chain has this issuer. + * So we now need to recursively query the keychain for this cert's parents to determine if + * they match. (This is why we limited the use of this key in _CreateSecItemParamsFromDictionary.) */ + CFDataRef issuer = SecCertificateCopyNormalizedIssuerSequence(cert); + if (items_matching_issuer_parent(issuer, issuers, 10)) { + status = errSecSuccess; + } + + CFReleaseNull(issuer); + return status; +} + static SecKeychainItemRef CopyResolvedKeychainItem(CFTypeRef item) { SecKeychainItemRef kcItem = NULL; - OSStatus status; + OSStatus status = errSecSuccess; if (item) { if (CFGetTypeID(item) == CFDataGetTypeID()) { // persistent reference, resolve first @@ -3399,9 +3475,13 @@ CopyResolvedKeychainItem(CFTypeRef item) // ask for the item's class: // will return an error if the item has been deleted SecItemClass itemClass; - SecKeychainItemRef certRef = NULL; - if (CFGetTypeID(kcItem) == SecIdentityGetTypeID()) { - status = SecIdentityCopyCertificate((SecIdentityRef)kcItem, (SecCertificateRef *)&certRef); + SecCertificateRef certRef = NULL; + CFTypeID itemTypeID = CFGetTypeID(kcItem); + if (itemTypeID == SecIdentityGetTypeID()) { + status = SecIdentityCopyCertificate((SecIdentityRef)kcItem, &certRef); + } + else if (itemTypeID == SecCertificateGetTypeID()) { + certRef = (SecCertificateRef) CFRetain(kcItem); } if (certRef) { // can't call SecKeychainItemCopyAttributesAndData on a SecCertificateRef @@ -3485,16 +3565,17 @@ SecItemSearchCopyNext(SecItemParams *params, CFTypeRef *item) OSStatus status; CFTypeRef search = (params) ? params->search : NULL; CFTypeID typeID = (search) ? CFGetTypeID(search) : 0; - if (typeID == SecIdentitySearchGetTypeID()) { + + if (search && typeID == SecIdentitySearchGetTypeID()) { status = SecIdentitySearchCopyNext((SecIdentitySearchRef)search, (SecIdentityRef*)item); } - else if (typeID == SecKeychainSearchGetTypeID()) { + else if (search && 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 if (typeID == 0 && (params->useItems || params->itemList)) { + else if (typeID == 0 && params && (params->useItems || params->itemList)) { // No search available, but there is an item list available. // Return the next candidate item from the caller's item list CFArrayRef itemList = (params->useItems) ? params->useItems : params->itemList; @@ -3619,6 +3700,11 @@ FilterCandidateItem(CFTypeRef *item, SecItemParams *itemParams, SecIdentityRef * } // certificate item is trusted on this system } + if (itemParams->matchIssuers) { + status = _FilterWithIssuers((CFArrayRef)itemParams->matchIssuers, (SecCertificateRef) *item); + if (status) goto filterOut; + // certificate item has one of the issuers + } } if (itemParams->itemList) { Boolean foundMatch = FALSE; @@ -3898,22 +3984,6 @@ static SInt32 readNumber(CFTypeRef obj) { return NULL; } -// -// Function to ensure the syncable keychain is unlocked. -// Currently, this means unlocking the login keychain, -// which will also unlock the keybag as a side effect. -// -static OSStatus SecItemUnlockSynchronizableKeychain() -{ - SecKeychainRef keychain = NULL; - OSStatus status = SecKeychainCopyLogin(&keychain); - if (!status) { - status = SecKeychainUnlock(keychain, 0, NULL, false); - } - CFReleaseSafe(keychain); - return status; -} - // // Function to check whether the kSecAttrSynchronizable flag is set in the query. // @@ -3926,28 +3996,115 @@ static Boolean SecItemSynchronizable(CFDictionaryRef query) } // -// Function to check whether the kSecAttrNoLegacy flag is set in the query. +// Function to check whether a synchronizable persistent reference was provided. // -static Boolean SecItemNoLegacy(CFDictionaryRef query) +static Boolean SecItemIsIOSPersistentReference(CFTypeRef value) { - CFTypeRef value = CFDictionaryGetValue(query, kSecAttrNoLegacy); - Boolean result = (value && readNumber(value)); - - return result; + if (value) { + return ::_SecItemParsePersistentRef((CFDataRef)value, NULL, NULL, NULL); + } + return false; } +extern "C" Boolean SecKeyIsCDSAKey(SecKeyRef ref); + // -// Function to check whether the kSecAttrSynchronizable flag is set in the query, -// and has the special value of kSecAttrSynchronizableAny. +// Function to find out which keychains are targetted by the query. // -static Boolean SecItemSynchronizableAny(CFDictionaryRef query) +static OSStatus SecItemCategorizeQuery(CFDictionaryRef query, bool &can_target_ios, bool &can_target_osx) { - CFTypeRef value = CFDictionaryGetValue(query, kSecAttrSynchronizable); - if (value) { - return (CFGetTypeID(value) == CFStringGetTypeID() && - CFEqual(value, kSecAttrSynchronizableAny)); + // By default, target both keychain. + can_target_osx = can_target_ios = true; + + // Check no-legacy flag. + // it's iOS or bust if we're on MZ! + CFTypeRef noLegacy = NULL; + if (_CFMZEnabled()) { + noLegacy = kCFBooleanTrue; + } + else { + noLegacy = CFDictionaryGetValue(query, kSecAttrNoLegacy); + } + + if (noLegacy != NULL) { + can_target_ios = readNumber(noLegacy) != 0; + can_target_osx = !can_target_ios; + return errSecSuccess; } - return false; + + // Check whether the query contains kSecValueRef and modify can_ flags according to the kind and type of the value. + CFTypeRef value = CFDictionaryGetValue(query, kSecValueRef); + if (value != NULL) { + CFTypeID typeID = CFGetTypeID(value); + if (typeID == SecKeyGetTypeID()) { + can_target_osx = SecKeyIsCDSAKey((SecKeyRef)value); + can_target_ios = !can_target_osx; + } else if (typeID == SecCertificateGetTypeID()) { + // All types of certificates can target OSX keychains, but OSX certificates won't work on iOS + can_target_ios &= !SecCertificateIsItemImplInstance((SecCertificateRef)value); + } else if (typeID == SecKeychainItemGetTypeID()) { + // SecKeychainItemRef can target iOS keychain only when it has attached iOS-style persistent reference. + if (_SecItemGetPersistentReference(value) == NULL) { + can_target_ios = false; + } + } + } + + // Check presence of kSecAttrTokenID and kSecAttrAccessControl; they are not defined for CDSA keychain. + if (CFDictionaryContainsKey(query, kSecAttrTokenID) || CFDictionaryContainsKey(query, kSecAttrAccessControl)) { + can_target_osx = false; + } + + // Check for special token access groups. If present, redirect query to iOS keychain. + value = CFDictionaryGetValue(query, kSecAttrAccessGroup); + if (value != NULL && CFEqual(value, kSecAttrAccessGroupToken)) { + can_target_osx = false; + } + + // Synchronizable items should go to iOS keychain only. + if (SecItemSynchronizable(query)) { + can_target_osx = false; + } + + value = CFDictionaryGetValue(query, kSecValuePersistentRef); + if (value != NULL) { + if (SecItemIsIOSPersistentReference(value)) { + can_target_osx = false; + } else { + // Non-iOS-style persistent references should not be fed to iOS keychain queries. + can_target_ios = false; + } + } + + // Presence of following atributes means that query is OSX-only. + static const CFStringRef *osx_only_items[] = { + &kSecMatchItemList, + &kSecMatchSearchList, + &kSecMatchSubjectStartsWith, + &kSecMatchSubjectEndsWith, + &kSecMatchSubjectWholeString, + &kSecMatchDiacriticInsensitive, + &kSecMatchWidthInsensitive, + &kSecUseItemList, + &kSecUseKeychain, + &kSecAttrAccess, + &kSecAttrPRF, + &kSecAttrSalt, + &kSecAttrRounds, + }; + + for (CFIndex i = 0; i < array_size(osx_only_items); i++) { + can_target_ios = can_target_ios && !CFDictionaryContainsKey(query, *osx_only_items[i]); + } + + // Absence of all of kSecItemClass, kSecValuePersistentRef, and kSecValueRef means that the query can't target iOS + if(CFDictionaryGetValue(query, kSecClass) == NULL && + CFDictionaryGetValue(query, kSecValuePersistentRef) == NULL && + CFDictionaryGetValue(query, kSecValueRef) == NULL) { + can_target_ios = false; + } + + return (can_target_ios || can_target_osx) ? errSecSuccess : errSecParam; } // @@ -3965,42 +4122,6 @@ static Boolean SecItemHasSynchronizableUpdate(Boolean synchronizable, CFDictiona return (old_sync != new_sync); } -// -// Returns true if keychain syncing is globally enabled. -// -static Boolean SecItemSyncEnabled() -{ - static dispatch_once_t onceToken; - static Boolean syncEnabled = true; - - //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()) { - syncEnabled = CFBooleanGetValue((CFBooleanRef)sync); - CFRelease(sync); - } - }); - - return syncEnabled; -} - -// -// Function to check whether a synchronizable persistent reference was provided. -// -static Boolean SecItemHasSynchronizablePersistentReference(CFDictionaryRef query) -{ - CFTypeRef value = CFDictionaryGetValue(query, kSecValuePersistentRef); - if (value) { - /* Synchronizable persistent ref consists of the sqlite rowid and 4-byte class value */ - const CFIndex kSynchronizablePersistentRefLength = sizeof(int64_t) + 4; - return (CFGetTypeID(value) == CFDataGetTypeID() && - CFDataGetLength((CFDataRef)value) == kSynchronizablePersistentRefLength); - } - return false; -} - // // Function to apply changes to a mutable dictionary. // (CFDictionaryApplierFunction, called by CFDictionaryApplyFunction) @@ -4121,22 +4242,18 @@ static OSStatus SecItemChangeSynchronizability(CFDictionaryRef query, CFDictiona extern "C" { CFTypeRef -SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes) { +SecItemCreateFromAttributeDictionary_osx(CFDictionaryRef refAttributes) { CFTypeRef ref = NULL; - CFStringRef key_class_string = (CFStringRef)CFDictionaryGetValue(refAttributes, kSecClass); - SecItemClass key_class; - bool key_class_found = false; + CFStringRef item_class_string = (CFStringRef)CFDictionaryGetValue(refAttributes, kSecClass); + SecItemClass item_class = (SecItemClass) 0; - if (CFEqual(key_class_string, kSecClassGenericPassword)) { - key_class = kSecGenericPasswordItemClass; - key_class_found = true; - } - if (CFEqual(key_class_string, kSecClassInternetPassword)) { - key_class = kSecInternetPasswordItemClass; - key_class_found = true; + if (CFEqual(item_class_string, kSecClassGenericPassword)) { + item_class = kSecGenericPasswordItemClass; + } else if (CFEqual(item_class_string, kSecClassInternetPassword)) { + item_class = kSecInternetPasswordItemClass; } - if (key_class_found) { + if (item_class != 0) { // we carry v_Data around here so the *_ios calls can find it and locate // their own data. Putting things in the attribute list doesn't help as // the osx keychainitem and item calls bail when they don't see a keychain @@ -4144,71 +4261,21 @@ SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes) { // find a way to craft a workable keychain object. #if'ed code left below // in case we need to go down that path. - struct SecKeychainAttributeList *attrs = (struct SecKeychainAttributeList *)malloc(sizeof(struct SecKeychainAttributeList) + sizeof(struct SecKeychainAttribute) * 0); - attrs->attr = (struct SecKeychainAttribute *)(attrs + 1); - attrs->count = 0; - CFTypeRef v; -#if 0 - // The C++ string objects need to last at least as long as the attr struct. - string account; - - v = CFDictionaryGetValue(refAttributes, CFSTR("mdat")); - if (v) { - attrs->attr[attrs->count].tag = kSecModDateItemAttr; - // XXX need to convert to YYYYMMDDhhmmSSZ - attrs->attr[attrs->count].data = (void*)"19690223140232Z"; - attrs->attr[attrs->count].length = strlen((char*)(attrs->attr[attrs->count].data)); - attrs->count++; - } - v = CFDictionaryGetValue(refAttributes, CFSTR("cdat")); - if (v) { - attrs->attr[attrs->count].tag = kSecCreationDateItemAttr; - // XXX need to convert to YYYYMMDDhhmmSSZ - attrs->attr[attrs->count].data = (void*)"19690223140232Z"; - attrs->attr[attrs->count].length = strlen((char*)(attrs->attr[attrs->count].data)); - attrs->count++; - } - - v = CFDictionaryGetValue(refAttributes, CFSTR("acct")); - if (v) { - attrs->attr[attrs->count].tag = kSecAccountItemAttr; - account = cfString((CFStringRef)v); - attrs->attr[attrs->count].data = (void*)(account.c_str()); - attrs->attr[attrs->count].length = account.length(); - attrs->count++; - } - - // class isn't treated as an attribute by the creation API + SecKeychainAttributeList attrs = {}; + SecKeychainAttribute attr = {}; - v = CFDictionaryGetValue(refAttributes, CFSTR("svce")); - if (v) { - attrs->attr[attrs->count].tag = kSecServiceItemAttr; - account = cfString((CFStringRef)v); - attrs->attr[attrs->count].data = (void*)(account.c_str()); - attrs->attr[attrs->count].length = account.length(); - attrs->count++; - } + attrs.attr = &attr; + attrs.count = 0; + CFTypeRef v; - v = CFDictionaryGetValue(refAttributes, CFSTR("acct")); - if (v) { - attrs->attr[attrs->count].tag = kSecLabelItemAttr; - account = cfString((CFStringRef)v); - attrs->attr[attrs->count].data = (void*)(account.c_str()); - attrs->attr[attrs->count].length = account.length(); - attrs->count++; - } -#endif - Item item = Item(key_class, attrs, 0, ""); - ItemImpl *real_item = item.get(); + Item item = Item(item_class, &attrs, 0, ""); v = CFDictionaryGetValue(refAttributes, kSecValuePersistentRef); if (v) { - real_item->setPersistentRef((CFDataRef)v); + item->setPersistentRef((CFDataRef)v); } - ref = real_item->handle(); - } else { - // keys, certs, identities are not currently sync'able. - ref = NULL; + ref = item->handle(); } + return ref; } @@ -4252,6 +4319,213 @@ SecItemValidateAppleApplicationGroupAccess(CFStringRef group) return status; } +static Mutex& gParentCertCacheLock() { + static Mutex fParentCertCacheLock; + return fParentCertCacheLock; +} +static CFMutableDictionaryRef gParentCertCache; +static CFMutableArrayRef gParentCertCacheList; +#define PARENT_CACHE_SIZE 100 + +void SecItemParentCachePurge() { + StLock _(gParentCertCacheLock()); + CFReleaseNull(gParentCertCache); + CFReleaseNull(gParentCertCacheList); +} + +static CFArrayRef CF_RETURNS_RETAINED parentCacheRead(SecCertificateRef certificate) { + CFArrayRef parents = NULL; + CFIndex ix; + CFDataRef digest = SecCertificateGetSHA1Digest(certificate); + if (!digest) return NULL; + + StLock _(gParentCertCacheLock()); + if (gParentCertCache && gParentCertCacheList) { + if (0 <= (ix = CFArrayGetFirstIndexOfValue(gParentCertCacheList, + CFRangeMake(0, CFArrayGetCount(gParentCertCacheList)), + digest))) { + // Cache hit. Get value and move entry to the top of the list. + parents = (CFArrayRef)CFDictionaryGetValue(gParentCertCache, digest); + CFArrayRemoveValueAtIndex(gParentCertCacheList, ix); + CFArrayAppendValue(gParentCertCacheList, digest); + } + } + CFRetainSafe(parents); + return parents; +} + +static void parentCacheWrite(SecCertificateRef certificate, CFArrayRef parents) { + CFDataRef digest = SecCertificateGetSHA1Digest(certificate); + if (!digest) return; + + StLock _(gParentCertCacheLock()); + if (!gParentCertCache || !gParentCertCacheList) { + CFReleaseNull(gParentCertCache); + gParentCertCache = makeCFMutableDictionary(); + CFReleaseNull(gParentCertCacheList); + gParentCertCacheList = makeCFMutableArray(0); + } + + if (gParentCertCache && gParentCertCacheList) { + // check to make sure another thread didn't add this entry to the cache already + if (0 > CFArrayGetFirstIndexOfValue(gParentCertCacheList, + CFRangeMake(0, CFArrayGetCount(gParentCertCacheList)), + digest)) { + CFDictionaryAddValue(gParentCertCache, digest, parents); + if (PARENT_CACHE_SIZE <= CFArrayGetCount(gParentCertCacheList)) { + // Remove least recently used cache entry. + CFDictionaryRemoveValue(gParentCertCache, CFArrayGetValueAtIndex(gParentCertCacheList, 0)); + CFArrayRemoveValueAtIndex(gParentCertCacheList, 0); + } + CFArrayAppendValue(gParentCertCacheList, digest); + } + } +} + +/* + * SecItemCopyParentCertificates_osx returns an array of zero of more possible + * issuer certificates for the provided certificate. No cryptographic validation + * of the signature is performed in this function; its purpose is only to + * provide a list of candidate certificates. + */ +CFArrayRef +SecItemCopyParentCertificates_osx(SecCertificateRef certificate, void *context) +{ +#pragma unused (context) /* for now; in future this can reference a container object */ + /* Check for parents in keychain cache */ + CFArrayRef parents = parentCacheRead(certificate); + if (parents) { + return parents; + } + + /* Cache miss. Query for parents. */ +#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) + CFDataRef normalizedIssuer = SecCertificateCopyNormalizedIssuerContent(certificate, NULL); +#else + CFDataRef normalizedIssuer = SecCertificateGetNormalizedIssuerContent(certificate); + CFRetainSafe(normalizedIssuer); +#endif + OSStatus status; + CFMutableArrayRef combinedSearchList = NULL; + + /* Define the array of keychains which will be searched for parents. */ + CFArrayRef searchList = NULL; + status = SecKeychainCopySearchList(&searchList); + if (searchList) { + combinedSearchList = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, searchList); + CFRelease(searchList); + } else { + combinedSearchList = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + } + SecKeychainRef rootStoreKeychain = NULL; + status = SecKeychainOpen(SYSTEM_ROOT_STORE_PATH, &rootStoreKeychain); + if (rootStoreKeychain) { + if (combinedSearchList) { + CFArrayAppendValue(combinedSearchList, rootStoreKeychain); + } + CFRelease(rootStoreKeychain); + } + + /* Create and populate a fixed-size query dictionary. */ + CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 5, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(query, kSecClass, kSecClassCertificate); + CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue); + CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll); + if (combinedSearchList) { + CFDictionaryAddValue(query, kSecMatchSearchList, combinedSearchList); + CFRelease(combinedSearchList); + } + + CFTypeRef results = NULL; + if (normalizedIssuer) { + /* Look up certs whose subject is the same as this cert's issuer. */ + CFDictionaryAddValue(query, kSecAttrSubject, normalizedIssuer); + status = SecItemCopyMatching_osx(query, &results); + } + else { + /* Cannot match anything without an issuer! */ + status = errSecItemNotFound; + } + + if ((status != errSecSuccess) && (status != errSecItemNotFound)) { + secitemlog(LOG_WARNING, "SecItemCopyParentCertificates_osx: %d", (int)status); + } + CFRelease(query); + + CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFTypeID resultType = (results) ? CFGetTypeID(results) : 0; + if (resultType == CFArrayGetTypeID()) { + CFIndex index, count = CFArrayGetCount((CFArrayRef)results); + for (index = 0; index < count; index++) { + CFDataRef data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef)results, index); + if (data) { + SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, data); + if (cert) { + CFArrayAppendValue(result, cert); + CFRelease(cert); + } + } + } + } else if (results && resultType == CFDataGetTypeID()) { + SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)results); + if (cert) { + CFArrayAppendValue(result, cert); + CFRelease(cert); + } + } + CFReleaseSafe(results); + CFReleaseSafe(normalizedIssuer); + + /* Add to cache. */ + parentCacheWrite(certificate, result); + + return result; +} + +SecCertificateRef SecItemCopyStoredCertificate(SecCertificateRef certificate, void *context) +{ +#pragma unused (context) /* for now; in future this can reference a container object */ + + /* Certificates are unique by issuer and serial number. */ + CFDataRef serialNumber = SecCertificateCopySerialNumberData(certificate, NULL); +#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) + CFDataRef normalizedIssuer = SecCertificateCopyNormalizedIssuerContent(certificate, NULL); +#else + CFDataRef normalizedIssuer = SecCertificateGetNormalizedIssuerContent(certificate); + CFRetainSafe(normalizedIssuer); +#endif + + const void *keys[] = { + kSecClass, + kSecMatchLimit, + kSecAttrIssuer, + kSecAttrSerialNumber, + kSecReturnRef + }, + *values[] = { + kSecClassCertificate, + kSecMatchLimitOne, + normalizedIssuer, + serialNumber, + kCFBooleanTrue + }; + CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 5, NULL, NULL); + CFTypeRef result = NULL; + + OSStatus status = SecItemCopyMatching_osx(query, &result); + if ((status != errSecSuccess) && (status != errSecItemNotFound)) { + secitemlog(LOG_WARNING, "SecItemCopyStoredCertificate: %d", (int)status); + CFReleaseNull(result); + } + CFReleaseSafe(query); + CFReleaseSafe(serialNumber); + CFReleaseSafe(normalizedIssuer); + + return (SecCertificateRef)result; +} + /* * SecItemCopyTranslatedAttributes accepts a user-provided attribute dictionary * and attempts to return a sanitized copy for passing to the underlying @@ -4338,11 +4612,6 @@ SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass, */ CFDictionaryRemoveValue(result, kSecUseKeychain); - /* Remove kSecMatchPolicy (value is a SecPolicyRef); - * TODO: need a way to externalize and restore a policy instance - */ - CFDictionaryRemoveValue(result, kSecMatchPolicy); - /* Potentially translate kSecAttrAccess (value is a SecAccessRef), * unless kSecAttrAccessGroup has already been specified. */ @@ -4363,15 +4632,20 @@ SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass, CFDictionaryRemoveValue(result, kSecAttrAccess); /* If item is specified by direct reference, and this is an iOS search, - * replace it with a persistent reference. + * replace it with a persistent reference, if it was recorded inside ItemImpl. */ - CFTypeRef directRef = CFDictionaryGetValue(result, kSecValueRef); - if (directRef) { - CFDataRef persistentRef = _SecItemGetPersistentReference(directRef); - if (persistentRef) { - CFDictionarySetValue(result, kSecValuePersistentRef, persistentRef); + CFTypeRef directRef = CFDictionaryGetValue(result, kSecValueRef); + if (directRef != NULL) { + CFTypeID typeID = CFGetTypeID(directRef); + if ((typeID != SecKeyGetTypeID() || SecKeyIsCDSAKey((SecKeyRef)directRef)) && + (typeID != SecCertificateGetTypeID() || SecCertificateIsItemImplInstance((SecCertificateRef)directRef)) && + (typeID != SecIdentityGetTypeID())) { + CFDataRef persistentRef = _SecItemGetPersistentReference(directRef); + if (persistentRef) { + CFDictionarySetValue(result, kSecValuePersistentRef, persistentRef); + CFDictionaryRemoveValue(result, kSecValueRef); + } } - CFDictionaryRemoveValue(result, kSecValueRef); } /* If item is specified by persistent reference, and this is an iOS search, @@ -4390,6 +4664,18 @@ SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass, * which won't work once the item is updated. */ CFDictionaryRemoveValue(result, kSecAttrModificationDate); + + /* Find all intermediate certificates in OSX keychain and append them in to the kSecMatchIssuers. + * This is required because secd cannot do query in to the OSX keychain + */ + CFTypeRef matchIssuers = CFDictionaryGetValue(result, kSecMatchIssuers); + if (matchIssuers && CFGetTypeID(matchIssuers) == CFArrayGetTypeID()) { + CFArrayRef newMatchIssuers = _CopyMatchingIssuers((CFArrayRef)matchIssuers); + if (newMatchIssuers) { + CFDictionarySetValue(result, kSecMatchIssuers, newMatchIssuers); + CFRelease(newMatchIssuers); + } + } } else { /* iOS doesn't add the class attribute, so we must do it here. */ @@ -4398,6 +4684,7 @@ SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass, /* Remove attributes which are not part of the OS X database schema. */ CFDictionaryRemoveValue(result, kSecAttrAccessible); + CFDictionaryRemoveValue(result, kSecAttrAccessControl); CFDictionaryRemoveValue(result, kSecAttrAccessGroup); CFDictionaryRemoveValue(result, kSecAttrSynchronizable); CFDictionaryRemoveValue(result, kSecAttrTombstone); @@ -4409,40 +4696,123 @@ SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass, return result; } -/* - * SecItemCopyMergedResults takes two input objects, which may be containers, - * and returns a retained object which merges the results. Merging depends on the - * result type. If each result is valid and is not an array, then only one match was - * requested; in that case, the syncable (ios) match is preferred. - * - * FIXME: There are some edge cases still to deal with; e.g. if the OSX search specified a - * particular keychain to search, we do not want to merge in any IOS results. Also, may need - * to filter out duplicates if two items differ only in the sync attribute. - */ -CFTypeRef -SecItemCopyMergedResults(CFDictionaryRef query, CFTypeRef result_osx, CFTypeRef result_ios) -{ - CFTypeID id_osx = (result_osx) ? CFGetTypeID(result_osx) : 0; - CFTypeID id_ios = (result_ios) ? CFGetTypeID(result_ios) : 0; - CFTypeID id_array = CFArrayGetTypeID(); - if ((id_osx == id_array) && (id_ios == id_array)) { - // Fold the arrays into one. - CFMutableArrayRef results = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - CFArrayAppendArray(results, (CFArrayRef)result_ios, CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_ios))); - CFArrayAppendArray(results, (CFArrayRef)result_osx, CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_osx))); - return results; - } - // Result type is not an array, so only one match can be returned. - return (id_ios) ? CFRetain(result_ios) : CFRetain(result_osx); +} /* extern "C" */ + +static CFArrayRef +_CopyMatchingIssuers(CFArrayRef matchIssuers) { + CFMutableArrayRef result = NULL; + CFMutableDictionaryRef query = NULL; + CFMutableDictionaryRef policyProperties = NULL; + SecPolicyRef policy = NULL; + CFTypeRef matchedCertificates = NULL; + + require_quiet(policyProperties = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks), out); + CFDictionarySetValue(policyProperties, kSecPolicyKU_KeyCertSign, kCFBooleanTrue); + require_quiet(policy = SecPolicyCreateWithProperties(kSecPolicyAppleX509Basic, policyProperties), out); + + require_quiet(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks), out); + CFDictionarySetValue(query, kSecClass, kSecClassCertificate); + CFDictionarySetValue(query, kSecMatchIssuers, matchIssuers); + CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll); + CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue); + CFDictionarySetValue(query, kSecUseCertificatesWithMatchIssuers, kCFBooleanTrue); + CFDictionarySetValue(query, kSecMatchPolicy, policy); + + if (SecItemCopyMatching_osx(query, &matchedCertificates) == errSecSuccess && CFGetTypeID(matchedCertificates) == CFArrayGetTypeID()) { + require_quiet(result = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, (CFArrayRef)matchedCertificates), out); + for(CFIndex i = 0; i < CFArrayGetCount((CFArrayRef)matchedCertificates); ++i) { + CFDictionaryRef attributes = (CFDictionaryRef)CFArrayGetValueAtIndex((CFArrayRef)matchedCertificates, i); + CFTypeRef subject = CFDictionaryGetValue(attributes, kSecAttrSubject); + if (!CFArrayContainsValue(result, CFRangeMake(0, CFArrayGetCount(result)), subject)) { + CFArrayAppendValue(result, subject); + } + } + } + +out: + CFReleaseSafe(query); + CFReleaseSafe(policyProperties); + CFReleaseSafe(policy); + CFReleaseSafe(matchedCertificates); + + return result; } -} /* extern "C" */ +static OSStatus +SecItemMergeResults(bool can_target_ios, OSStatus status_ios, CFTypeRef result_ios, + bool can_target_osx, OSStatus status_osx, CFTypeRef result_osx, + CFTypeRef *result) { + // When querying both keychains and iOS keychain fails because of missing + // entitlements, completely ignore iOS keychain result. This is to keep + // backward compatibility with applications which know nothing about iOS keychain + // and use SecItem API to access OSX keychain which does not need any entitlements. + if (can_target_osx && can_target_ios && status_ios == errSecMissingEntitlement) { + can_target_ios = false; + } + + if (can_target_osx && can_target_ios) { + // If both keychains were targetted, examine returning statuses and decide what to do. + if (status_ios != errSecSuccess) { + // iOS keychain failed to produce results because of some error, go with results from OSX keychain. + // Since iOS keychain queries will fail without a keychain-access-group or proper entitlements, SecItemCopyMatching + // calls against the OSX keychain API that should return errSecItemNotFound will return nonsense from the iOS keychain. + AssignOrReleaseResult(result_osx, result); + return status_osx; + } else if (status_osx != errSecSuccess) { + if (status_osx != errSecItemNotFound) { + // OSX failed to produce results with some failure mode (else than not_found), but iOS produced results. + // We have to either return OSX failure result and discard iOS results, or vice versa. For now, we just + // ignore OSX error and return just iOS results. + secitemlog(LOG_NOTICE, "SecItemMergeResults: osx_result=%d, ignoring it, iOS succeeded fine", status_osx); + } + // OSX failed to produce results, but we have success from iOS keychain; go with results from iOS keychain. + AssignOrReleaseResult(result_ios, result); + return errSecSuccess; + } else { + // Both searches succeeded, merge results. + if (result != NULL) { + CFTypeID id_osx = (result_osx) ? CFGetTypeID(result_osx) : 0; + CFTypeID id_ios = (result_ios) ? CFGetTypeID(result_ios) : 0; + CFTypeID id_array = CFArrayGetTypeID(); + if ((id_osx == id_array) && (id_ios == id_array)) { + // Fold the arrays into one. + *result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFArrayAppendArray((CFMutableArrayRef)*result, (CFArrayRef)result_ios, + CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_ios))); + CFArrayAppendArray((CFMutableArrayRef)*result, (CFArrayRef)result_osx, + CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_osx))); + } else { + // Result type is not an array, so only one match can be returned. + *result = (id_ios) ? result_ios : result_osx; + CFRetainSafe(*result); + } + } + CFReleaseSafe(result_osx); + CFReleaseSafe(result_ios); + return errSecSuccess; + } + } else if (can_target_ios) { + // Only iOS keychain was targetted. + AssignOrReleaseResult(result_ios, result); + return status_ios; + } else if (can_target_osx) { + // Only OSX keychain was targetted. + AssignOrReleaseResult(result_osx, result); + return status_osx; + } else { + // Query could not run at all? + return errSecParam; + } +} OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result) { - secitemlog(LOG_NOTICE, "SecItemCopyMatching"); + os_activity_t activity = os_activity_create("SecItemCopyMatching", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT); + os_activity_scope(activity); + os_release(activity); + if (!query) { return errSecParam; } @@ -4450,73 +4820,51 @@ SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result) OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound; CFTypeRef result_osx = NULL, result_ios = NULL; - Boolean ios_only = SecItemNoLegacy(query); - Boolean sync_enabled = SecItemSyncEnabled(); - Boolean search_ios = SecItemSynchronizable(query); - Boolean merge_search = SecItemSynchronizableAny(query); - Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query); + bool can_target_ios, can_target_osx; + OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx); + if (status != errSecSuccess) { + return status; + } - if (ios_only || (sync_enabled && (merge_search || persistref_ios || search_ios))) { + if (can_target_ios) { CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query, CFDictionaryGetValue(query, kSecClass), true, false, false, false, true, true); if (!attrs_ios) { status_ios = errSecParam; } else { - SecItemUnlockSynchronizableKeychain(); - status_ios = SecItemCopyMatching_ios(attrs_ios, &result_ios); + status_ios = SecItemCopyMatching_ios(attrs_ios, &result_ios); CFRelease(attrs_ios); } secitemlog(LOG_NOTICE, "SecItemCopyMatching_ios result: %d", status_ios); - if (ios_only || !merge_search || persistref_ios) { - AssignOrReleaseResult(result_ios, result); - return status_ios; // no need to search non-syncable keychains - } - } - - CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query, - CFDictionaryGetValue(query, kSecClass), false, false, true, false, true, true); - if (!attrs_osx) { - status_osx = errSecParam; - } - else { - status_osx = SecItemCopyMatching_osx(attrs_osx, &result_osx); - CFRelease(attrs_osx); } - secitemlog(LOG_NOTICE, "SecItemCopyMatching_osx result: %d", status_osx); - // If one of the searches failed to occur or produce results, we can eliminate it - if (result_ios == NULL) { - AssignOrReleaseResult(result_osx, result); - return status_osx; // we can only have non-syncable results - } - if (result_osx == NULL) { - AssignOrReleaseResult(result_ios, result); - return status_ios; // we can only have syncable results + if (can_target_osx) { + CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query, + CFDictionaryGetValue(query, kSecClass), false, false, true, false, true, true); + if (!attrs_osx) { + status_osx = errSecParam; + } + else { + status_osx = SecItemCopyMatching_osx(attrs_osx, &result_osx); + CFRelease(attrs_osx); + } + secitemlog(LOG_NOTICE, "SecItemCopyMatching_osx result: %d", status_osx); } - // If we get here, need to merge results - CFTypeRef result_merged = SecItemCopyMergedResults(query, result_osx, result_ios); - CFReleaseSafe(result_osx); - CFReleaseSafe(result_ios); - AssignOrReleaseResult(result_merged, result); - - if (status_osx == status_ios) { - return status_osx; // both searches produced the same result - } - else if (!status_osx || !status_ios) { - return errSecSuccess; // one of the searches succeeded - } - else if (status_osx == errSecItemNotFound) { - return status_ios; // this failure was more interesting - } - return status_osx; + status = SecItemMergeResults(can_target_ios, status_ios, result_ios, + can_target_osx, status_osx, result_osx, result); + secitemlog(LOG_NOTICE, "SecItemCopyMatching result: %d", status); + return status; } OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) { - secitemlog(LOG_NOTICE, "SecItemAdd"); + os_activity_t activity = os_activity_create("SecItemAdd", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT); + os_activity_scope(activity); + os_release(activity); + if (!attributes) { return errSecParam; } @@ -4525,52 +4873,50 @@ SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) } secitemshow(attributes, "SecItemAdd attrs:"); - OSStatus status_osx, status_ios; CFTypeRef result_osx = NULL, result_ios = NULL; - Boolean ios_only = SecItemNoLegacy(attributes); - Boolean sync_enabled = SecItemSyncEnabled(); - Boolean add_ios = SecItemSynchronizable(attributes); + bool can_target_ios, can_target_osx; + OSStatus status = SecItemCategorizeQuery(attributes, can_target_ios, can_target_osx); + if (status != errSecSuccess) { + return status; + } - if (ios_only || (sync_enabled && add_ios)) { + // SecItemAdd cannot be really done on both keychains. In order to keep backward compatibility + // with existing applications, we prefer to add items into legacy keychain and fallback + // into iOS (modern) keychain only when the query is not suitable for legacy keychain. + if (!can_target_osx) { CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(attributes, NULL, true, true, false, false, false, false); if (!attrs_ios) { - status_ios = errSecParam; - } - else { - SecItemUnlockSynchronizableKeychain(); - status_ios = SecItemAdd_ios(attrs_ios, &result_ios); + status = errSecParam; + } else { + status = SecItemAdd_ios(attrs_ios, &result_ios); CFRelease(attrs_ios); } - secitemlog(LOG_NOTICE, "SecItemAdd_ios result: %d", status_ios); - if (result) - *result = result_ios; - else - CFReleaseSafe(result_ios); - return status_ios; - } - - CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(attributes, - NULL, false, false, true, false, false, false); - if (!attrs_osx) { - status_osx = errSecParam; - } - else { - status_osx = SecItemAdd_osx(attrs_osx, &result_osx); - CFRelease(attrs_osx); + secitemlog(LOG_NOTICE, "SecItemAdd_ios result: %d", status); + AssignOrReleaseResult(result_ios, result); + return status; + } else { + CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(attributes, + NULL, false, false, true, false, false, false); + if (!attrs_osx) { + status = errSecParam; + } else { + status = SecItemAdd_osx(attrs_osx, &result_osx); + CFRelease(attrs_osx); + } + secitemlog(LOG_NOTICE, "SecItemAdd_osx result: %d", status); + AssignOrReleaseResult(result_osx, result); + return status; } - secitemlog(LOG_NOTICE, "SecItemAdd_osx result: %d", status_osx); - if (result) - *result = result_osx; - else - CFReleaseSafe(result_osx); - return status_osx; } OSStatus SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate) { - secitemlog(LOG_NOTICE, "SecItemUpdate"); + os_activity_t activity = os_activity_create("SecItemUpdate", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT); + os_activity_scope(activity); + os_release(activity); + if (!query || !attributesToUpdate) { return errSecParam; } @@ -4578,130 +4924,107 @@ SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate) secitemshow(attributesToUpdate, "SecItemUpdate attrs:"); OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound; - Boolean ios_only = SecItemNoLegacy(query); - Boolean sync_enabled = SecItemSyncEnabled(); - Boolean search_ios = SecItemSynchronizable(query); - Boolean merge_search = SecItemSynchronizableAny(query); - Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query); + bool can_target_ios, can_target_osx; + OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx); + if (status != errSecSuccess) { + return status; + } - if (ios_only || (sync_enabled && (merge_search || persistref_ios || search_ios))) { + if (can_target_ios) { CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query, CFDictionaryGetValue(query, kSecClass), true, true, false, true, true, true); if (!attrs_ios) { status_ios = errSecParam; } else { - SecItemUnlockSynchronizableKeychain(); - if (SecItemHasSynchronizableUpdate(true, attributesToUpdate)) + if (SecItemHasSynchronizableUpdate(true, attributesToUpdate)) { status_ios = SecItemChangeSynchronizability(attrs_ios, attributesToUpdate, false); - else + } else { status_ios = SecItemUpdate_ios(attrs_ios, attributesToUpdate); + } CFRelease(attrs_ios); } secitemlog(LOG_NOTICE, "SecItemUpdate_ios result: %d", status_ios); - if (ios_only || !merge_search || persistref_ios) - return status_ios; } - CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query, - CFDictionaryGetValue(query, kSecClass), false, false, true, true, true, true); - if (!attrs_osx) { - status_osx = errSecParam; - } - else { - if (SecItemHasSynchronizableUpdate(false, attributesToUpdate)) - status_osx = SecItemChangeSynchronizability(attrs_osx, attributesToUpdate, true); - else - status_osx = SecItemUpdate_osx(attrs_osx, attributesToUpdate); - - CFRelease(attrs_osx); - } - secitemlog(LOG_NOTICE, "SecItemUpdate_osx result: %d", status_osx); - if (merge_search) { - // Harmonize the result of the update attempts. - if (status_osx == status_ios) { - // both updates produced the same result - return status_ios; + if (can_target_osx) { + CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query, + CFDictionaryGetValue(query, kSecClass), false, false, true, true, true, true); + if (!attrs_osx) { + status_osx = errSecParam; } - else if (!status_osx || !status_ios) { - // one of the updates succeeded, but the other failed - if (status_osx == errSecItemNotFound || status_ios == errSecItemNotFound) - return errSecSuccess; // item only found in one keychain + else { + if (SecItemHasSynchronizableUpdate(false, attributesToUpdate)) + status_osx = SecItemChangeSynchronizability(attrs_osx, attributesToUpdate, true); else - return (status_osx) ? status_osx : status_ios; // return the error - } - else if (status_osx == errSecItemNotFound) { - // both updates failed, status_ios failure is more interesting - // since the item was actually found - return status_ios; + status_osx = SecItemUpdate_osx(attrs_osx, attributesToUpdate); + + CFRelease(attrs_osx); } + secitemlog(LOG_NOTICE, "SecItemUpdate_osx result: %d", status_osx); } - return status_osx; + + status = SecItemMergeResults(can_target_ios, status_ios, NULL, + can_target_osx, status_osx, NULL, NULL); + secitemlog(LOG_NOTICE, "SecItemUpdate result: %d", status); + return status; } OSStatus SecItemDelete(CFDictionaryRef query) { - secitemlog(LOG_NOTICE, "SecItemDelete"); + os_activity_t activity = os_activity_create("SecItemDelete", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT); + os_activity_scope(activity); + os_release(activity); + if (!query) { return errSecParam; } secitemshow(query, "SecItemDelete query:"); OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound; - Boolean ios_only = SecItemNoLegacy(query); - Boolean sync_enabled = SecItemSyncEnabled(); - Boolean search_ios = SecItemSynchronizable(query); - Boolean merge_search = SecItemSynchronizableAny(query); - Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query); + bool can_target_ios, can_target_osx; + OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx); + if (status != errSecSuccess) { + return status; + } - if (ios_only || (sync_enabled && (merge_search || persistref_ios || search_ios))) { + if (can_target_ios) { CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query, NULL, true, true, false, true, true, true); if (!attrs_ios) { status_ios = errSecParam; - } - else { - SecItemUnlockSynchronizableKeychain(); + } else { status_ios = SecItemDelete_ios(attrs_ios); CFRelease(attrs_ios); } secitemlog(LOG_NOTICE, "SecItemDelete_ios result: %d", status_ios); - if (ios_only || !merge_search || persistref_ios) - return status_ios; - } - - CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query, - NULL, false, false, true, true, true, true); - if (!attrs_osx) { - status_osx = errSecParam; - } - else { - status_osx = SecItemDelete_osx(attrs_osx); - CFRelease(attrs_osx); } - secitemlog(LOG_NOTICE, "SecItemDelete_osx result: %d", status_osx); - if (merge_search) { - // Harmonize the result of the delete attempts. - if (status_osx == status_ios) { - // both deletes produced the same result - return status_ios; - } - else if (!status_osx || !status_ios) { - // one of the deletes succeeded, but the other failed - if (status_osx == errSecItemNotFound || status_ios == errSecItemNotFound) - return errSecSuccess; // item only found in one keychain - else - return (status_osx) ? status_osx : status_ios; // return the error - } - else if (status_osx == errSecItemNotFound) { - // both deletes failed, status_ios failure is more interesting - // since the item was actually found - return status_ios; + if (can_target_osx) { + CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query, + NULL, false, false, true, true, true, true); + if (!attrs_osx) { + status_osx = errSecParam; + } else { + status_osx = SecItemDelete_osx(attrs_osx); + CFRelease(attrs_osx); } + secitemlog(LOG_NOTICE, "SecItemDelete_osx result: %d", status_osx); } - return status_osx; + + status = SecItemMergeResults(can_target_ios, status_ios, NULL, + can_target_osx, status_osx, NULL, NULL); + secitemlog(LOG_NOTICE, "SecItemCopyDelete result: %d", status); + return status; +} + +OSStatus +SecItemUpdateTokenItems(CFTypeRef tokenID, CFArrayRef tokenItemsAttributes) +{ + OSStatus status = SecItemUpdateTokenItems_ios(tokenID, tokenItemsAttributes); + secitemlog(LOG_NOTICE, "SecItemUpdateTokenItems_ios result: %d", status); + return status; } OSStatus @@ -4799,7 +5122,11 @@ SecItemAdd_osx( // but in any case it should try to add the certificate. See . require_action(!itemParams->returnIdentity, error_exit, status = errSecItemInvalidValue); - if (!itemParams->useItems) { + if (itemParams->useItems == NULL) { + + require_action(itemParams->itemData == NULL || CFGetTypeID(itemParams->itemData) == CFDataGetTypeID(), + error_exit, status = errSecItemInvalidValue); + // create a single keychain item specified by the input attributes status = SecKeychainItemCreateFromContent(itemParams->itemClass, itemParams->attrList, @@ -4927,7 +5254,7 @@ SecItemUpdate_osx( // run the provided query to get a list of items to update CFTypeRef results = NULL; - OSStatus status = SecItemCopyMatching(query, &results); + OSStatus status = SecItemCopyMatching_osx(query, &results); if (status != errSecSuccess) return status; // nothing was matched, or the query was bad