]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_keychain/lib/SecItem.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / SecItem.cpp
index 2467ebb173328ae7ae00ec4410ca59fcdbbd1945..e5b078ad01aa9b189fd1ccc28b3dba9e469d732f 100644 (file)
@@ -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@
  *
  */
 
 #include "SecBridge.h"
-#include "SecInternal.h"
+#include <Security/SecInternal.h>
 #include <CoreFoundation/CoreFoundation.h>
 #include <security_utilities/cfutilities.h>
 #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"
-#include "SecItemPriv.h"
-#include "SecIdentitySearchPriv.h"
-#include "SecKeychainPriv.h"
-#include "SecCertificatePriv.h"
-#include "SecCertificatePrivP.h"
+#include <Security/SecItem.h>
+#include <Security/SecItemPriv.h>
+#include <Security/SecIdentitySearchPriv.h>
+#include <Security/SecKeychainPriv.h>
+#include <Security/SecCertificatePriv.h>
+#include <Security/SecPolicyPriv.h>
 #include "TrustAdditions.h"
+#include "TrustSettingsSchema.h"
+#include <Security/SecTrustPriv.h>
+#include "utilities/array_size.h"
+#include "utilities/SecCFWrappers.h"
+#include "LegacyAPICounts.h"
 
 #include <AssertMacros.h>
 #include <syslog.h>
+#include <dlfcn.h>
 
 #include <Security/SecTrustedApplication.h>
 #include <Security/SecTrustedApplicationPriv.h>
 #include <Security/SecCodePriv.h>
 #include <Security/SecRequirement.h>
 
+#include <login/SessionAgentCom.h>
+#include <login/SessionAgentStatusCom.h>
+#include <os/activity.h>
+#include <CoreFoundation/CFPriv.h>
+
+
 const uint8_t kUUIDStringLength = 36;
 
 OSStatus SecItemAdd_osx(CFDictionaryRef attributes, CFTypeRef *result);
@@ -60,14 +73,16 @@ OSStatus SecItemCopyMatching_ios(CFDictionaryRef query, CFTypeRef *result);
 OSStatus SecItemUpdate_ios(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
 OSStatus SecItemDelete_ios(CFDictionaryRef query);
 
-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, ...)
 {
@@ -524,6 +539,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,
@@ -532,15 +553,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<CFTypeRef> keys(itemsInDictionary);
+       std::vector<CFTypeRef> values(itemsInDictionary);
 
-       CFDictionaryGetKeysAndValues(dictionaryRef, keys, values);
+       CFDictionaryGetKeysAndValues(dictionaryRef, keys.data(), values.data());
 
        // count the number of items we are interested in
        CFIndex count = 0;
@@ -548,12 +564,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<SecKeychainAttrType> tags(itemsInDictionary);
+       std::vector<ItemRepresentation> 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,28 +586,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;
 }
@@ -655,6 +676,7 @@ _ConvertOldFormatToNewFormat(
                                                        break;
                                                default:
                                                        stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyRecordValue);
+                            retainString = false;
                                                        break;
                                        }
                                        if (stringRef) {
@@ -861,7 +883,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 +963,7 @@ _CreateAttributesDictionaryFromKeyItem(
                                                        break;
                                                default:
                                                        stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%u"), (unsigned int)keyRecordValue);
+                            retainString = false;
                                                        break;
                                        }
                                        if (stringRef) {
@@ -1179,7 +1202,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;
@@ -1563,7 +1586,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);
@@ -1997,7 +2020,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 +2248,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);
@@ -2343,24 +2366,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;
@@ -2412,7 +2422,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 +2494,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 +2511,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 +2526,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 +2748,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 +2763,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 +2790,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 +2830,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 +2942,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 +2961,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 +2996,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);
@@ -3025,11 +3063,40 @@ _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error)
        else if (value) {
                itemParams->itemClass = _ConvertItemClass(value, itemParams->keyClass, &itemParams->returnIdentity);
                if (itemParams->itemClass == kSecSymmetricKeyItemClass && !itemParams->keyClass) {
-                       itemParams->assumedKeyClass = kSecAttrKeyClassSymmetric; // no key class specified, so start with symmetric key class; will search the others later
+            // no key class specified, so start with symmetric key class; will search the others later in UpdateKeychainSearchAndCopyNext
+            itemParams->itemClass = kSecSymmetricKeyItemClass;
+            itemParams->assumedKeyClass = kSecAttrKeyClassPublic;
                }
                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);
@@ -3078,12 +3145,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
        }
 
@@ -3092,12 +3165,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)
@@ -3146,6 +3219,7 @@ _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error)
        }
 
 error_exit:
