X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/6b200bc335dc93c5516ccb52f14bd896d8c7fad7..ee5f17c73ddf6cea151be3383378b7972c71f538:/OSX/libsecurity_keychain/lib/SecItem.cpp diff --git a/OSX/libsecurity_keychain/lib/SecItem.cpp b/OSX/libsecurity_keychain/lib/SecItem.cpp index c25efd5c..2cdb17f9 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-2019 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -22,24 +22,26 @@ */ #include "SecBridge.h" -#include "SecInternal.h" +#include #include #include #include #include #include +#include #include #include "cssmdatetime.h" -#include "SecItem.h" -#include "SecItemPriv.h" -#include "SecIdentitySearchPriv.h" -#include "SecKeychainPriv.h" -#include "SecCertificatePriv.h" -#include "SecCertificatePrivP.h" +#include +#include +#include +#include +#include +#include #include "TrustAdditions.h" #include "TrustSettingsSchema.h" #include #include "utilities/array_size.h" +#include "utilities/SecCFWrappers.h" #include #include @@ -53,6 +55,8 @@ #include #include +#include +#include const uint8_t kUUIDStringLength = 36; @@ -73,9 +77,13 @@ OSStatus SecItemUpdateTokenItems_ios(CFTypeRef tokenID, CFArrayRef tokenItemsAtt 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, ...) { @@ -532,6 +540,12 @@ _ConvertNewFormatToOldFormat( SecKeychainAttributeList* &attrList ) { + // make storage to extract the dictionary items + CFIndex itemsInDictionary = CFDictionaryGetCount(dictionaryRef); + if (itemsInDictionary > 10000) { + return errSecParam; + } + // get the keychain attributes array from the data item // here's the problem. On the one hand, we have a dictionary that is purported to contain // attributes for our type. On the other hand, the dictionary may contain items we don't support, @@ -540,15 +554,10 @@ _ConvertNewFormatToOldFormat( // setup the return attrList = (SecKeychainAttributeList*) calloc(1, sizeof(SecKeychainAttributeList)); - // make storage to extract the dictionary items - CFIndex itemsInDictionary = CFDictionaryGetCount(dictionaryRef); - CFTypeRef keys[itemsInDictionary]; - CFTypeRef values[itemsInDictionary]; - - CFTypeRef *keysPtr = keys; - CFTypeRef *valuesPtr = values; + std::vector keys(itemsInDictionary); + std::vector values(itemsInDictionary); - CFDictionaryGetKeysAndValues(dictionaryRef, keys, values); + CFDictionaryGetKeysAndValues(dictionaryRef, keys.data(), values.data()); // count the number of items we are interested in CFIndex count = 0; @@ -556,12 +565,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) @@ -578,28 +587,33 @@ _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); - // fill out the array - int resultPointer = 0; - for (i = 0; i < itemsInDictionary; ++i) - { - if (values[i] != NULL) - { - attrList->attr[resultPointer].tag = tags[i]; + if(count == 0) { + attrList->attr = NULL; + } else { + attrList->attr = (SecKeychainAttribute*) calloc(count, sizeof(SecKeychainAttribute)); - // 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; - } - } + // 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], values[i], attrList->attr[resultPointer].length); + resultPointer += 1; + } + } + } return errSecSuccess; } @@ -663,6 +677,7 @@ _ConvertOldFormatToNewFormat( break; default: stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyRecordValue); + retainString = false; break; } if (stringRef) { @@ -949,6 +964,7 @@ _CreateAttributesDictionaryFromKeyItem( break; default: stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%u"), (unsigned int)keyRecordValue); + retainString = false; break; } if (stringRef) { @@ -1571,7 +1587,7 @@ _CreateSecKeychainKeyAttributeListFromDictionary( // [5] get the kSecKeyKeyType number if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeyType, (const void **)&value) && value) { - UInt32 keyAlgValue = _SecAlgorithmTypeFromSecAttrKeyType(kSecAttrKeyType); + UInt32 keyAlgValue = _SecAlgorithmTypeFromSecAttrKeyType(value); if (keyAlgValue != 0) { attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32)); require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall); @@ -2233,7 +2249,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); @@ -2351,24 +2367,11 @@ _ReplaceKeychainItem( // make attribute list for new item (the data is still owned by attrList) newAttrList.count = attrList->count; - newAttrList.attr = (SecKeychainAttribute *) malloc(sizeof(SecKeychainAttribute) * attrList->count); + newAttrList.attr = (SecKeychainAttribute *) calloc(attrList->count, sizeof(SecKeychainAttribute)); int i, newCount; 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; @@ -2509,7 +2512,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); @@ -2524,7 +2527,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) @@ -2828,6 +2831,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 @@ -2939,6 +2943,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); @@ -2957,6 +2962,7 @@ _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error) { OSStatus status; CFTypeRef value = NULL; + CFDictionaryRef policyDict = NULL; SecItemParams *itemParams = (SecItemParams *)calloc(1, sizeof(struct SecItemParams)); require_action(itemParams != NULL, error_exit, status = errSecAllocate); @@ -3065,6 +3071,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); @@ -3113,12 +3146,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 } @@ -3127,12 +3166,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) @@ -3181,6 +3220,7 @@ _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error) } error_exit: + CFReleaseNull(policyDict); if (status) { _FreeSecItemParams(itemParams); itemParams = NULL; @@ -3245,6 +3285,7 @@ _FilterWithPolicy(SecPolicyRef policy, CFDateRef date, SecCertificateRef cert) SecTrustResultType trustResult; Boolean needChain = false; + Boolean needCSEKU = false; OSStatus status; if (!policy || !cert) return errSecParam; @@ -3258,13 +3299,33 @@ _FilterWithPolicy(SecPolicyRef policy, CFDateRef date, SecCertificateRef cert) if(status) goto cleanup; } - /* Check whether this is the X509 Basic policy, which means chain building */ + /* Check whether we can avoid full chain evaluation, based on policy */ props = SecPolicyCopyProperties(policy); if (props) { CFTypeRef oid = (CFTypeRef) CFDictionaryGetValue(props, kSecPolicyOid); if (oid && (CFEqual(oid, kSecPolicyAppleX509Basic) || - CFEqual(oid, kSecPolicyAppleRevocation))) { + CFEqual(oid, kSecPolicyAppleRevocation))) { needChain = true; + } else if (oid && (CFEqual(oid, kSecPolicyAppleCodeSigning))) { + needCSEKU = true; + } + } + + /* If a code signing EKU purpose is needed, filter out certs without it here + to reduce log noise associated with evaluation failures. */ + if (needCSEKU) { + CFDataRef eku = CFDataCreate(kCFAllocatorDefault, + oidExtendedKeyUsageCodeSigning.data, + oidExtendedKeyUsageCodeSigning.length); + if (eku) { + if (!SecPolicyCheckCertExtendedKeyUsage(cert, eku)) { + needCSEKU = false; + } + CFRelease(eku); + } + if (!needCSEKU) { + status = errSecCertificateCannotOperate; + goto cleanup; } } @@ -3357,6 +3418,77 @@ _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) { @@ -3465,16 +3597,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; @@ -3599,6 +3732,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; @@ -3895,10 +4033,7 @@ static Boolean SecItemSynchronizable(CFDictionaryRef query) static Boolean SecItemIsIOSPersistentReference(CFTypeRef value) { 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 ::_SecItemParsePersistentRef((CFDataRef)value, NULL, NULL, NULL); } return false; } @@ -3908,21 +4043,38 @@ extern "C" Boolean SecKeyIsCDSAKey(SecKeyRef ref); // // Function to find out which keychains are targetted by the query. // -static OSStatus SecItemCategorizeQuery(CFDictionaryRef query, bool &can_target_ios, bool &can_target_osx) +static OSStatus SecItemCategorizeQuery(CFDictionaryRef query, bool &can_target_ios, bool &can_target_osx, bool &useDataProtectionKeychainFlag) { // By default, target both keychain. can_target_osx = can_target_ios = true; + useDataProtectionKeychainFlag = false; // Check no-legacy flag. - CFTypeRef value = CFDictionaryGetValue(query, kSecAttrNoLegacy); - if (value != NULL) { - can_target_ios = readNumber(value) != 0; + // it's iOS or bust if we're on MZ! + CFTypeRef useDataProtection = NULL; + if (_CFMZEnabled()) { + useDataProtection = kCFBooleanTrue; + } + else { + // In case your CFDict is dumb and compares by pointer equality we check both versions of the symbol + if (!CFDictionaryGetValueIfPresent(query, kSecUseDataProtectionKeychain, &useDataProtection)) { + // Ah the irony of ignoring deprecation while checking for a legacy-avoiding attribute +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + useDataProtection = CFDictionaryGetValue(query, kSecAttrNoLegacy); +#pragma clang diagnostic pop + } + } + + if (useDataProtection != NULL) { + useDataProtectionKeychainFlag = readNumber(useDataProtection); + can_target_ios = useDataProtectionKeychainFlag; can_target_osx = !can_target_ios; return errSecSuccess; } // Check whether the query contains kSecValueRef and modify can_ flags according to the kind and type of the value. - value = CFDictionaryGetValue(query, kSecValueRef); + CFTypeRef value = CFDictionaryGetValue(query, kSecValueRef); if (value != NULL) { CFTypeID typeID = CFGetTypeID(value); if (typeID == SecKeyGetTypeID()) { @@ -3986,6 +4138,13 @@ static OSStatus SecItemCategorizeQuery(CFDictionaryRef query, bool &can_target_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; } @@ -4151,7 +4310,7 @@ SecItemCreateFromAttributeDictionary_osx(CFDictionaryRef refAttributes) { CFTypeRef v; Item item = Item(item_class, &attrs, 0, ""); - v = CFDictionaryGetValue(refAttributes, kSecValuePersistentRef); + v = CFCast(CFData, CFDictionaryGetValue(refAttributes, kSecValuePersistentRef)); if (v) { item->setPersistentRef((CFDataRef)v); } @@ -4201,24 +4360,27 @@ SecItemValidateAppleApplicationGroupAccess(CFStringRef group) return status; } -static Mutex gParentCertCacheLock; +static Mutex& gParentCertCacheLock() { + static Mutex fParentCertCacheLock; + return fParentCertCacheLock; +} static CFMutableDictionaryRef gParentCertCache; static CFMutableArrayRef gParentCertCacheList; #define PARENT_CACHE_SIZE 100 void SecItemParentCachePurge() { - StLock _(gParentCertCacheLock); + StLock _(gParentCertCacheLock()); CFReleaseNull(gParentCertCache); CFReleaseNull(gParentCertCacheList); } -static CFArrayRef parentCacheRead(SecCertificateRef certificate) { +static CFArrayRef CF_RETURNS_RETAINED parentCacheRead(SecCertificateRef certificate) { CFArrayRef parents = NULL; CFIndex ix; CFDataRef digest = SecCertificateGetSHA1Digest(certificate); if (!digest) return NULL; - StLock _(gParentCertCacheLock); + StLock _(gParentCertCacheLock()); if (gParentCertCache && gParentCertCacheList) { if (0 <= (ix = CFArrayGetFirstIndexOfValue(gParentCertCacheList, CFRangeMake(0, CFArrayGetCount(gParentCertCacheList)), @@ -4237,7 +4399,7 @@ static void parentCacheWrite(SecCertificateRef certificate, CFArrayRef parents) CFDataRef digest = SecCertificateGetSHA1Digest(certificate); if (!digest) return; - StLock _(gParentCertCacheLock); + StLock _(gParentCertCacheLock()); if (!gParentCertCache || !gParentCertCacheList) { CFReleaseNull(gParentCertCache); gParentCertCache = makeCFMutableDictionary(); @@ -4253,6 +4415,7 @@ static void parentCacheWrite(SecCertificateRef certificate, CFArrayRef parents) 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); @@ -4261,13 +4424,13 @@ static void parentCacheWrite(SecCertificateRef certificate, CFArrayRef parents) } /* - * SecItemCopyParentCertificates returns an array of zero of more possible + * 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(SecCertificateRef certificate, void *context) +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 */ @@ -4277,7 +4440,7 @@ SecItemCopyParentCertificates(SecCertificateRef certificate, void *context) } /* Cache miss. Query for parents. */ -#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) +#if TARGET_OS_OSX CFDataRef normalizedIssuer = SecCertificateCopyNormalizedIssuerContent(certificate, NULL); #else CFDataRef normalizedIssuer = SecCertificateGetNormalizedIssuerContent(certificate); @@ -4328,7 +4491,7 @@ SecItemCopyParentCertificates(SecCertificateRef certificate, void *context) } if ((status != errSecSuccess) && (status != errSecItemNotFound)) { - secitemlog(LOG_WARNING, "SecItemCopyParentCertificates: %d", (int)status); + secitemlog(LOG_WARNING, "SecItemCopyParentCertificates_osx: %d", (int)status); } CFRelease(query); @@ -4346,7 +4509,7 @@ SecItemCopyParentCertificates(SecCertificateRef certificate, void *context) } } } - } else if (resultType == CFDataGetTypeID()) { + } else if (results && resultType == CFDataGetTypeID()) { SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)results); if (cert) { CFArrayAppendValue(result, cert); @@ -4367,11 +4530,10 @@ SecCertificateRef SecItemCopyStoredCertificate(SecCertificateRef certificate, vo #pragma unused (context) /* for now; in future this can reference a container object */ /* Certificates are unique by issuer and serial number. */ -#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) - CFDataRef serialNumber = SecCertificateCopySerialNumber(certificate, NULL); + CFDataRef serialNumber = SecCertificateCopySerialNumberData(certificate, NULL); +#if TARGET_OS_OSX CFDataRef normalizedIssuer = SecCertificateCopyNormalizedIssuerContent(certificate, NULL); #else - CFDataRef serialNumber = SecCertificateCopySerialNumber(certificate); CFDataRef normalizedIssuer = SecCertificateGetNormalizedIssuerContent(certificate); CFRetainSafe(normalizedIssuer); #endif @@ -4543,6 +4705,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. */ @@ -4558,13 +4732,58 @@ SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass, } /* This attribute is consumed by the bridge itself. */ + CFDictionaryRemoveValue(result, kSecUseDataProtectionKeychain); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // Also remove deprecated symbol in case your CFDict is derpy CFDictionaryRemoveValue(result, kSecAttrNoLegacy); +#pragma clang diagnostic pop return result; } } /* 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; +} + static OSStatus SecItemMergeResults(bool can_target_ios, OSStatus status_ios, CFTypeRef result_ios, bool can_target_osx, OSStatus status_osx, CFTypeRef result_osx, @@ -4633,49 +4852,13 @@ SecItemMergeResults(bool can_target_ios, OSStatus status_ios, CFTypeRef result_i } } -static bool -ShouldTryUnlockKeybag(CFDictionaryRef query, OSErr status) -{ - static __typeof(SASSessionStateForUser) *soft_SASSessionStateForUser = NULL; - static dispatch_once_t onceToken; - static void *framework; - - if (status != errSecInteractionNotAllowed) - return false; - - // If the query disabled authUI, respect it. - CFTypeRef authUI = NULL; - if (query) { - authUI = CFDictionaryGetValue(query, kSecUseAuthenticationUI); - if (authUI == NULL) { - authUI = CFDictionaryGetValue(query, kSecUseNoAuthenticationUI); - authUI = (authUI != NULL && CFEqual(authUI, kCFBooleanTrue)) ? kSecUseAuthenticationUIFail : NULL; - } - } - if (authUI && !CFEqual(authUI, kSecUseAuthenticationUIAllow)) - return false; - - dispatch_once(&onceToken, ^{ - framework = dlopen("/System/Library/PrivateFrameworks/login.framework/login", RTLD_LAZY); - if (framework == NULL) - return; - soft_SASSessionStateForUser = (__typeof(soft_SASSessionStateForUser)) dlsym(framework, "SASSessionStateForUser"); - }); - - if (soft_SASSessionStateForUser == NULL) - return false; - - SessionAgentState sessionState = soft_SASSessionStateForUser(getuid()); - if(sessionState != kSA_state_desktopshowing) - return false; - - return true; -} - 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; } @@ -4683,8 +4866,8 @@ SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result) OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound; CFTypeRef result_osx = NULL, result_ios = NULL; - bool can_target_ios, can_target_osx; - OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx); + bool can_target_ios, can_target_osx, useDataProtectionKeychainFlag; + OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx, useDataProtectionKeychainFlag); if (status != errSecSuccess) { return status; } @@ -4697,14 +4880,6 @@ SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result) } else { status_ios = SecItemCopyMatching_ios(attrs_ios, &result_ios); - if(ShouldTryUnlockKeybag(query, status_ios)) { - // The keybag is locked. Attempt to unlock it... - secitemlog(LOG_WARNING, "SecItemCopyMatching triggering SecurityAgent"); - if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(1)) { - CFReleaseNull(result_ios); - status_ios = SecItemCopyMatching_ios(attrs_ios, &result_ios); - } - } CFRelease(attrs_ios); } secitemlog(LOG_NOTICE, "SecItemCopyMatching_ios result: %d", status_ios); @@ -4732,7 +4907,10 @@ SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result) 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; } @@ -4742,8 +4920,8 @@ SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) secitemshow(attributes, "SecItemAdd attrs:"); CFTypeRef result_osx = NULL, result_ios = NULL; - bool can_target_ios, can_target_osx; - OSStatus status = SecItemCategorizeQuery(attributes, can_target_ios, can_target_osx); + bool can_target_ios, can_target_osx, useDataProtectionKeychainFlag; + OSStatus status = SecItemCategorizeQuery(attributes, can_target_ios, can_target_osx, useDataProtectionKeychainFlag); if (status != errSecSuccess) { return status; } @@ -4758,14 +4936,6 @@ SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) status = errSecParam; } else { status = SecItemAdd_ios(attrs_ios, &result_ios); - if(ShouldTryUnlockKeybag(attributes, status)) { - // The keybag is locked. Attempt to unlock it... - secitemlog(LOG_WARNING, "SecItemAdd triggering SecurityAgent"); - if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(3)) { - CFReleaseNull(result_ios); - status = SecItemAdd_ios(attrs_ios, &result_ios); - } - } CFRelease(attrs_ios); } secitemlog(LOG_NOTICE, "SecItemAdd_ios result: %d", status); @@ -4789,7 +4959,10 @@ SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) 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; } @@ -4797,12 +4970,29 @@ SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate) secitemshow(attributesToUpdate, "SecItemUpdate attrs:"); OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound; - bool can_target_ios, can_target_osx; - OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx); + bool can_target_ios, can_target_osx, useDataProtectionKeychainFlag; + OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx, useDataProtectionKeychainFlag); if (status != errSecSuccess) { return status; } + /* + * If the user have explicity opted in to UseDataProtectionKeychain, then don't touch the legacy keychain at all + * ie don't move the item back to legacy keychain if you remove sync=1 or the inverse. + */ + if (useDataProtectionKeychainFlag) { + CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query, + CFDictionaryGetValue(query, kSecClass), true, true, false, true, true, true); + if (!attrs_ios) { + status_ios = errSecParam; + } else { + status_ios = SecItemUpdate_ios(attrs_ios, attributesToUpdate); + CFRelease(attrs_ios); + } + secitemlog(LOG_NOTICE, "SecItemUpdate(ios only) result: %d", status_ios); + return status_ios; + } + if (can_target_ios) { CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query, CFDictionaryGetValue(query, kSecClass), true, true, false, true, true, true); @@ -4812,22 +5002,8 @@ SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate) else { if (SecItemHasSynchronizableUpdate(true, attributesToUpdate)) { status_ios = SecItemChangeSynchronizability(attrs_ios, attributesToUpdate, false); - if(ShouldTryUnlockKeybag(query, status_ios)) { - // The keybag is locked. Attempt to unlock it... - secitemlog(LOG_WARNING, "SecItemUpdate triggering SecurityAgent"); - if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(1)) { - status_ios = SecItemChangeSynchronizability(attrs_ios, attributesToUpdate, false); - } - } } else { status_ios = SecItemUpdate_ios(attrs_ios, attributesToUpdate); - if(ShouldTryUnlockKeybag(query, status_ios)) { - // The keybag is locked. Attempt to unlock it... - secitemlog(LOG_WARNING, "SecItemUpdate triggering SecurityAgent"); - if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(1)) { - status_ios = SecItemUpdate_ios(attrs_ios, attributesToUpdate); - } - } } CFRelease(attrs_ios); } @@ -4860,15 +5036,18 @@ SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate) 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; - bool can_target_ios, can_target_osx; - OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx); + bool can_target_ios, can_target_osx, useDataProtectionKeychainFlag; + OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx, useDataProtectionKeychainFlag); if (status != errSecSuccess) { return status; } @@ -4907,13 +5086,6 @@ OSStatus SecItemUpdateTokenItems(CFTypeRef tokenID, CFArrayRef tokenItemsAttributes) { OSStatus status = SecItemUpdateTokenItems_ios(tokenID, tokenItemsAttributes); - if(ShouldTryUnlockKeybag(NULL, status)) { - // The keybag is locked. Attempt to unlock it... - if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(1)) { - secitemlog(LOG_WARNING, "SecItemUpdateTokenItems triggering SecurityAgent"); - status = SecItemUpdateTokenItems_ios(tokenID, tokenItemsAttributes); - } - } secitemlog(LOG_NOTICE, "SecItemUpdateTokenItems_ios result: %d", status); return status; }