]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_keychain/lib/SecItem.cpp
Security-58286.20.16.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / SecItem.cpp
index c25efd5cc5fb197dfa8e10e6d156e88b3bd776b9..49a0a4d8a76f58be9d622e19d95a3705011c0540 100644 (file)
@@ -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 <Security/SecBase.h>
 #include <Security/SecKeychainItem.h>
 #include <Security/SecCertificate.h>
+#include <Security/SecCertificatePriv.h>
 #include <sys/param.h>
 #include "cssmdatetime.h"
 #include "SecItem.h"
@@ -53,6 +54,7 @@
 
 #include <login/SessionAgentCom.h>
 #include <login/SessionAgentStatusCom.h>
+#include <os/activity.h>
 
 
 const uint8_t kUUIDStringLength = 36;
@@ -73,6 +75,9 @@ 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);
@@ -584,7 +589,7 @@ _ConvertNewFormatToOldFormat(
 
        // 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;
@@ -663,6 +668,7 @@ _ConvertOldFormatToNewFormat(
                                                        break;
                                                default:
                                                        stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyRecordValue);
+                            retainString = false;
                                                        break;
                                        }
                                        if (stringRef) {
@@ -949,6 +955,7 @@ _CreateAttributesDictionaryFromKeyItem(
                                                        break;
                                                default:
                                                        stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%u"), (unsigned int)keyRecordValue);
+                            retainString = false;
                                                        break;
                                        }
                                        if (stringRef) {
@@ -2233,7 +2240,7 @@ _SafeSecKeychainItemDelete(
                }
 
                // create SecTrustedApplicationRef for current application/tool
-               CFReleaseSafe(currentAppRef);
+               CFReleaseNull(currentAppRef);
                status = SecTrustedApplicationCreateFromPath(NULL, &currentAppRef);
                require_noerr(status, finish);
                require_quiet(currentAppRef != NULL, finish);
@@ -2509,7 +2516,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 +2531,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 +2835,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 +2947,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 +2966,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 +3075,32 @@ _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error)
                require_action(!(itemParams->itemClass == 0 && !itemParams->useItems), error_exit, status = errSecItemClassMissing);
        }
 
+    // kSecMatchIssuers is only permitted with identities.
+    // Convert the input issuers to normalized form.
+    require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchIssuers, (const void **)&itemParams->matchIssuers, CFArrayGetTypeID(), NULL), error_exit);
+    if (itemParams->matchIssuers) {
+        require_action(itemParams->returnIdentity, 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 +3149,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 +3169,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 +3223,7 @@ _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error)
        }
 
 error_exit:
+    CFReleaseNull(policyDict);
        if (status) {
                _FreeSecItemParams(itemParams);
                itemParams = NULL;
@@ -3357,6 +3400,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 +3579,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 +3714,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 +4015,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;
 }
@@ -4212,7 +4329,7 @@ void SecItemParentCachePurge() {
     CFReleaseNull(gParentCertCacheList);
 }
 
-static CFArrayRef parentCacheRead(SecCertificateRef certificate) {
+static CFArrayRef CF_RETURNS_RETAINED parentCacheRead(SecCertificateRef certificate) {
     CFArrayRef parents = NULL;
     CFIndex ix;
     CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
@@ -4261,13 +4378,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 */
@@ -4328,7 +4445,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 +4463,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);
@@ -4675,7 +4792,10 @@ ShouldTryUnlockKeybag(CFDictionaryRef query, OSErr status)
 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;
        }
@@ -4732,7 +4852,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;
        }
@@ -4789,7 +4912,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;
        }
@@ -4860,7 +4986,10 @@ 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;
        }