]> git.saurik.com Git - apple/security.git/blobdiff - OSX/sec/Security/SecItem.c
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / sec / Security / SecItem.c
index 0a2e523c5b8b808463a2f21915576f193dbb9595..4f325ec90850b59d29bdf61967cc5e66263a5464 100644 (file)
 #include <utilities/SecXPCError.h>
 #include <utilities/der_plist.h>
 #include <utilities/der_plist_internal.h>
-#include <assert.h>
-#include <dlfcn.h>
+#include <utilities/simulatecrash_assert.h>
 #include <libaks_acl_cf_keys.h>
 #include <os/activity.h>
-#include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
 #include <pthread.h>
+#include <os/lock.h>
+#include <os/feature_private.h>
 
 #include <Security/SecInternal.h>
-#include "SOSInternal.h"
+#include "keychain/SecureObjectSync/SOSInternal.h"
 #include <TargetConditionals.h>
 #include <ipc/securityd_client.h>
 #include <Security/SecuritydXPC.h>
 
 #include <coreauthd_spi.h>
 #include <LocalAuthentication/LAPrivateDefines.h>
-#include <LocalAuthentication/LACFSupport.h>
 
-#include <ctkclient.h>
+#include <ctkclient/ctkclient.h>
+
+#include "SecItemRateLimit.h"
+
+/*
+ * See corresponding definition in SecDbKeychainItemV7. This is the unserialized
+ * maximum, so the daemon's limit is not exactly the same.
+ */
+#define REASONABLE_DATA_SIZE 4096
+
+const CFStringRef kSecNetworkExtensionAccessGroupSuffix = CFSTR("com.apple.networkextensionsharing");
 
 /* Return an OSStatus for a sqlite3 error code. */
 static OSStatus osstatus_for_s3e(int s3e)
 {
-       if (s3e > 0 && s3e <= SQLITE_DONE) switch (s3e)
+       switch (s3e)
        {
         case SQLITE_OK:
-            return 0;
-        case SQLITE_ERROR:
-            return errSecNotAvailable; /* errSecDuplicateItem; */
-        case SQLITE_FULL: /* Happens if we run out of uniqueids */
-            return errSecNotAvailable; /* TODO: Replace with a better error code. */
-        case SQLITE_PERM:
+        case SQLITE_DONE:
+            return errSecSuccess;
         case SQLITE_READONLY:
-            return errSecNotAvailable;
-        case SQLITE_CANTOPEN:
-            return errSecNotAvailable;
-        case SQLITE_EMPTY:
-            return errSecNotAvailable;
+            return errSecReadOnly;
         case SQLITE_CONSTRAINT:
             return errSecDuplicateItem;
-        case SQLITE_ABORT:
+        case SQLITE_ABORT:             // There is no errSecOperationCancelled
             return -1;
         case SQLITE_MISMATCH:
             return errSecNoSuchAttr;
-        case SQLITE_AUTH:
-            return errSecNotAvailable;
         case SQLITE_NOMEM:
-            return -2; /* TODO: Replace with a real error code. */
+            return errSecAllocate;
+        case SQLITE_IOERR:
+            return errSecIO;
         case SQLITE_INTERNAL:
+            return errSecInternalComponent;
+        case SQLITE_FULL:              // Happens if we run out of uniqueids or disk is full (TODO: replace with better code)
+        case SQLITE_PERM:              // No acess permission
+        case SQLITE_AUTH:              // No authorization (e.g. no class key for file)
+        case SQLITE_CANTOPEN:          // can be several reasons for this. Caller should sqlite3_system_errno()
+        case SQLITE_EMPTY:             // SQLite does not seem to use this. Was already here, so keeping
+        case SQLITE_ERROR:
         default:
-            return errSecNotAvailable; /* TODO: Replace with a real error code. */
+            return errSecNotAvailable;
        }
-    return s3e;
 }
 
 static OSStatus osstatus_for_kern_return(CFIndex kernResult)
@@ -189,24 +196,6 @@ static OSStatus osstatus_for_der_error(CFIndex derError) {
     }
 }
 
-static OSStatus osstatus_for_ids_error(CFIndex idsError) {
-    switch (idsError)
-    {
-        case kSecIDSErrorNoDeviceID:
-            return errSecDeviceIDNeeded;
-        case kSecIDSErrorNotRegistered:
-            return errSecIDSNotRegistered;
-        case kSecIDSErrorFailedToSend:
-            return errSecFailedToSendIDSMessage;
-        case kSecIDSErrorCouldNotFindMatchingAuthToken:
-            return errSecDeviceIDNoMatch;
-        case kSecIDSErrorNoPeersAvailable:
-            return errSecPeersNotAvailable;
-        default:
-            return errSecInternal;
-    }
-}
-
 static OSStatus osstatus_for_localauthentication_error(CFIndex laError) {
     // Wrap LA error in Sec error.
     switch (laError) {
@@ -231,6 +220,9 @@ static OSStatus osstatus_for_ctk_error(CFIndex ctkError) {
             return errSecUserCanceled;
         case kTKErrorCodeCorruptedData:
             return errSecDecode;
+        case kTKErrorCodeTokenNotFound:
+        case kTKErrorCodeObjectNotFound:
+            return errSecItemNotFound;
         default:
             return errSecInternal;
     }
@@ -259,8 +251,6 @@ OSStatus SecErrorGetOSStatus(CFErrorRef error) {
             status = osstatus_for_xpc_error(CFErrorGetCode(error));
         } else if (CFEqual(sSecDERErrorDomain, domain)) {
             status = osstatus_for_der_error(CFErrorGetCode(error));
-        } else if (CFEqual(kSecIDSErrorDomain, domain)) {
-            status = osstatus_for_ids_error(CFErrorGetCode(error));
         } else if (CFEqual(CFSTR(kLAErrorDomain), domain)) {
             status = osstatus_for_localauthentication_error(CFErrorGetCode(error));
         } else if (CFEqual(CFSTR(kTKErrorDomain), domain)) {
@@ -349,6 +339,25 @@ OSStatus SecOSStatusWith(bool (^perform)(CFErrorRef *error)) {
     return status;
 }
 
+static void
+logUnreasonableDataLength(CFDictionaryRef attributes)
+{
+    CFDataRef data;
+    CFIndex length;
+
+    if (isDictionary(attributes)) {
+        data = CFDictionaryGetValue(attributes, kSecValueData);
+        if (isData(data)) {
+            length = CFDataGetLength(data);
+            if (length > REASONABLE_DATA_SIZE) {
+                // This log message is vague, as we may not know anything else about the item.
+                // securityd logging (correlate by activity ID) will have more information.
+                secwarning("keychain item data exceeds reasonable size (%lu bytes)", (unsigned long)length);
+            }
+        }
+    }
+}
+
 /* Drop assorted kSecAttrCanXxxx attributes from the query, because these attributes are generated
    by SecKey implementation and may differ between OS versions, see <rdar://problem/27095761>.
  */
@@ -368,6 +377,7 @@ AttributeCreateFilteredOutSecAttrs(CFDictionaryRef attributes)
     CFDictionaryRemoveValue(filtered, kSecAttrCanUnwrap);
     CFDictionaryRemoveValue(filtered, kSecAttrCanSignRecover);
     CFDictionaryRemoveValue(filtered, kSecAttrCanVerifyRecover);
+    CFDictionaryRemoveValue(filtered, kSecAttrIsPermanent);
 
     return filtered;
 }
@@ -434,11 +444,16 @@ SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes) {
        } else if (CFEqual(class, kSecClassIdentity)) {
                CFDataRef data = CFDictionaryGetValue(refAttributes, kSecAttrIdentityCertificateData);
                SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, data);
-               SecKeyRef key = SecKeyCreateFromAttributeDictionary(refAttributes);
-               if (key && cert)
-                       ref = SecIdentityCreate(kCFAllocatorDefault, cert, key);
+        SecKeyRef key = SecKeyCreateFromAttributeDictionary(refAttributes);
+        if (key && cert) {
+            ref = SecIdentityCreate(kCFAllocatorDefault, cert, key);
+        }
+        else {
+            secerror("SecItem: failed to create identity");
+        }
+
+        CFReleaseSafe(key);
                CFReleaseSafe(cert);
-               CFReleaseSafe(key);
 #ifdef SECITEM_SHIM_OSX
        } else {
         ref = SecItemCreateFromAttributeDictionary_osx(refAttributes);
@@ -447,13 +462,6 @@ SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes) {
        return ref;
 }
 