+    CFReleaseNull(policyDict);
        if (status) {
                _FreeSecItemParams(itemParams);
                itemParams = NULL;
@@ -3198,31 +3272,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)
 {
@@ -3234,8 +3283,8 @@ _FilterWithPolicy(SecPolicyRef policy, CFDateRef date, SecCertificateRef cert)
        SecTrustRef trust = NULL;
 
        SecTrustResultType      trustResult;
-       CSSM_TP_APPLE_EVIDENCE_INFO *evidence = NULL;
        Boolean needChain = false;
+       Boolean needCSEKU = false;
        OSStatus status;
        if (!policy || !cert) return errSecParam;
 
@@ -3249,42 +3298,41 @@ _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)) {
+               if (oid && (CFEqual(oid, kSecPolicyAppleX509Basic) ||
+                           CFEqual(oid, kSecPolicyAppleRevocation))) {
                        needChain = true;
+               } else if (oid && (CFEqual(oid, kSecPolicyAppleCodeSigning))) {
+                       needCSEKU = 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;
+       /* 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;
+               }
        }
 
-       /* 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 (!needChain) {
+               status = SecTrustEvaluateLeafOnly(trust, &trustResult);
+       } else {
+               status = SecTrustEvaluate(trust, &trustResult);
+       }
 
        if (!(trustResult == kSecTrustResultProceed ||
                  trustResult == kSecTrustResultUnspecified ||
@@ -3297,18 +3345,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:
@@ -3379,11 +3417,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
@@ -3397,9 +3506,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
@@ -3483,16 +3596,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;
@@ -3617,6 +3731,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;
@@ -3707,6 +3826,8 @@ AddItemResults(SecKeychainItemRef item,
        //
        // Note that we allocate *items if needed.
 
+       CFTypeRef localResult = NULL;
+
        if (!item || !itemParams || !result)
                return errSecParam;
 
@@ -3735,7 +3856,8 @@ AddItemResults(SecKeychainItemRef item,
                        CFArrayAppendValue(itemArray, itemRef);
                }
                else {
-                       *result = CFRetain((CFTypeRef)itemRef);
+                       CFReleaseNull(localResult);
+                       localResult = CFRetain((CFTypeRef)itemRef);
                }
        }
 
@@ -3754,7 +3876,8 @@ AddItemResults(SecKeychainItemRef item,
                                CFArrayAppendValue(itemArray, persistentRef);
                        }
                        else {
-                               *result = CFRetain(persistentRef);
+                               CFReleaseNull(localResult);
+                               localResult = CFRetain(persistentRef);
                        }
                        CFRelease(persistentRef);
                }
@@ -3778,7 +3901,8 @@ AddItemResults(SecKeychainItemRef item,
                                        CFArrayAppendValue(itemArray, dataRef);
                                }
                                else {
-                                       *result = CFRetain(dataRef);
+                                       CFReleaseNull(localResult);
+                                       localResult = CFRetain(dataRef);
                                }
                                CFRelease(dataRef);
                                status = errSecSuccess;
@@ -3800,7 +3924,8 @@ AddItemResults(SecKeychainItemRef item,
                                        CFArrayAppendValue(itemArray, dataRef);
                                }
                                else {
-                                       *result = CFRetain(dataRef);
+                                       CFReleaseNull(localResult);
+                                       localResult = CFRetain(dataRef);
                                }
                                CFRelease(dataRef);
                                (void) SecKeychainItemFreeContent(NULL, data);
@@ -3829,7 +3954,8 @@ AddItemResults(SecKeychainItemRef item,
                                CFArrayAppendValue(itemArray, attrsDict);
                        }
                        else {
-                               *result = CFRetain(attrsDict);
+                               CFReleaseNull(localResult);
+                               localResult = CFRetain(attrsDict);
                        }
                        CFRelease(attrsDict);
                }
@@ -3842,14 +3968,22 @@ AddItemResults(SecKeychainItemRef item,
                if (itemArray) {
                        CFArrayAppendValue(itemArray, itemDict);
                        CFRelease(itemDict);
-                       *result = itemArray;
+                       CFReleaseNull(localResult);
+                       localResult = itemArray;
                }
                else {
-                       *result = itemDict;
+                       CFReleaseNull(localResult);
+                       localResult = itemDict;
                }
        }
        else if (itemArray) {
-               *result = itemArray;
+               CFReleaseNull(localResult);
+               localResult = itemArray;
+       }
+
+       if (localResult) {
+               *result = localResult;
+               localResult = NULL;
        }
 
        return status;
@@ -3896,22 +4030,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.
 //
@@ -3924,28 +4042,124 @@ 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, bool &useDataProtectionKeychainFlag)
 {
-       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;
+    useDataProtectionKeychainFlag = false;
+
+       // Check no-legacy flag.
+    // 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;
        }
-       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;
 }
 
 //
@@ -3963,42 +4177,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)
@@ -4119,22 +4297,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
@@ -4142,71 +4316,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"));
+    Item item = Item(item_class, &attrs, 0, "");
+               v = CFCast(CFData, CFDictionaryGetValue(refAttributes, kSecValuePersistentRef));
                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++;
+                       item->setPersistentRef((CFDataRef)v);
                }
-#endif
-               Item item = Item(key_class, attrs, 0, "");
-               ItemImpl *real_item = item.get();
-               v = CFDictionaryGetValue(refAttributes, kSecValuePersistentRef);
-               if (v) {
-                       real_item->setPersistentRef((CFDataRef)v);
-               }
-               ref = real_item->handle();
-       } else {
-               // keys, certs, identities are not currently sync'able.
-               ref = NULL;
+               ref = item->handle();
        }
+
        return ref;
 }
 
@@ -4250,6 +4374,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<Mutex> _(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<Mutex> _(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<Mutex> _(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_OSX
+       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_OSX
+       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
@@ -4336,11 +4667,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.
                 */
@@ -4361,15 +4687,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,
@@ -4388,6 +4719,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. */
@@ -4396,51 +4739,140 @@ 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);
        }
 
     /* 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;
 }
 
-/*
- * 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;
        }
@@ -4448,73 +4880,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, useDataProtectionKeychainFlag;
+       OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx, useDataProtectionKeychainFlag);
+       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;
        }
@@ -4523,52 +4933,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, useDataProtectionKeychainFlag;
+       OSStatus status = SecItemCategorizeQuery(attributes, can_target_ios, can_target_osx, useDataProtectionKeychainFlag);
+       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;
        }
@@ -4576,130 +4984,116 @@ 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, 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 (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, useDataProtectionKeychainFlag;
+       OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx, useDataProtectionKeychainFlag);
+       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
@@ -4712,6 +5106,8 @@ SecItemCopyMatching_osx(
        else
                *result = NULL;
 
+    setCountLegacyAPIEnabledForThread(false);
+
        CFAllocatorRef allocator = CFGetAllocator(query);
        CFIndex matchCount = 0;
        CFMutableArrayRef itemArray = NULL;
@@ -4756,20 +5152,9 @@ error_exit:
        }
        _FreeSecItemParams(itemParams);
 
-       return status;
-}
+    setCountLegacyAPIEnabledForThread(true);
 
-OSStatus
-SecItemCopyDisplayNames(
-       CFArrayRef items,
-       CFArrayRef *displayNames)
-{
-    BEGIN_SECAPI
-       Required(items);
-       Required(displayNames);
-    //%%%TBI
-    return errSecUnimplemented;
-    END_SECAPI
+       return status;
 }
 
 OSStatus
@@ -4782,6 +5167,8 @@ SecItemAdd_osx(
        else if (result)
                *result = NULL;
 
+    setCountLegacyAPIEnabledForThread(false);
+
        CFAllocatorRef allocator = CFGetAllocator(attributes);
        CFMutableArrayRef itemArray = NULL;
        SecKeychainItemRef item = NULL;
@@ -4797,7 +5184,11 @@ SecItemAdd_osx(
        // but in any case it should try to add the certificate. See <rdar://8317887>.
        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,
@@ -4911,6 +5302,7 @@ error_exit:
                *result = NULL;
        }
        _FreeSecItemParams(itemParams);
+    setCountLegacyAPIEnabledForThread(true);
 
        return status;
 }
@@ -4925,7 +5317,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
 
@@ -4938,6 +5330,8 @@ SecItemUpdate_osx(
                CFRelease(results);
        }
 
+    setCountLegacyAPIEnabledForThread(false);
+
        OSStatus result = errSecSuccess;
        CFIndex ix, count = CFArrayGetCount(items);
        for (ix=0; ix < count; ix++) {
@@ -4948,6 +5342,8 @@ SecItemUpdate_osx(
                }
        }
 
+    setCountLegacyAPIEnabledForThread(true);
+
        if (items) {
                CFRelease(items);
        }
@@ -4976,6 +5372,8 @@ SecItemDelete_osx(
                CFRelease(results);
        }
 
+    setCountLegacyAPIEnabledForThread(false);
+
        OSStatus result = errSecSuccess;
        CFIndex ix, count = CFArrayGetCount(items);
        for (ix=0; ix < count; ix++) {
@@ -4991,6 +5389,8 @@ SecItemDelete_osx(
                }
        }
 
+    setCountLegacyAPIEnabledForThread(true);
+
        if (items)
                CFRelease(items);