-OSStatus
-SecItemCopyDisplayNames(CFArrayRef items, CFArrayRef *displayNames)
-{
-    // @@@ TBI
-    return -1 /* errSecUnimplemented */;
-}
-
 typedef OSStatus (*secitem_operation)(CFDictionaryRef attributes, CFTypeRef *result);
 
 static bool explode_identity(CFDictionaryRef attributes, secitem_operation operation, 
@@ -527,10 +535,12 @@ static bool explode_identity(CFDictionaryRef attributes, secitem_operation opera
                     if (result) {
                         if (!status) {
                             /* result is a persistent ref to a cert */
-                            sqlite_int64 rowid;
-                            if (_SecItemParsePersistentRef(result, NULL, &rowid)) {
-                                *return_result = _SecItemMakePersistentRef(kSecClassIdentity, rowid);
+                            sqlite_int64 rowid = -1;
+                            CFDictionaryRef tokenAttrs = NULL;
+                            if (_SecItemParsePersistentRef(result, NULL, &rowid, &tokenAttrs)) {
+                                *return_result = _SecItemCreatePersistentRef(kSecClassIdentity, rowid, tokenAttrs);
                             }
+                            CFReleaseNull(tokenAttrs);
                         }
                         CFRelease(result);
                     }
@@ -656,61 +666,139 @@ static void infer_cert_label(SecCFDictionaryCOW *attributes)
     }
 }
 
-/* A persistent ref is just the class and the rowid of the record. */
-CF_RETURNS_RETAINED CFDataRef _SecItemMakePersistentRef(CFTypeRef class, sqlite_int64 rowid)
+static CFDataRef CreateTokenPersistentRefData(CFTypeRef class, CFDictionaryRef attributes) {
+    CFDataRef tokenPersistentRef = NULL;
+    CFStringRef tokenID = NULL;
+    CFDataRef tokenData = NULL;
+    CFDataRef oid = NULL;
+    CFDictionaryRef itemValue = NULL;
+
+    oid = CFDictionaryGetValue(attributes, kSecAttrTokenOID);
+    if (oid != NULL) {
+        require_quiet(tokenID = CFCast(CFString, CFDictionaryGetValue(attributes, kSecAttrTokenID)), out);
+    } else {
+        // Identities are identified by their contained certificate, so we need to get tokenID and OID from certificate,
+        // not from the key.
+        if (CFEqual(class, kSecClassIdentity) && oid == NULL) {
+            require_quiet(tokenID = CFCast(CFString, CFDictionaryGetValue(attributes, kSecAttrIdentityCertificateTokenID)), out);
+            require_quiet(tokenData = CFCast(CFData, CFDictionaryGetValue(attributes, kSecAttrIdentityCertificateData)), out);
+        } else {
+            require_quiet(tokenID = CFCast(CFString, CFDictionaryGetValue(attributes, kSecAttrTokenID)), out);
+            require_quiet(tokenData = CFCast(CFData, CFDictionaryGetValue(attributes, kSecValueData)), out);
+        }
+        require_quiet(itemValue = SecTokenItemValueCopy(tokenData, NULL), out);
+        require_quiet(oid = CFDictionaryGetValue(itemValue, kSecTokenValueObjectIDKey), out);
+        require_quiet(CFCast(CFData, oid) != NULL, out);
+    }
+    CFArrayRef array = CFArrayCreateForCFTypes(kCFAllocatorDefault, class, tokenID, oid, NULL);
+    tokenPersistentRef = CFPropertyListCreateDERData(kCFAllocatorDefault, array, NULL);
+    CFRelease(array);
+out:
+    CFReleaseNull(itemValue);
+    return tokenPersistentRef;
+}
+
+static const uint8_t tk_persistent_ref_id[] = {'t', 'k', 'p', 'r'};
+/* A persistent ref is just the class and the rowid of the record.
+   Persistent ref for token items is a der blob with class, tokenID and objectId. */
+CFDataRef _SecItemCreatePersistentRef(CFTypeRef class, sqlite_int64 rowid, CFDictionaryRef attributes)
 {
-    uint8_t bytes[sizeof(sqlite_int64) + 4];
-    if (rowid < 0)
-        return NULL;
-    if (CFStringGetCString(class, (char *)bytes, 4 + 1 /*null-term*/, 
-        kCFStringEncodingUTF8))
-    {
-        OSWriteBigInt64(bytes + 4, 0, rowid);
-        return CFDataCreate(NULL, bytes, sizeof(bytes));
+    CFDataRef result = NULL;
+    CFDataRef tokenPersistentRef = NULL;
+    if (attributes != NULL) {
+        tokenPersistentRef = CreateTokenPersistentRefData(class, attributes);
+    }
+
+    if (tokenPersistentRef != NULL) {
+        CFMutableDataRef tmpData = CFDataCreateMutable(kCFAllocatorDefault, sizeof(tk_persistent_ref_id) + CFDataGetLength(tokenPersistentRef));
+        CFDataAppendBytes(tmpData, tk_persistent_ref_id, sizeof(tk_persistent_ref_id));
+        CFDataAppend(tmpData, tokenPersistentRef);
+        CFReleaseNull(tokenPersistentRef);
+        result = tmpData;
+    } else {
+        require(rowid >= 0, out);
+        uint8_t bytes[sizeof(sqlite_int64) + 4];
+        if (CFStringGetCString(class, (char *)bytes, 4 + 1 /*null-term*/,
+            kCFStringEncodingUTF8))
+        {
+            OSWriteBigInt64(bytes + 4, 0, rowid);
+            result = CFDataCreate(NULL, bytes, sizeof(bytes));
+        }
     }
-    return NULL;
+out:
+    return result;
+}
+
+static Boolean isValidClass(CFStringRef class, CFStringRef *return_class) {
+    const void *valid_classes[] = { kSecClassGenericPassword,
+        kSecClassInternetPassword,
+        kSecClassAppleSharePassword,
+        kSecClassCertificate,
+        kSecClassKey,
+        kSecClassIdentity };
+
+    for (size_t i = 0; i < array_size(valid_classes); i++) {
+        if (CFEqual(valid_classes[i], class)) {
+            if (return_class)
+                *return_class = valid_classes[i];
+            return true;
+        }
+    }
+    
+    return false;
+}
+
+static bool ParseTokenPersistentRefData(CFDataRef persistent_ref, CFStringRef *return_class, CFDictionaryRef *return_token_attrs) {
+    bool valid_ref = false;
+    CFPropertyListRef pl = NULL;
+    const uint8_t *der = CFDataGetBytePtr(persistent_ref) + sizeof(tk_persistent_ref_id);
+    const uint8_t *der_end = der + (CFDataGetLength(persistent_ref) - sizeof(tk_persistent_ref_id));
+    require_quiet(der = der_decode_plist(0, &pl, NULL, der, der_end), out);
+    require_quiet(der == der_end, out);
+    require_quiet(CFGetTypeID(pl) == CFArrayGetTypeID(), out);
+    require_quiet(CFArrayGetCount(pl) == 3, out);
+    require_quiet(valid_ref = isValidClass(CFArrayGetValueAtIndex(pl, 0), return_class), out);
+    if (return_token_attrs) {
+            *return_token_attrs = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+                                                               kSecAttrTokenID, CFArrayGetValueAtIndex(pl, 1),
+                                                               kSecAttrTokenOID, CFArrayGetValueAtIndex(pl, 2), NULL);
+    }
+out:
+    CFReleaseNull(pl);
+    return valid_ref;
 }
 
 /* AUDIT[securityd](done):
    persistent_ref (ok) is a caller provided, non NULL CFTypeRef.
  */
-bool _SecItemParsePersistentRef(CFDataRef persistent_ref, CFStringRef *return_class, sqlite_int64 *return_rowid)
+bool _SecItemParsePersistentRef(CFDataRef persistent_ref, CFStringRef *return_class, sqlite_int64 *return_rowid, CFDictionaryRef *return_token_attrs)
 {
        bool valid_ref = false;
-    if (CFGetTypeID(persistent_ref) == CFDataGetTypeID() &&
-        CFDataGetLength(persistent_ref) == (CFIndex)(sizeof(sqlite_int64) + 4)) {
+    require(CFGetTypeID(persistent_ref) == CFDataGetTypeID(), out);
+    
+    if (CFDataGetLength(persistent_ref) > (CFIndex)sizeof(tk_persistent_ref_id) &&
+        memcmp(tk_persistent_ref_id, CFDataGetBytePtr(persistent_ref), sizeof(tk_persistent_ref_id)) == 0) {
+        valid_ref = ParseTokenPersistentRefData(persistent_ref, return_class, return_token_attrs);
+    } else if (CFDataGetLength(persistent_ref) == (CFIndex)(sizeof(sqlite_int64) + 4)) {
         const uint8_t *bytes = CFDataGetBytePtr(persistent_ref);
         sqlite_int64 rowid = OSReadBigInt64(bytes + 4, 0);
                
         CFStringRef class = CFStringCreateWithBytes(kCFAllocatorDefault, 
             bytes, CFStringGetLength(kSecClassGenericPassword), 
             kCFStringEncodingUTF8, true);
-        const void *valid_classes[] = { kSecClassGenericPassword,
-            kSecClassInternetPassword,
-            kSecClassAppleSharePassword,
-            kSecClassCertificate,
-            kSecClassKey,
-            kSecClassIdentity };
         
-        unsigned i;
-        for (i=0; i< array_size(valid_classes); i++) {
-            if (CFEqual(valid_classes[i], class)) {
-                if (return_class)
-                    *return_class = valid_classes[i];
-                if (return_rowid)
-                    *return_rowid = rowid;
-                valid_ref = true;
-                break;
-            }
+        if ((valid_ref = isValidClass(class, return_class))) {
+            if (return_rowid)
+                *return_rowid = rowid;
         }
         CFRelease(class);
     }
+out:
     return valid_ref;
 }
 
-static bool cf_bool_value(CFTypeRef cf_bool)
-{
-       return (cf_bool && CFEqual(kCFBooleanTrue, cf_bool));
+static bool cf_bool_value(CFTypeRef cf_bool) {
+       return cf_bool && CFBooleanGetValue(cf_bool);
 }
 
 CFMutableDictionaryRef SecCFDictionaryCOWGetMutable(SecCFDictionaryCOW *cow_dictionary) {
@@ -727,11 +815,6 @@ CFMutableDictionaryRef SecCFDictionaryCOWGetMutable(SecCFDictionaryCOW *cow_dict
     return cow_dictionary->mutable_dictionary;
 }
 
-// Keys for dictionary of kSecvalueData of token-based items.
-static const CFStringRef kSecTokenValueObjectIDKey = CFSTR("oid");
-static const CFStringRef kSecTokenValueAccessControlKey = CFSTR("ac");
-static const CFStringRef kSecTokenValueDataKey = CFSTR("data");
-
 // Creates kSecValueData field stored in the DB for token-based items.  Data field consists of objectID, real
 // access_control and optionally of the data value.
 static CFDataRef SecTokenItemValueCreate(CFDataRef oid, CFDataRef access_control, CFDataRef object_value, CFErrorRef *error) {
@@ -749,36 +832,67 @@ static CFDataRef SecTokenItemValueCreate(CFDataRef oid, CFDataRef access_control
     return value_data;
 }
 
-static CFDictionaryRef SecTokenItemValueCopy(CFDataRef db_value, CFErrorRef *error) {
-    CFPropertyListRef plist = NULL;
+CFDictionaryRef SecTokenItemValueCopy(CFDataRef db_value, CFErrorRef *error) {
+    CFPropertyListRef plist = NULL, result = NULL;
+    require_quiet(CFCastWithError(CFData, db_value, error), out);
     const uint8_t *der = CFDataGetBytePtr(db_value);
     const uint8_t *der_end = der + CFDataGetLength(db_value);
-    require_quiet(der = der_decode_plist(0, kCFPropertyListImmutable, &plist, error, der, der_end), out);
+    require_quiet(der = der_decode_plist(0, &plist, error, der, der_end), out);
     require_action_quiet(der == der_end, out, SecError(errSecDecode, error, CFSTR("trailing garbage at end of token data field")));
-    require_action_quiet(CFDictionaryGetValue(plist, kSecTokenValueObjectIDKey) != NULL, out,
+    CFTypeRef value = CFDictionaryGetValue(plist, kSecTokenValueObjectIDKey);
+    require_action_quiet(CFCast(CFData, value) != NULL, out,
                          SecError(errSecInternal, error, CFSTR("token based item data does not have OID")));
-
-out:
-    return plist;
-}
-
-CFDataRef _SecTokenItemCopyValueData(CFDataRef db_value, CFErrorRef *error) {
-    CFDataRef valueData = NULL;
-    CFDictionaryRef itemDict = NULL;
-    require_quiet(itemDict = SecTokenItemValueCopy(db_value, error), out);
-    CFRetainAssign(valueData, CFDictionaryGetValue(itemDict, kSecTokenValueDataKey));
-    require_action_quiet(valueData, out, SecError(errSecInternal, error, CFSTR("token item does not contain value data")));
-
+    value = CFDictionaryGetValue(plist, kSecTokenValueAccessControlKey);
+    require_quiet(value == NULL || CFCastWithError(CFData, value, error), out);
+    value = CFDictionaryGetValue(plist, kSecTokenValueDataKey);
+    require_quiet(value == NULL || CFCastWithError(CFData, value, error), out);
+    result = CFRetainSafe(plist);
 out:
-    CFReleaseSafe(itemDict);
-    return valueData;
+    CFReleaseNull(plist);
+    return result;
 }
 
-TKTokenRef SecTokenCreate(CFStringRef token_id, CFDictionaryRef auth_params, CFErrorRef *error) {
+TKTokenRef SecTokenCreate(CFStringRef token_id, SecCFDictionaryCOW *auth_params, CFErrorRef *error) {
     CFMutableDictionaryRef token_attrs = NULL;
     TKTokenRef token = NULL;
-    token_attrs = (auth_params != NULL) ?
-        CFDictionaryCreateMutableCopy(NULL, 0, auth_params) :
+    
+    static CFMutableDictionaryRef sharedLAContexts = NULL;
+    static dispatch_once_t onceToken;
+    static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
+    if ((auth_params->dictionary == NULL || CFDictionaryGetValue(auth_params->dictionary, kSecUseCredentialReference) == NULL) && !CFStringHasPrefix(token_id, kSecAttrTokenIDSecureEnclave)) {
+        dispatch_once(&onceToken, ^{
+            sharedLAContexts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+        });
+
+        os_unfair_lock_lock(&lock);
+        CFTypeRef ctx = CFDictionaryGetValue(sharedLAContexts, token_id);
+        if (ctx == nil) {
+            ctx = LACreateNewContextWithACMContext(NULL, error);
+            if (!ctx) {
+                os_unfair_lock_unlock(&lock);
+                secerror("Failed to create authentication context %@", *error);
+                return token;
+            }
+            CFDictionarySetValue(sharedLAContexts, token_id, ctx);
+            CFRelease(ctx);
+            ctx = CFDictionaryGetValue(sharedLAContexts, token_id);
+        }
+
+        CFDataRef credRef = NULL;
+        if (ctx != nil) {
+            credRef = LACopyACMContext(ctx, NULL);
+        }
+
+        if (credRef) {
+            CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseAuthenticationContext, ctx);
+            CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseCredentialReference, credRef);
+            CFRelease(credRef);
+        }
+        os_unfair_lock_unlock(&lock);
+    }
+    
+    token_attrs = (auth_params->dictionary != NULL) ?
+        CFDictionaryCreateMutableCopy(NULL, 0, auth_params->dictionary) :
         CFDictionaryCreateMutableForCFTypes(NULL);
     CFDictionarySetValue(token_attrs, kSecAttrTokenID, token_id);
 
@@ -789,23 +903,25 @@ TKTokenRef SecTokenCreate(CFStringRef token_id, CFDictionaryRef auth_params, CFE
     return token;
 }
 
-static bool SecTokenItemCreateFromAttributes(CFDictionaryRef attributes, CFDictionaryRef auth_params,
+static bool SecTokenItemCreateFromAttributes(CFDictionaryRef attributes, CFDictionaryRef auth_params_dict,
                                              TKTokenRef token, CFDataRef object_id, CFTypeRef *ref, CFErrorRef *error) {
     bool ok = false;
+    SecCFDictionaryCOW auth_params = { auth_params_dict };
     CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, attributes);
     CFTypeRef token_id = CFDictionaryGetValue(attributes, kSecAttrTokenID);
     if (token_id != NULL && object_id != NULL) {
+        require_quiet(CFCastWithError(CFString, token_id, error), out);
         if (CFRetainSafe(token) == NULL) {
-            require_quiet(token = SecTokenCreate(token_id, auth_params, error), out);
+            require_quiet(token = SecTokenCreate(token_id, &auth_params, error), out);
         }
 
-        if (auth_params != NULL) {
-            CFDictionaryForEach(auth_params, ^(const void *key, const void *value) {
-                CFDictionarySetValue(attrs, key, value);
+        if (auth_params.dictionary != NULL) {
+            CFDictionaryForEach(auth_params.dictionary, ^(const void *key, const void *value) {
+                CFDictionaryAddValue(attrs, key, value);
             });
         }
         CFDictionarySetValue(attrs, kSecUseToken, token);
-        CFDictionarySetValue(attrs, kSecUseTokenObjectID, object_id);
+        CFDictionarySetValue(attrs, kSecAttrTokenOID, object_id);
         CFRelease(token);
     }
     *ref = SecItemCreateFromAttributeDictionary(attrs);
@@ -813,6 +929,7 @@ static bool SecTokenItemCreateFromAttributes(CFDictionaryRef attributes, CFDicti
 
 out:
     CFReleaseSafe(attrs);
+    CFReleaseSafe(auth_params.mutable_dictionary);
     return ok;
 }
 
@@ -820,7 +937,7 @@ out:
 /* Turn the returned single value or dictionary that contains all the attributes to create a
  ref into the exact result the client asked for */
 static bool SecItemResultCopyPrepared(CFTypeRef raw_result, TKTokenRef token,
-                                      CFDictionaryRef query, CFDictionaryRef auth_params,
+                                      CFDictionaryRef query, CFDictionaryRef auth_params_dict,
                                       CFTypeRef *result, CFErrorRef *error) {
     bool ok = false;
     CFDataRef ac_data = NULL;
@@ -833,6 +950,7 @@ static bool SecItemResultCopyPrepared(CFTypeRef raw_result, TKTokenRef token,
     CFDataRef cert_data = NULL;
     CFDataRef cert_object_id = NULL;
     TKTokenRef cert_token = NULL;
+    SecCFDictionaryCOW auth_params = { auth_params_dict };
 
     bool wants_ref = cf_bool_value(CFDictionaryGetValue(query, kSecReturnRef));
     bool wants_data = cf_bool_value(CFDictionaryGetValue(query, kSecReturnData));
@@ -845,9 +963,11 @@ static bool SecItemResultCopyPrepared(CFTypeRef raw_result, TKTokenRef token,
     if (token == NULL) {
         if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID()) {
             token_id = CFDictionaryGetValue(raw_result, kSecAttrTokenID);
+            require_quiet(token_id == NULL || CFCastWithError(CFString, token_id, error) != NULL, out);
             token_item = (token_id != NULL);
 
             cert_token_id = CFDictionaryGetValue(raw_result, kSecAttrIdentityCertificateTokenID);
+            require_quiet(cert_token_id == NULL || CFCastWithError(CFString, cert_token_id, error) != NULL, out);
             cert_token_item = (cert_token_id != NULL);
         }
     } else {
@@ -857,6 +977,11 @@ static bool SecItemResultCopyPrepared(CFTypeRef raw_result, TKTokenRef token,
         CFRetainAssign(cert_token, token);
     }
 
+    if ((token_item || cert_token_item) && cf_bool_value(CFDictionaryGetValue(query, kSecUseTokenRawItems))) {
+        token_item = false;
+        cert_token_item = false;
+    }
+
     // Decode and prepare data value, if it is requested at the output, or if we want attributes from token.
     if (wants_data || wants_ref || (token_item && wants_attributes)) {
         if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID())
@@ -869,13 +994,16 @@ static bool SecItemResultCopyPrepared(CFTypeRef raw_result, TKTokenRef token,
             CFDictionaryRef parsed_value = NULL;
             require_quiet(parsed_value = SecTokenItemValueCopy(value, error), out);
             object_id = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueObjectIDKey));
+            require_quiet(object_id == NULL || CFCastWithError(CFData, object_id, error) != NULL, out);
             ac_data = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueAccessControlKey));
+            require_quiet(ac_data == NULL || CFCastWithError(CFData, ac_data, error) != NULL, out);
             object_value = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueDataKey));
+            require_quiet(object_value == NULL || CFCastWithError(CFData, object_value, error) != NULL, out);
             CFRelease(parsed_value);
             if ((wants_data || wants_ref) && object_value == NULL) {
                 // Retrieve value directly from the token.
                 if (token == NULL) {
-                    require_quiet(token = SecTokenCreate(token_id, auth_params, error), out);
+                    require_quiet(token = SecTokenCreate(token_id, &auth_params, error), out);
                 }
                 require_quiet(object_value = TKTokenCopyObjectData(token, object_id, error), out);
                 if (CFEqual(object_value, kCFNull))
@@ -907,82 +1035,86 @@ static bool SecItemResultCopyPrepared(CFTypeRef raw_result, TKTokenRef token,
         }
     }
 
-    if (wants_ref || wants_attributes || (wants_data && wants_persistent_ref)) {
-        // For these cases we need output dictionary.
-        if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID())
-            *result = CFDictionaryCreateMutableCopy(NULL, 0, raw_result);
-        else
-            *result = CFDictionaryCreateForCFTypes(NULL, NULL);
-        CFMutableDictionaryRef output = (CFMutableDictionaryRef)*result;
-
-        if ((wants_data || wants_ref) && value != NULL)
-            CFDictionarySetValue(output, kSecValueData, value);
-        else
-            CFDictionaryRemoveValue(output, kSecValueData);
+    if (!wants_ref && !wants_attributes && (!wants_data || !wants_persistent_ref)) {
+        *result = NULL;
+        ok = true;
+        goto out;
+    }
 
-        if (wants_persistent_ref && persistent_ref != NULL)
-            CFDictionarySetValue(output, kSecValuePersistentRef, persistent_ref);
-        else
-            CFDictionaryRemoveValue(output, kSecValuePersistentRef);
-
-        if ((wants_ref || wants_attributes) && cert_token_item &&
-            CFEqualSafe(CFDictionaryGetValue(output, kSecClass), kSecClassIdentity)) {
-            // Decode also certdata field of the identity.
-            CFDataRef data = CFDictionaryGetValue(output, kSecAttrIdentityCertificateData);
-            if (data != NULL) {
-                CFDictionaryRef parsed_value;
-                require_quiet(parsed_value = SecTokenItemValueCopy(data, error), out);
-                cert_data = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueDataKey));
-                cert_object_id = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueObjectIDKey));
-                CFRelease(parsed_value);
-                if (cert_data == NULL) {
-                    // Retrieve value directly from the token.
-                    if (cert_token == NULL) {
-                        require_quiet(cert_token = SecTokenCreate(cert_token_id, auth_params, error), out);
-                    }
-                    require_quiet(cert_data = TKTokenCopyObjectData(cert_token, cert_object_id, error), out);
-                    if (CFEqual(cert_data, kCFNull))
-                        CFReleaseNull(cert_data);
-                }
-                if (cert_data != NULL) {
-                    CFDictionarySetValue(output, kSecAttrIdentityCertificateData, cert_data);
-                } else {
-                    CFDictionaryRemoveValue(output, kSecAttrIdentityCertificateData);
+    // For other cases we need an output dictionary.
+    if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID())
+        *result = CFDictionaryCreateMutableCopy(NULL, 0, raw_result);
+    else
+        *result = CFDictionaryCreateForCFTypes(NULL, NULL);
+    CFMutableDictionaryRef output = (CFMutableDictionaryRef)*result;
+
+    if ((wants_data || wants_ref) && value != NULL)
+        CFDictionarySetValue(output, kSecValueData, value);
+    else
+        CFDictionaryRemoveValue(output, kSecValueData);
+
+    if (wants_persistent_ref && persistent_ref != NULL)
+        CFDictionarySetValue(output, kSecValuePersistentRef, persistent_ref);
+    else
+        CFDictionaryRemoveValue(output, kSecValuePersistentRef);
+
+    if ((wants_ref || wants_attributes) && cert_token_item &&
+        CFEqualSafe(CFDictionaryGetValue(output, kSecClass), kSecClassIdentity)) {
+        // Decode also certdata field of the identity.
+        CFDataRef data = CFDictionaryGetValue(output, kSecAttrIdentityCertificateData);
+        if (data != NULL) {
+            CFDictionaryRef parsed_value;
+            require_quiet(parsed_value = SecTokenItemValueCopy(data, error), out);
+            cert_data = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueDataKey));
+            require_quiet(cert_data == NULL || CFCastWithError(CFData, cert_data, error) != NULL, out);
+            cert_object_id = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueObjectIDKey));
+            require_quiet(cert_object_id == NULL || CFCastWithError(CFData, cert_object_id, error) != NULL, out);
+            CFRelease(parsed_value);
+            if (cert_data == NULL) {
+                // Retrieve value directly from the token.
+                if (cert_token == NULL) {
+                    require_quiet(cert_token = SecTokenCreate(cert_token_id, &auth_params, error), out);
                 }
+                require_quiet(cert_data = TKTokenCopyObjectData(cert_token, cert_object_id, error), out);
+                if (CFEqual(cert_data, kCFNull))
+                    CFReleaseNull(cert_data);
+            }
+            if (cert_data != NULL) {
+                CFDictionarySetValue(output, kSecAttrIdentityCertificateData, cert_data);
+            } else {
+                CFDictionaryRemoveValue(output, kSecAttrIdentityCertificateData);
             }
         }
+    }
 
-        if (wants_ref) {
-            CFTypeRef ref;
-            require_quiet(SecTokenItemCreateFromAttributes(output, auth_params, token, object_id, &ref, error), out);
-            if (!(wants_attributes || wants_data || wants_persistent_ref)) {
-                CFAssignRetained(*result, ref);
-            } else if (ref != NULL) {
-                CFDictionarySetValue(output, kSecValueRef, ref);
-                CFRelease(ref);
-                if (!wants_data) {
-                    // We could have stored data value previously to make ref creation succeed.
-                    // They are not needed any more and in case that caller did not want the data, avoid returning them.
-                    CFDictionaryRemoveValue(output, kSecValueData);
-                }
-            }
+    if (wants_ref || wants_attributes) {
+        // Convert serialized form of access control to object form.
+        if (!token_item) {
+            CFRetainAssign(ac_data, CFDictionaryGetValue(output, kSecAttrAccessControl));
         }
 
-        if (wants_attributes) {
-            // Convert serialized form of access control to object form.
-            if (!token_item) {
-                CFRetainAssign(ac_data, CFDictionaryGetValue(output, kSecAttrAccessControl));
-            }
+        if (ac_data != NULL) {
+            SecAccessControlRef ac;
+            require_quiet(ac = SecAccessControlCreateFromData(kCFAllocatorDefault, ac_data, error), out);
+            CFDictionarySetValue(output, kSecAttrAccessControl, ac);
+            CFRelease(ac);
+        }
+    }
 
-            if (ac_data != NULL) {
-                SecAccessControlRef ac;
-                require_quiet(ac = SecAccessControlCreateFromData(kCFAllocatorDefault, ac_data, error), out);
-                CFDictionarySetValue(output, kSecAttrAccessControl, ac);
-                CFRelease(ac);
+    if (wants_ref) {
+        CFTypeRef ref;
+        require_quiet(SecTokenItemCreateFromAttributes(output, auth_params.dictionary, token, object_id, &ref, error), out);
+        if (!(wants_attributes || wants_data || wants_persistent_ref)) {
+            CFAssignRetained(*result, ref);
+        } else if (ref != NULL) {
+            CFDictionarySetValue(output, kSecValueRef, ref);
+            CFRelease(ref);
+            if (!wants_data) {
+                // We could have stored data value previously to make ref creation succeed.
+                // They are not needed any more and in case that caller did not want the data, avoid returning them.
+                CFDictionaryRemoveValue(output, kSecValueData);
             }
         }
-    } else {
-        *result = NULL;
     }
 
     ok = true;
@@ -997,11 +1129,12 @@ out:
     CFReleaseSafe(attrs);
     CFReleaseSafe(token);
     CFReleaseSafe(cert_token);
+    CFReleaseSafe(auth_params.mutable_dictionary);
     return ok;
 }
 
-static bool SecItemResultProcess(CFDictionaryRef query, CFDictionaryRef auth_params, TKTokenRef token,
-                                 CFTypeRef raw_result, CFTypeRef *result, CFErrorRef *error) {
+bool SecItemResultProcess(CFDictionaryRef query, CFDictionaryRef auth_params, TKTokenRef token,
+                          CFTypeRef raw_result, CFTypeRef *result, CFErrorRef *error) {
     bool ok = false;
     require_action_quiet(raw_result != NULL, out, ok = true);
     require_action_quiet(result != NULL, out, ok = true);
@@ -1011,9 +1144,16 @@ static bool SecItemResultProcess(CFDictionaryRef query, CFDictionaryRef auth_par
         *result = CFArrayCreateMutableForCFTypes(NULL);
         for (i = 0; i < count; i++) {
             CFTypeRef ref;
-            require_quiet(SecItemResultCopyPrepared(CFArrayGetValueAtIndex(raw_result, i),
-                                                    token, query, auth_params, &ref, error), out);
-            if (ref != NULL) {
+            CFErrorRef localError = NULL;
+            bool prepared = SecItemResultCopyPrepared(CFArrayGetValueAtIndex(raw_result, i),
+                                                      token, query, auth_params, &ref, &localError);
+            if (!prepared) {
+                // TokenNotFound or TokenObjectNotFound will just not insert failing item into resulting array, other errors abort processing.
+                require_action_quiet(localError != NULL && CFEqual(CFErrorGetDomain(localError), CFSTR(kTKErrorDomain)) &&
+                                     (CFErrorGetCode(localError) == kTKErrorCodeTokenNotFound || CFErrorGetCode(localError) == kTKErrorCodeObjectNotFound), out,
+                                     CFErrorPropagate(localError, error));
+                CFReleaseNull(localError);
+            } else if (ref != NULL) {
                 CFArrayAppendValue((CFMutableArrayRef)*result, ref);
                 CFRelease(ref);
             }
@@ -1028,31 +1168,43 @@ out:
     return ok;
 }
 
-CFDataRef SecItemAttributesCopyPreparedAuthContext(CFTypeRef la_context, CFErrorRef *error) {
-    void *la_lib = NULL;
-    CFDataRef acm_context = NULL;
-    require_action_quiet(la_lib = dlopen("/System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication", RTLD_LAZY), out,
-                         SecError(errSecInternal, error, CFSTR("failed to open LocalAuthentication.framework")));
-    LAFunctionCopyExternalizedContext fnCopyExternalizedContext = NULL;
-    require_action_quiet(fnCopyExternalizedContext = dlsym(la_lib, "LACopyExternalizedContext"), out,
-                         SecError(errSecInternal, error, CFSTR("failed to obtain LACopyExternalizedContext")));
-    require_action_quiet(acm_context = fnCopyExternalizedContext(la_context), out,
-                         SecError(errSecInternal, error, CFSTR("failed to get ACM handle from LAContext")));
-out:
-    if (la_lib != NULL) {
-        dlclose(la_lib);
-    }
-    return acm_context;
-}
-
 static bool SecItemAttributesPrepare(SecCFDictionaryCOW *attrs, bool forQuery, CFErrorRef *error) {
     bool ok = false;
     CFDataRef ac_data = NULL, acm_context = NULL;
-    void *la_lib = NULL;
+
+    // If a ref was specified we get its attribute dictionary and parse it.
+    CFTypeRef value = CFDictionaryGetValue(attrs->dictionary, kSecValueRef);
+    if (value) {
+        CFDictionaryRef ref_attributes;
+        require_action_quiet(ref_attributes = SecItemCopyAttributeDictionary(value, forQuery), out,
+                             SecError(errSecValueRefUnsupported, error, CFSTR("unsupported kSecValueRef in query")));
+
+        // Replace any attributes we already got from the ref with the ones from the attributes dictionary the caller passed us.
+        // This allows a caller to add an item using attributes from the ref and still override some of them in the dictionary directly.
+        CFDictionaryForEach(ref_attributes, ^(const void *key, const void *value) {
+            // Attributes already present in 'attrs' have precedence over the generic ones retrieved from the ref,
+            // so add only those attributes from 'ref' which are missing in attrs.
+            CFDictionaryAddValue(SecCFDictionaryCOWGetMutable(attrs), key, value);
+        });
+        CFRelease(ref_attributes);
+
+        if (forQuery) {
+            CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs), kSecAttrTokenOID);
+        }
+
+        // Remove original expanded valueRef. Do not remove it in case when adding token item, because it is needed later to avoid
+        // another roundtrip to token driver.
+        if (forQuery || !CFDictionaryContainsKey(attrs->dictionary, kSecAttrTokenID)) {
+            CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs), kSecValueRef);
+        }
+    }
 
     SecAccessControlRef access_control = (SecAccessControlRef)CFDictionaryGetValue(attrs->dictionary, kSecAttrAccessControl);
     if (access_control != NULL) {
-        require_quiet(ac_data = SecAccessControlCopyData(access_control), out);
+        require_action_quiet(CFGetTypeID(access_control) == SecAccessControlGetTypeID(), out,
+                             SecError(errSecParam, error, CFSTR("Unexpected type of kSecAttrAccessControl attribute")));
+        require_action_quiet(ac_data = SecAccessControlCopyData(access_control), out,
+                             SecError(errSecParam, error, CFSTR("unsupported kSecAttrAccessControl in query")));
         CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecAttrAccessControl, ac_data);
     }
 
@@ -1060,32 +1212,11 @@ static bool SecItemAttributesPrepare(SecCFDictionaryCOW *attrs, bool forQuery, C
     if (la_context) {
         require_action_quiet(!CFDictionaryContainsKey(attrs->dictionary, kSecUseCredentialReference), out,
                              SecError(errSecParam, error, CFSTR("kSecUseAuthenticationContext cannot be used together with kSecUseCredentialReference")));
-        require_quiet(acm_context = SecItemAttributesCopyPreparedAuthContext(la_context, error), out);
+        require_quiet(acm_context = LACopyACMContext(la_context, error), out);
         CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseAuthenticationContext);
         CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseCredentialReference, acm_context);
     }
 
-    // If a ref was specified we get its attribute dictionary and parse it.
-    CFTypeRef value = CFDictionaryGetValue(attrs->dictionary, kSecValueRef);
-    if (value) {
-        CFDictionaryRef ref_attributes;
-        require_action_quiet(ref_attributes = SecItemCopyAttributeDictionary(value, forQuery), out,
-                             SecError(errSecValueRefUnsupported, error, CFSTR("unsupported kSecValueRef in query")));
-
-        /* Replace any attributes we already got from the ref with the ones
-         from the attributes dictionary the caller passed us.  This allows
-         a caller to add an item using attributes from the ref and still
-         override some of them in the dictionary directly.  */
-        CFMutableDictionaryRef new_query = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, ref_attributes);
-        CFRelease(ref_attributes);
-        CFDictionaryForEach(attrs->dictionary, ^(const void *key, const void *value) {
-            if (!CFEqual(key, kSecValueRef))
-                CFDictionarySetValue(new_query, key, value);
-        });
-        CFAssignRetained(attrs->mutable_dictionary, new_query);
-        attrs->dictionary = attrs->mutable_dictionary;
-    }
-
     CFTypeRef policy = CFDictionaryGetValue(attrs->dictionary, kSecMatchPolicy);
     if (policy) {
         require_action_quiet(CFGetTypeID(policy) == SecPolicyGetTypeID(), out,
@@ -1109,11 +1240,11 @@ static bool SecItemAttributesPrepare(SecCFDictionaryCOW *attrs, bool forQuery, C
     value = CFDictionaryGetValue(attrs->dictionary, kSecAttrIssuer);
     if (value) {
         /* convert DN to canonical issuer, if value is DN (top level sequence) */
-        const DERItem name = { (unsigned char *)CFDataGetBytePtr(value), CFDataGetLength(value) };
+        CFDataRef issuer;
+        require_quiet(issuer = CFCastWithError(CFData, value, error), out);
+        const DERItem name = { (unsigned char *)CFDataGetBytePtr(issuer), CFDataGetLength(issuer) };
         DERDecodedInfo content;
-        if (!DERDecodeItem(&name, &content) &&
-            (content.tag == ASN1_CONSTR_SEQUENCE))
-        {
+        if (DERDecodeItem(&name, &content) == DR_Success && content.tag == ASN1_CONSTR_SEQUENCE) {
             CFDataRef canonical_issuer = createNormalizedX501Name(kCFAllocatorDefault, &content.content);
             if (canonical_issuer) {
                 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecAttrIssuer, canonical_issuer);
@@ -1122,12 +1253,14 @@ static bool SecItemAttributesPrepare(SecCFDictionaryCOW *attrs, bool forQuery, C
         }
     }
 
+    if (CFDictionaryContainsKey(attrs->dictionary, kSecUseTokenRawItems)) {
+        // This use flag is client-only, securityd does not understand it.
+        CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseTokenRawItems);
+    }
+
     ok = true;
 
 out:
-    if (la_lib != NULL) {
-        dlclose(la_lib);
-    }
     CFReleaseSafe(ac_data);
     CFReleaseSafe(acm_context);
     return ok;
@@ -1146,43 +1279,22 @@ static bool SecItemAuthMaxAttemptsReached(CFArrayRef ac_pairs, CFErrorRef *error
     }
 
     CFStringRef reason = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("Reached maximum count of authentication attempts\n %@"), log_string);
-    SecError(errSecAuthFailed, error, reason);
+    SecError(errSecAuthFailed, error, CFSTR("%@"), reason);
     __security_simulatecrash(reason, __sec_exception_code_AuthLoop);
-
     CFRelease(reason);
     CFRelease(log_string);
     return false;
 }
 
-bool SecItemAuthDo(SecCFDictionaryCOW *auth_params, CFErrorRef *error, SecItemAuthResult (^perform)(CFDictionaryRef auth_params, CFArrayRef *ac_pairs, CFErrorRef *error)) {
+bool SecItemAuthDo(SecCFDictionaryCOW *auth_params, CFErrorRef *error, SecItemAuthResult (^perform)(CFArrayRef *ac_pairs, CFErrorRef *error),
+                   void (^newCredentialRefAdded)(void)) {
     bool ok = false;
     CFArrayRef ac_pairs = NULL;
     SecCFDictionaryCOW auth_options = { NULL };
-    // We need to create shared LAContext for Mail to reduce popups with Auth UI.
-    // This app-hack will be removed by:<rdar://problem/28305552>
-    // Similar workaround is for Safari, will be removed by fixing <rdar://problem/29683072>
-    static CFTypeRef sharedLAContext = NULL;
-    static CFDataRef sharedACMContext = NULL;
-    static dispatch_once_t onceToken;
-    dispatch_once(&onceToken, ^{
-        CFBundleRef bundle = CFBundleGetMainBundle();
-        CFStringRef bundleName = (bundle != NULL) ? CFBundleGetIdentifier(bundle) : NULL;
-        if (CFEqualSafe(bundleName, CFSTR("com.apple.mail")) ||
-            CFEqualSafe(bundleName, CFSTR("com.apple.WebKit.Networking"))) {
-            sharedLAContext = LACreateNewContextWithACMContext(NULL, error);
-            sharedACMContext = (sharedLAContext != NULL) ? LACopyACMContext(sharedLAContext, error) : NULL;
-        }
-    });
-    if (sharedLAContext && sharedACMContext &&
-        (auth_params->dictionary == NULL || (CFDictionaryGetValue(auth_params->dictionary, kSecUseAuthenticationContext) == NULL &&
-                                             CFDictionaryGetValue(auth_params->dictionary, kSecUseCredentialReference) == NULL))) {
-        CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseAuthenticationContext, sharedLAContext);
-        CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseCredentialReference, sharedACMContext);
-    }
 
     for (uint32_t i = 0;; ++i) {
         // If the operation succeeded or failed with other than auth-needed error, just leave.
-        SecItemAuthResult auth_result = perform(auth_params->dictionary, &ac_pairs, error);
+        SecItemAuthResult auth_result = perform(&ac_pairs, error);
         require_quiet(auth_result != kSecItemAuthResultError, out);
         require_action_quiet(auth_result == kSecItemAuthResultNeedAuth, out, ok = true);
 
@@ -1200,6 +1312,9 @@ bool SecItemAuthDo(SecCFDictionaryCOW *auth_params, CFErrorRef *error, SecItemAu
                 require_quiet(acm_context = LACopyACMContext(auth_handle, error), out);
                 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseCredentialReference, acm_context);
                 CFRelease(acm_context);
+                if (newCredentialRefAdded) {
+                    newCredentialRefAdded();
+                }
             }
         }
 
@@ -1316,16 +1431,18 @@ static SecItemAuthResult SecItemCreatePairsFromError(CFErrorRef *error, CFArrayR
 }
 
 // Wrapper to handle automatic authentication and token/secd case switching.
-static bool SecItemAuthDoQuery(SecCFDictionaryCOW *query, SecCFDictionaryCOW *attributes, const void *secItemOperation, CFErrorRef *error,
+bool SecItemAuthDoQuery(SecCFDictionaryCOW *query, SecCFDictionaryCOW *attributes, const void *secItemOperation, CFErrorRef *error,
                                    bool (^perform)(TKTokenRef token, CFDictionaryRef query, CFDictionaryRef attributes, CFDictionaryRef auth_params, CFErrorRef *error)) {
     bool ok = false;
-    SecCFDictionaryCOW auth_params = { NULL };
-    SecAccessControlRef access_control = NULL;
+    __block SecCFDictionaryCOW auth_params = { NULL };
     __block TKTokenRef token = NULL;
 
+    CFDictionaryRef dict = attributes ? attributes->dictionary : query->dictionary;
+    SecAccessControlRef access_control = (SecAccessControlRef)CFDictionaryGetValue(dict, kSecAttrAccessControl);
+    require_action_quiet(access_control == NULL || CFGetTypeID(access_control) == SecAccessControlGetTypeID(), out,
+                         SecError(errSecParam, error, CFSTR("Unexpected type of kSecAttrAccessControl attribute")));
+
     if (secItemOperation == SecItemAdd || secItemOperation == SecItemUpdate) {
-        CFDictionaryRef dict = attributes ? attributes->dictionary : query->dictionary;
-        access_control = (SecAccessControlRef)CFDictionaryGetValue(dict, kSecAttrAccessControl);
         if (access_control && SecAccessControlGetConstraints(access_control) &&
             CFEqualSafe(CFDictionaryGetValue(dict, kSecAttrSynchronizable), kCFBooleanTrue))
             require_quiet(SecError(errSecParam, error, CFSTR("item with kSecAttrAccessControl is not synchronizable")), out);
@@ -1352,17 +1469,17 @@ static bool SecItemAuthDoQuery(SecCFDictionaryCOW *query, SecCFDictionaryCOW *at
                                       CFSTR("kSecUseAuthenticationUISkip is allowed only for SecItemCopyMatching")));
     }
 
-    ok = SecItemAuthDo(&auth_params, error, ^SecItemAuthResult(CFDictionaryRef auth_params, CFArrayRef *ac_pairs, CFErrorRef *error) {
+    ok = SecItemAuthDo(&auth_params, error, ^SecItemAuthResult(CFArrayRef *ac_pairs, CFErrorRef *error) {
         SecItemAuthResult result = kSecItemAuthResultError;
 
         // Propagate actual credential reference to the query.
-        if (auth_params != NULL) {
-            CFDataRef acm_context = CFDictionaryGetValue(auth_params, kSecUseCredentialReference);
+        if (auth_params.dictionary != NULL) {
+            CFDataRef acm_context = CFDictionaryGetValue(auth_params.dictionary, kSecUseCredentialReference);
             if (acm_context != NULL) {
                 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query), kSecUseCredentialReference, acm_context);
             }
 
-            CFDataRef acl_data_ref = CFDictionaryGetValue(auth_params, kSecAttrAccessControl);
+            CFDataRef acl_data_ref = CFDictionaryGetValue(auth_params.dictionary, kSecAttrAccessControl);
             if (acl_data_ref != NULL) {
                 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes ?: query), kSecAttrAccessControl, acl_data_ref);
             }
@@ -1370,20 +1487,36 @@ static bool SecItemAuthDoQuery(SecCFDictionaryCOW *query, SecCFDictionaryCOW *at
 
         // Prepare connection to target token if it is present.
         CFStringRef token_id = CFDictionaryGetValue(query->dictionary, kSecAttrTokenID);
-        if (secItemOperation != SecItemCopyMatching && token_id != NULL) {
-            require_quiet(CFAssignRetained(token, SecTokenCreate(token_id, auth_params, error)), out);
+        require_quiet(token_id == NULL || CFCastWithError(CFString, token_id, error) != NULL, out);
+        if (secItemOperation != SecItemCopyMatching && token_id != NULL && !cf_bool_value(CFDictionaryGetValue(query->dictionary, kSecUseTokenRawItems))) {
+            CFErrorRef localError = NULL;
+            CFAssignRetained(token, SecTokenCreate(token_id, &auth_params, &localError));
+            if (token == NULL) {
+                require_action_quiet(secItemOperation == SecItemDelete &&
+                                     CFEqual(CFErrorGetDomain(localError), CFSTR(kTKErrorDomain)) &&
+                                     CFErrorGetCode(localError) == kTKErrorCodeTokenNotFound,
+                                     out, CFErrorPropagate(localError, error));
+
+                // In case that token cannot be found and deletion is required, just continue and delete item from keychain only.
+                CFReleaseNull(localError);
+            }
         }
 
         CFDictionaryRef attrs = (attributes != NULL) ? attributes->dictionary : NULL;
-        if(!perform(token, query->dictionary, attrs, auth_params, error)) {
-            require_quiet((result = SecItemCreatePairsFromError(error, ac_pairs)) == kSecItemAuthResultOK, out);
+        if(perform(token, query->dictionary, attrs, auth_params.dictionary, error)) {
+            result = kSecItemAuthResultOK;
+            if(error && *error) {
+                // <rdar://problem/60642633> SecItemAuthDoQuery perform() sometimes returns success and fills in error parameter
+                secdebug("SecItemAuthDoQuery", "perform() succeded but returned an error: %@", *error);
+                CFReleaseNull(*error);
+            }
+        } else {
+            result = SecItemCreatePairsFromError(error, ac_pairs);
         }
 
-        result = kSecItemAuthResultOK;
-
     out:
         return result;
-    });
+    }, NULL);
     require_quiet(ok, out);
 
     ok = true;
@@ -1428,16 +1561,21 @@ static bool dict_to_error_request(enum SecXPCOperation op, CFDictionaryRef query
     }, NULL);
 }
 
-static bool cfstring_array_to_error_request(enum SecXPCOperation op, CFStringRef string, CFArrayRef attributes, __unused SecurityClient *client, CFErrorRef *error)
+static bool cfstring_array_array_to_error_request(enum SecXPCOperation op, CFStringRef string, CFArrayRef groups, CFArrayRef items, __unused SecurityClient *client, CFErrorRef *error)
 {
     return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
         if (string) {
             if (!SecXPCDictionarySetString(message, kSecXPCKeyString, string, error))
                 return false;
         }
+
+        if (groups) {
+            if (!SecXPCDictionarySetPList(message, kSecXPCKeyArray, groups, error))
+                return false;
+        }
         
-        if (attributes) {
-            if (!SecXPCDictionarySetPList(message, kSecXPCKeyQuery, attributes, error))
+        if (items) {
+            if (!SecXPCDictionarySetPList(message, kSecXPCKeyQuery, items, error))
                 return false;
         }
         
@@ -1464,13 +1602,11 @@ static bool SecTokenCreateAccessControlError(CFStringRef operation, CFDataRef ac
 
 static bool SecTokenProcessError(CFStringRef operation, TKTokenRef token, CFTypeRef object_or_attrs, CFErrorRef *error) {
     if (CFEqualSafe(CFErrorGetDomain(*error), CFSTR(kTKErrorDomain)) &&
-        CFErrorGetCode(*error) == kTKErrorCodeAuthenticationFailed) {
+        CFErrorGetCode(*error) == kTKErrorCodeAuthenticationNeeded) {
         // Replace error with the one which is augmented with access control and operation which failed,
         // which will cause SecItemDoWithAuth to throw UI.
         // Create array containing tuple (array) with error and requested operation.
-        CFDataRef access_control = (CFGetTypeID(object_or_attrs) == CFDataGetTypeID()) ?
-            TKTokenCopyObjectAccessControl(token, object_or_attrs, error) :
-            TKTokenCopyObjectCreationAccessControl(token, object_or_attrs, error);
+        CFDataRef access_control = TKTokenCopyObjectAccessControl(token, object_or_attrs, error);
         if (access_control != NULL) {
             SecTokenCreateAccessControlError(operation, access_control, error);
             CFRelease(access_control);
@@ -1480,8 +1616,9 @@ static bool SecTokenProcessError(CFStringRef operation, TKTokenRef token, CFType
 }
 
 static CFTypeRef SecTokenCopyUpdatedObjectID(TKTokenRef token, CFDataRef object_id, CFMutableDictionaryRef attributes, CFErrorRef *error) {
-    CFDataRef access_control = NULL, db_value = NULL, new_object_id = NULL;
+    CFDataRef access_control = NULL, db_value = NULL, new_object_id = NULL, result = NULL;
     SecAccessControlRef ac = NULL;
+    CFDictionaryRef old_attrs = NULL;
 
     // Make sure that ACL is bound - if not, generate an error which will trigger binding.
     CFDataRef ac_data = CFDictionaryGetValue(attributes, kSecAttrAccessControl);
@@ -1492,8 +1629,14 @@ static CFTypeRef SecTokenCopyUpdatedObjectID(TKTokenRef token, CFDataRef object_
     }
 
     // Create or update the object on the token.
+    old_attrs = CFDictionaryCreateCopy(kCFAllocatorDefault, attributes);
     require_action_quiet(new_object_id = TKTokenCreateOrUpdateObject(token, object_id, attributes, error), out,
                          SecTokenProcessError(kAKSKeyOpEncrypt, token, object_id ?: (CFTypeRef)attributes, error));
+    CFDictionaryForEach(old_attrs, ^(const void *key, const void *value) {
+        if (!CFEqual(key, kSecValueData)) {
+            CFDictionaryAddValue(attributes, key, value);
+        }
+    });
 
     // Prepare kSecValueData field for the item to be stored into the keychain DB.
     require_quiet(access_control = TKTokenCopyObjectAccessControl(token, new_object_id, error), out);
@@ -1503,12 +1646,15 @@ static CFTypeRef SecTokenCopyUpdatedObjectID(TKTokenRef token, CFDataRef object_
 
     // kSecAttrAccessControl is handled directly by the token and stored inside data field.
     CFDictionaryRemoveValue(attributes, kSecAttrAccessControl);
+    CFRetainAssign(result, new_object_id);
 
 out:
     CFReleaseSafe(ac);
     CFReleaseSafe(access_control);
     CFReleaseSafe(db_value);
-    return new_object_id;
+    CFReleaseSafe(old_attrs);
+    CFReleaseSafe(new_object_id);
+    return result;
 }
 
 static bool SecTokenItemAdd(TKTokenRef token, CFDictionaryRef attributes, CFDictionaryRef auth_params,
@@ -1517,20 +1663,30 @@ static bool SecTokenItemAdd(TKTokenRef token, CFDictionaryRef attributes, CFDict
     CFTypeRef object_id = NULL, ref = NULL;
     CFDictionaryRef ref_attrs = NULL;
     CFTypeRef db_result = NULL;
-
+    CFDataRef db_value = NULL;
     CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, attributes);
-    require_quiet(object_id = SecTokenCopyUpdatedObjectID(token, NULL, attrs, error), out);
-
-    // Augment attributes from default attributes of the related ref (SecKeyRef, SecCertificateRef).  This is best done
-    // by creating ref and getting back its attributes.
-    require_quiet(SecTokenItemCreateFromAttributes(attrs, auth_params, token, object_id, &ref, error), out);
-    if (ref != NULL) {
-        if ((ref_attrs = SecItemCopyAttributeDictionary(ref, false)) != NULL) {
-            CFDictionaryForEach(ref_attrs, ^(const void *key, const void *value) {
-                if (!CFEqual(key, kSecValueData)) {
-                    CFDictionaryAddValue(attrs, key, value);
-                }
-            });
+
+    CFDictionarySetValue(attrs, kSecAttrAccessible, kSecAttrAccessibleAlwaysPrivate);     //token items should be accesible always because have own ACL encoded in OID
+    object_id = CFRetainSafe(CFDictionaryGetValue(attrs, kSecAttrTokenOID));
+    CFDictionaryRemoveValue(attrs, kSecAttrTokenOID);
+    require_quiet(CFAssignRetained(object_id, SecTokenCopyUpdatedObjectID(token, object_id, attrs, error)), out);
+    CFDictionaryRemoveValue(attrs, kSecAttrTokenOID);
+    if (CFDictionaryContainsKey(attrs, kSecValueRef)) {
+        // All attributes already had been extracted from valueRef, so do not go through that step again, just remove
+        // the ref from the dictionary since it is of no use any more.
+        CFDictionaryRemoveValue(attrs, kSecValueRef);
+    } else {
+        // Augment attributes from default attributes of the related ref (SecKeyRef, SecCertificateRef).  This is best done
+        // by creating ref and getting back its attributes.
+        require_quiet(SecTokenItemCreateFromAttributes(attrs, auth_params, token, object_id, &ref, error), out);
+        if (ref != NULL) {
+            if ((ref_attrs = SecItemCopyAttributeDictionary(ref, false)) != NULL) {
+                CFDictionaryForEach(ref_attrs, ^(const void *key, const void *value) {
+                    if (!CFEqual(key, kSecValueData)) {
+                        CFDictionaryAddValue(attrs, key, value);
+                    }
+                });
+            }
         }
     }
 
@@ -1547,11 +1703,11 @@ static bool SecTokenItemAdd(TKTokenRef token, CFDictionaryRef attributes, CFDict
         db_result = CFRetain(attrs);
     }
     require_quiet(SecItemResultProcess(attributes, auth_params, token, db_result, result, error), out);
-
     ok = true;
 
 out:
     CFReleaseSafe(db_result);
+    CFReleaseSafe(db_value);
     CFReleaseSafe(attrs);
     CFReleaseSafe(ref_attrs);
     CFReleaseSafe(object_id);
@@ -1559,12 +1715,24 @@ out:
     return ok;
 }
 
+static void countReadOnlyAPICall() {
+    if (!isReadOnlyAPIRateWithinLimits()) {
+    }
+}
+
+static void countModifyingAPICall() {
+    if (!isModifyingAPIRateWithinLimits()) {
+    }
+}
+
 OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) {
     __block SecCFDictionaryCOW attrs = { attributes };
     OSStatus status;
 
-    os_activity_t trace_activity = os_activity_start("SecItemAdd_ios", OS_ACTIVITY_FLAG_DEFAULT);
-    
+    os_activity_t activity = os_activity_create("SecItemAdd_ios", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
+    os_activity_scope(activity);
+    os_release(activity);
+
     require_quiet(!explode_identity(attrs.dictionary, (secitem_operation)SecItemAdd, &status, result), errOut);
        infer_cert_label(&attrs);
 
@@ -1572,8 +1740,11 @@ OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) {
         return SecItemAuthDoQuery(&attrs, NULL, SecItemAdd, error, ^bool(TKTokenRef token, CFDictionaryRef attributes, CFDictionaryRef unused, CFDictionaryRef auth_params, CFErrorRef *error) {
             if (token == NULL) {
                 CFTypeRef raw_result = NULL;
-                if (!SECURITYD_XPC(sec_item_add, cftype_client_to_bool_cftype_error_request, attributes, SecSecurityClientGet(), &raw_result, error))
+                logUnreasonableDataLength(attributes);
+                countModifyingAPICall();
+                if (!SECURITYD_XPC(sec_item_add, cftype_client_to_bool_cftype_error_request, attributes, SecSecurityClientGet(), &raw_result, error)) {
                     return false;
+                }
 
                 bool ok = SecItemResultProcess(attributes, auth_params, token, raw_result, result, error);
                 CFReleaseSafe(raw_result);
@@ -1587,8 +1758,8 @@ OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) {
 
 errOut:
     CFReleaseSafe(attrs.mutable_dictionary);
+    secdebug("secitem", "SecItemAdd returned: %d", (int)status);
 
-    os_activity_end(trace_activity);
        return status;
 }
 
@@ -1597,24 +1768,27 @@ OSStatus SecItemCopyMatching(CFDictionaryRef inQuery, CFTypeRef *result) {
     OSStatus status;
     __block SecCFDictionaryCOW query = { inQuery };
 
-    os_activity_t trace_activity = os_activity_start("SecItemCopyMatching_ios", OS_ACTIVITY_FLAG_DEFAULT);
-    
+    os_activity_t activity = os_activity_create("SecItemCopyMatching_ios", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
+    os_activity_scope(activity);
+    os_release(activity);
+
     require_quiet(!explode_identity(query.dictionary, (secitem_operation)SecItemCopyMatching, &status, result), errOut);
 
     bool wants_data = cf_bool_value(CFDictionaryGetValue(query.dictionary, kSecReturnData));
     bool wants_attributes = cf_bool_value(CFDictionaryGetValue(query.dictionary, kSecReturnAttributes));
-    if ((wants_data && !wants_attributes) || (!wants_data && wants_attributes)) {
+    if ((wants_data && !wants_attributes)) {
         // When either attributes or data are requested, we need to query both, because for token based items,
         // both are needed in order to generate proper data and/or attributes results.
         CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query), kSecReturnAttributes, kCFBooleanTrue);
-        CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query), kSecReturnData, kCFBooleanTrue);
     }
 
     status = SecOSStatusWith(^bool(CFErrorRef *error) {
         return SecItemAuthDoQuery(&query, NULL, SecItemCopyMatching, error, ^bool(TKTokenRef token, CFDictionaryRef query, CFDictionaryRef attributes, CFDictionaryRef auth_params, CFErrorRef *error) {
             CFTypeRef raw_result = NULL;
-            if (!SECURITYD_XPC(sec_item_copy_matching, cftype_client_to_bool_cftype_error_request, query, SecSecurityClientGet(), &raw_result, error))
+            countReadOnlyAPICall();
+            if (!SECURITYD_XPC(sec_item_copy_matching, cftype_client_to_bool_cftype_error_request, query, SecSecurityClientGet(), &raw_result, error)) {
                 return false;
+            }
 
             // We intentionally pass NULL as token argument, because we want to be able to decide about token on which the item lives
             // on per-record basis, not wholesale.  Logic inside SecItemResultCopyPrepared will open proper token according
@@ -1626,9 +1800,8 @@ OSStatus SecItemCopyMatching(CFDictionaryRef inQuery, CFTypeRef *result) {
     });
 
 errOut:
-
+    secdebug("secitem", "SecItemCopyMatching_ios returned: %d", (int)status);
     CFReleaseSafe(query.mutable_dictionary);
-    os_activity_end(trace_activity);
     return status;
 }
 
@@ -1697,6 +1870,7 @@ static bool SecItemRawUpdate(CFDictionaryRef query, CFDictionaryRef attributesTo
         if (message) {
             if (SecXPCDictionarySetPList(message, kSecXPCKeyQuery, query, error) &&
                 SecXPCDictionarySetPList(message, kSecXPCKeyAttributesToUpdate, attributesToUpdate, error)) {
+                logUnreasonableDataLength(attributesToUpdate);
                 xpc_object_t reply = securityd_message_with_reply_sync(message, error);
                 if (reply) {
                     ok = securityd_message_no_error(reply, error);
@@ -1734,6 +1908,10 @@ static bool SecTokenItemUpdate(TKTokenRef token, CFDictionaryRef query, CFDictio
 }
 
 OSStatus SecItemUpdate(CFDictionaryRef inQuery, CFDictionaryRef inAttributesToUpdate) {
+    os_activity_t activity = os_activity_create("SecItemUpdate_ios", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
+    os_activity_scope(activity);
+    os_release(activity);
+
     return SecOSStatusWith(^bool(CFErrorRef *error) {
         return SecItemUpdateWithError(inQuery, inAttributesToUpdate, error);
     });
@@ -1752,6 +1930,7 @@ SecItemUpdateWithError(CFDictionaryRef inQuery,
         goto errOut;
 
     result = SecItemAuthDoQuery(&query, &attributesToUpdate, SecItemUpdate, error, ^bool(TKTokenRef token, CFDictionaryRef query, CFDictionaryRef attributes, CFDictionaryRef auth_params, CFErrorRef *error) {
+        countModifyingAPICall();
         if (token == NULL) {
                 return SecItemRawUpdate(query, attributes, error);
         } else {
@@ -1762,6 +1941,7 @@ SecItemUpdateWithError(CFDictionaryRef inQuery,
 errOut:
     CFReleaseSafe(query.mutable_dictionary);
     CFReleaseSafe(attributesToUpdate.mutable_dictionary);
+    secdebug("secitem", "SecItemUpdateWithError returned: %d", (int)result);
     return result;
 }
 
@@ -1770,7 +1950,7 @@ static OSStatus explode_persistent_identity_ref(SecCFDictionaryCOW *query)
     OSStatus status = errSecSuccess;
     CFTypeRef persist = CFDictionaryGetValue(query->dictionary, kSecValuePersistentRef);
     CFStringRef class;
-    if (persist && _SecItemParsePersistentRef(persist, &class, NULL)
+    if (persist && _SecItemParsePersistentRef(persist, &class, NULL, NULL)
         && CFEqual(class, kSecClassIdentity)) {
         const void *keys[] = { kSecReturnRef, kSecValuePersistentRef };
         const void *vals[] = { kCFBooleanTrue, persist };
@@ -1781,6 +1961,8 @@ static OSStatus explode_persistent_identity_ref(SecCFDictionaryCOW *query)
         CFReleaseNull(persistent_query);
         if (status)
             return status;
+        if (item_query == NULL)
+            return errSecItemNotFound;
 
         CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecValuePersistentRef);
         CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query), kSecValueRef, item_query);
@@ -1794,13 +1976,16 @@ OSStatus SecItemDelete(CFDictionaryRef inQuery) {
     OSStatus status;
     __block SecCFDictionaryCOW query = { inQuery };
 
-    os_activity_t trace_activity = os_activity_start("SecItemDelete_ios", OS_ACTIVITY_FLAG_DEFAULT);
-        
+    os_activity_t activity = os_activity_create("SecItemDelete_ios", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
+    os_activity_scope(activity);
+    os_release(activity);
+
     require_noerr_quiet(status = explode_persistent_identity_ref(&query), errOut);
     require_quiet(!explode_identity(query.dictionary, (secitem_operation)SecItemDelete, &status, NULL), errOut);
 
     status = SecOSStatusWith(^bool(CFErrorRef *error) {
         return SecItemAuthDoQuery(&query, NULL, SecItemDelete, error, ^bool(TKTokenRef token, CFDictionaryRef query, CFDictionaryRef attributes, CFDictionaryRef auth_params, CFErrorRef *error) {
+            countModifyingAPICall();
             if (token == NULL) {
                 return SECURITYD_XPC(sec_item_delete, dict_client_to_error_request, query, SecSecurityClientGet(), error);
             } else {
@@ -1809,8 +1994,14 @@ OSStatus SecItemDelete(CFDictionaryRef inQuery) {
 
                     // Delete item from the token.
                     CFDataRef object_id = CFDictionaryGetValue(object_data, kSecTokenValueObjectIDKey);
-                    require_action_quiet(TKTokenDeleteObject(token, object_id, error), out,
-                                         SecTokenProcessError(kAKSKeyOpDelete, token, object_id, error));
+                    CFErrorRef localError = NULL;
+                    if (!TKTokenDeleteObject(token, object_id, &localError)) {
+                        // Check whether object was not found; in this case, ignore the error.
+                        require_action_quiet(CFEqual(CFErrorGetDomain(localError), CFSTR(kTKErrorDomain)) &&
+                                             CFErrorGetCode(localError) == kTKErrorCodeObjectNotFound, out,
+                                             (CFErrorPropagate(localError, error), SecTokenProcessError(kAKSKeyOpDelete, token, object_id, error)));
+                        CFReleaseNull(localError);
+                    }
 
                     // Delete the item from the keychain.
                     require_quiet(SECURITYD_XPC(sec_item_delete, dict_client_to_error_request, item_query,
@@ -1826,9 +2017,8 @@ OSStatus SecItemDelete(CFDictionaryRef inQuery) {
 
 errOut:
     CFReleaseSafe(query.mutable_dictionary);
-    
-    os_activity_end(trace_activity);
-    
+    secdebug("secitem", "SecItemDelete returned: %d", (int)status);
+
        return status;
 }
 
@@ -1838,11 +2028,6 @@ SecItemDeleteAll(void)
     return SecOSStatusWith(^bool (CFErrorRef *error) {
         bool ok = true;
         if (gSecurityd) {
-#ifndef SECITEM_SHIM_OSX
-            SecTrustStoreRef ts = SecTrustStoreForDomain(kSecTrustStoreDomainUser);
-            if (!gSecurityd->sec_truststore_remove_all(ts, error))
-                ok &= SecError(errSecInternal, error, CFSTR("sec_truststore_remove_all is NULL"));
-#endif // *** END SECITEM_SHIM_OSX ***
             if (!gSecurityd->sec_item_delete_all(error))
                 ok &= SecError(errSecInternal, error, CFSTR("sec_item_delete_all is NULL"));
         } else {
@@ -1852,72 +2037,52 @@ SecItemDeleteAll(void)
     });
 }
 
-static bool
-agrps_client_to_error_request(enum SecXPCOperation op, CFArrayRef agrps, __unused SecurityClient *client, CFErrorRef *error)
-{
-    return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
-        return SecXPCDictionarySetPList(message, kSecXPCKeyAccessGroups, agrps, error);
-    }, NULL);
-}
-
 bool SecItemDeleteAllWithAccessGroups(CFArrayRef accessGroups, CFErrorRef *error) {
-    os_activity_t trace_activity = os_activity_start("SecItemDeleteAllWithAccessGroups", OS_ACTIVITY_FLAG_DEFAULT);
-
-    bool ok = SECURITYD_XPC(sec_delete_items_with_access_groups, agrps_client_to_error_request, accessGroups,
-                            SecSecurityClientGet(), error);
-
-    os_activity_end(trace_activity);
-    return ok;
+    return true;
 }
 
 OSStatus
-#if SECITEM_SHIM_OSX
-SecItemUpdateTokenItems_ios(CFTypeRef tokenID, CFArrayRef tokenItemsAttributes)
-#else
-SecItemUpdateTokenItems(CFTypeRef tokenID, CFArrayRef tokenItemsAttributes)
-#endif
+SecItemUpdateTokenItemsForAccessGroups(CFTypeRef tokenID, CFArrayRef accessGroups, CFArrayRef tokenItemsAttributes)
 {
     OSStatus status;
 
-    os_activity_t trace_activity = os_activity_start("SecItemDelete_ios", OS_ACTIVITY_FLAG_DEFAULT);
+    os_activity_t activity = os_activity_create("SecItemUpdateTokenItemsForAccessGroups", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
+    os_activity_scope(activity);
+    os_release(activity);
 
     status = SecOSStatusWith(^bool(CFErrorRef *error) {
-        CFArrayRef tmpArrayRef = tokenItemsAttributes;
+        bool ok = false;
+        CFMutableArrayRef tokenItemsForServer = NULL;
         if (tokenItemsAttributes) {
-            CFMutableArrayRef tokenItems = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+            tokenItemsForServer = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
             for (CFIndex i = 0; i < CFArrayGetCount(tokenItemsAttributes); ++i) {
                 CFDictionaryRef itemAttributes = CFArrayGetValueAtIndex(tokenItemsAttributes, i);
                 CFTypeRef accessControl = CFDictionaryGetValue(itemAttributes, kSecAttrAccessControl);
                 CFTypeRef tokenOID = CFDictionaryGetValue(itemAttributes, kSecAttrTokenOID);
                 CFTypeRef valueData = CFDictionaryGetValue(itemAttributes, kSecValueData);
                 if (tokenOID != NULL && accessControl != NULL && CFDataGetTypeID() == CFGetTypeID(accessControl)) {
-                    CFDataRef data = SecTokenItemValueCreate(tokenOID, accessControl, valueData, error);
-                    if (!data) {
-                        CFRelease(tokenItems);
-                        return false;
-                    }
-                    
+                    CFDataRef data;
+                    require_quiet(data = SecTokenItemValueCreate(tokenOID, accessControl, valueData, error), out);
                     CFMutableDictionaryRef attributes = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, itemAttributes);
                     CFDictionarySetValue(attributes, kSecValueData, data);
                     CFDictionarySetValue(attributes, kSecAttrTokenID, tokenID);
                     CFDictionaryRemoveValue(attributes, kSecAttrAccessControl);
                     CFDictionaryRemoveValue(attributes, kSecAttrTokenOID);
-                    CFArrayAppendValue(tokenItems, attributes);
-                    CFRelease(attributes);
-                    CFRelease(data);
+                    CFArrayAppendValue(tokenItemsForServer, attributes);
+                    CFReleaseNull(attributes);
+                    CFReleaseNull(data);
+                } else {
+                    CFArrayAppendValue(tokenItemsForServer, itemAttributes);
                 }
-                else
-                    CFArrayAppendValue(tokenItems, itemAttributes);
             }
-            
-            tmpArrayRef = tokenItems;
         }
 
-        return SECURITYD_XPC(sec_item_update_token_items, cfstring_array_to_error_request, tokenID, tmpArrayRef, SecSecurityClientGet(), error);
+        ok = SECURITYD_XPC(sec_item_update_token_items_for_access_groups, cfstring_array_array_to_error_request, tokenID, accessGroups, tokenItemsForServer, SecSecurityClientGet(), error);
+    out:
+        CFReleaseNull(tokenItemsForServer);
+        return ok;
     });
 
-    os_activity_end(trace_activity);
-
     return status;
 }
 
@@ -1929,20 +2094,6 @@ CFArrayRef _SecKeychainSyncUpdateMessage(CFDictionaryRef updates, CFErrorRef *er
     return result;
 }
 
-#ifndef SECITEM_SHIM_OSX
-OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement);
-
-OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement)
-{
-    return -1; /* this is only on OS X currently */
-}
-
-#else
-
-extern OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement);
-
-#endif
-
 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
 
 bool _SecKeychainRollKeys(bool force, CFErrorRef *error)
@@ -1964,4 +2115,52 @@ bool _SecKeychainRollKeys(bool force, CFErrorRef *error)
     return result;
 }
 
+static CFArrayRef data_array_to_array_error_request(enum SecXPCOperation op, CFDataRef normalizedIssuer, CFArrayRef accessGroups, CFErrorRef *error) {
+    __block CFArrayRef results = NULL;
+    securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
+        SecXPCDictionarySetData(message, kSecXPCKeyNormalizedIssuer, normalizedIssuer, error);
+        SecXPCDictionarySetPList(message, kSecXPCKeyAccessGroups, accessGroups, error);
+        return true;
+    }, ^bool(xpc_object_t response, CFErrorRef *error) {
+        return SecXPCDictionaryCopyArrayOptional(response, kSecXPCKeyResult, &results, error);
+    });
+    return results;
+}
 
+static bool data_data_array_to_bool_error_request(enum SecXPCOperation op, CFDataRef normalizedIssuer, CFDataRef serialNumber, CFArrayRef accessGroups, CFErrorRef *error) {
+    __block bool result = false;
+    securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
+        SecXPCDictionarySetData(message, kSecXPCKeyNormalizedIssuer, normalizedIssuer, error);
+        SecXPCDictionarySetData(message, kSecXPCKeySerialNumber, serialNumber, error);
+        SecXPCDictionarySetPList(message, kSecXPCKeyAccessGroups, accessGroups, error);
+        return true;
+    }, ^bool(xpc_object_t response, CFErrorRef *error) {
+        result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
+        return result;
+    });
+    return result;
+}
+
+CFArrayRef SecItemCopyParentCertificates_ios(CFDataRef normalizedIssuer, CFArrayRef accessGroups, CFErrorRef *error) {
+    CFArrayRef results = NULL;
+
+    os_activity_t activity = os_activity_create("SecItemCopyParentCertificates_ios", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
+    os_activity_scope(activity);
+    os_release(activity);
+
+    results = SECURITYD_XPC(sec_item_copy_parent_certificates, data_array_to_array_error_request, normalizedIssuer, accessGroups, error);
+
+    return results;
+}
+
+bool SecItemCertificateExists(CFDataRef normalizedIssuer, CFDataRef serialNumber, CFArrayRef accessGroups, CFErrorRef *error) {
+    bool results = false;
+
+    os_activity_t activity = os_activity_create("SecItemCertificateExists", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
+    os_activity_scope(activity);
+    os_release(activity);
+
+    results = SECURITYD_XPC(sec_item_certificate_exists, data_data_array_to_bool_error_request, normalizedIssuer, serialNumber, accessGroups, error);
+
+    return results;
+}