--- /dev/null
+/*
+ * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * SecItem.c - CoreFoundation-based constants and functions for
+ access to Security items (certificates, keys, identities, and
+ passwords.)
+ */
+
+#include <Security/SecBasePriv.h>
+#include <Security/SecItem.h>
+#include <Security/SecItemPriv.h>
+#include <Security/SecItemInternal.h>
+#include <Security/SecAccessControl.h>
+#include <Security/SecAccessControlPriv.h>
+#ifndef SECITEM_SHIM_OSX
+#include <Security/SecKey.h>
+#include <Security/SecKeyPriv.h>
+#include <Security/SecCertificateInternal.h>
+#include <Security/SecIdentity.h>
+#include <Security/SecIdentityPriv.h>
+#include <Security/SecRandom.h>
+#include <Security/SecBasePriv.h>
+#endif // *** END SECITEM_SHIM_OSX ***
+#include <Security/SecCTKKeyPriv.h>
+#include <Security/SecTask.h>
+#include <errno.h>
+#include <limits.h>
+#include <sqlite3.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <Security/SecBase.h>
+#include <CoreFoundation/CFData.h>
+#include <CoreFoundation/CFDate.h>
+#include <CoreFoundation/CFDictionary.h>
+#include <CoreFoundation/CFNumber.h>
+#include <CoreFoundation/CFString.h>
+#include <CoreFoundation/CFURL.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <libkern/OSByteOrder.h>
+#include <corecrypto/ccder.h>
+#include <utilities/array_size.h>
+#include <utilities/debugging.h>
+#include <utilities/SecCFError.h>
+#include <utilities/SecCFWrappers.h>
+#include <utilities/SecIOFormat.h>
+#include <utilities/SecXPCError.h>
+#include <utilities/der_plist.h>
+#include <utilities/der_plist_internal.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include <libaks_acl_cf_keys.h>
+#include <os/activity.h>
+#include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
+
+#include <Security/SecInternal.h>
+#include <TargetConditionals.h>
+#include <ipc/securityd_client.h>
+#include <Security/SecuritydXPC.h>
+#include <AssertMacros.h>
+#include <asl.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#ifndef SECITEM_SHIM_OSX
+#include <libDER/asn1Types.h>
+#endif // *** END SECITEM_SHIM_OSX ***
+
+/* label when certificate data is joined with key data */
+#define CERTIFICATE_DATA_COLUMN_LABEL "certdata"
+
+#include <utilities/SecDb.h>
+#include <IOKit/IOReturn.h>
+
+#include <coreauthd_spi.h>
+#include <LocalAuthentication/LAPrivateDefines.h>
+#include <LocalAuthentication/LACFSupport.h>
+
+#include <ctkclient.h>
+
+/* Return an OSStatus for a sqlite3 error code. */
+static OSStatus osstatus_for_s3e(int s3e)
+{
+ if (s3e > 0 && s3e <= SQLITE_DONE) 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_READONLY:
+ return errSecNotAvailable;
+ case SQLITE_CANTOPEN:
+ return errSecNotAvailable;
+ case SQLITE_EMPTY:
+ return errSecNotAvailable;
+ case SQLITE_CONSTRAINT:
+ return errSecDuplicateItem;
+ case SQLITE_ABORT:
+ return -1;
+ case SQLITE_MISMATCH:
+ return errSecNoSuchAttr;
+ case SQLITE_AUTH:
+ return errSecNotAvailable;
+ case SQLITE_NOMEM:
+ return -2; /* TODO: Replace with a real error code. */
+ case SQLITE_INTERNAL:
+ default:
+ return errSecNotAvailable; /* TODO: Replace with a real error code. */
+ }
+ return s3e;
+}
+
+static OSStatus osstatus_for_kern_return(CFIndex kernResult)
+{
+ switch (kernResult)
+ {
+ case KERN_SUCCESS:
+ return errSecSuccess;
+ case kIOReturnNotReadable:
+ case kIOReturnNotWritable:
+ return errSecAuthFailed;
+ case kIOReturnNotPermitted:
+ case kIOReturnNotPrivileged:
+ case kIOReturnLockedRead:
+ case kIOReturnLockedWrite:
+ return errSecInteractionNotAllowed;
+ case kIOReturnError:
+ return errSecDecode;
+ case kIOReturnBadArgument:
+ return errSecParam;
+ default:
+ return errSecNotAvailable; /* TODO: Replace with a real error code. */
+ }
+}
+
+static OSStatus osstatus_for_xpc_error(CFIndex xpcError) {
+ switch (xpcError)
+ {
+ case kSecXPCErrorSuccess:
+ return errSecSuccess;
+ case kSecXPCErrorUnexpectedType:
+ case kSecXPCErrorUnexpectedNull:
+ return errSecParam;
+ case kSecXPCErrorConnectionFailed:
+ return errSecNotAvailable;
+ case kSecXPCErrorUnknown:
+ default:
+ return errSecInternal;
+ }
+}
+
+static OSStatus osstatus_for_der_error(CFIndex derError) {
+ switch (derError)
+ {
+ case kSecDERErrorUnknownEncoding:
+ case kSecDERErrorUnsupportedDERType:
+ case kSecDERErrorUnsupportedNumberType:
+ return errSecDecode;
+ case kSecDERErrorUnsupportedCFObject:
+ return errSecParam;
+ case kSecDERErrorAllocationFailure:
+ return errSecAllocate;
+ default:
+ return errSecInternal;
+ }
+}
+
+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;
+ default:
+ return errSecInternal;
+ }
+}
+
+static OSStatus osstatus_for_localauthentication_error(CFIndex laError) {
+ // Wrap LA error in Sec error.
+ switch (laError) {
+ case kLAErrorUserCancel:
+ return errSecUserCanceled;
+ case kLAErrorParameter:
+ return errSecParam;
+ case kLAErrorNotInteractive:
+ return errSecInteractionNotAllowed;
+ default:
+ return errSecAuthFailed;
+ }
+}
+
+static OSStatus osstatus_for_ctk_error(CFIndex ctkError) {
+ // Hack, get rid of it once dep lands: <rdar://problem/21181736> Export error code constants from ctkclient.h header
+#ifndef kTKErrorCodeNotImplemented
+#define kTKErrorCodeNotImplemented -1
+#endif
+#ifndef kTKErrorCodeCanceledByUser
+#define kTKErrorCodeCanceledByUser -4
+#endif
+
+ switch (ctkError) {
+ case kTKErrorCodeBadParameter:
+ return errSecParam;
+ case kTKErrorCodeNotImplemented:
+ return errSecUnimplemented;
+ case kTKErrorCodeCanceledByUser:
+ return errSecUserCanceled;
+ default:
+ return errSecInternal;
+ }
+}
+
+// Convert from securityd error codes to OSStatus for legacy API.
+OSStatus SecErrorGetOSStatus(CFErrorRef error) {
+ OSStatus status;
+ if (error == NULL) {
+ status = errSecSuccess;
+ } else {
+ CFStringRef domain = CFErrorGetDomain(error);
+ if (domain == NULL) {
+ secerror("No error domain for error: %@", error);
+ status = errSecInternal;
+ } else if (CFEqual(kSecErrorDomain, domain)) {
+ status = (OSStatus)CFErrorGetCode(error);
+ } else if (CFEqual(kSecDbErrorDomain, domain)) {
+ status = osstatus_for_s3e((int)CFErrorGetCode(error));
+ } else if (CFEqual(kSecErrnoDomain, domain)) {
+ status = (OSStatus)CFErrorGetCode(error);
+ } else if (CFEqual(kSecKernDomain, domain)) {
+ status = osstatus_for_kern_return(CFErrorGetCode(error));
+ } else if (CFEqual(sSecXPCErrorDomain, domain)) {
+ 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)) {
+ status = osstatus_for_ctk_error(CFErrorGetCode(error));
+ } else {
+ secnotice("securityd", "unknown error domain: %@ for error: %@", domain, error);
+ status = errSecInternal;
+ }
+ }
+ return status;
+}
+
+// Wrapper to provide a CFErrorRef for legacy API.
+OSStatus SecOSStatusWith(bool (^perform)(CFErrorRef *error)) {
+ CFErrorRef error = NULL;
+ OSStatus status;
+ if (perform(&error)) {
+ assert(error == NULL);
+ status = errSecSuccess;
+ } else {
+ assert(error);
+ status = SecErrorGetOSStatus(error);
+ if (status != errSecItemNotFound) // Occurs in normal operation, so exclude
+ secerror("error:[%" PRIdOSStatus "] %@", status, error);
+ CFReleaseNull(error);
+ }
+ return status;
+}
+
+/* IPC uses CFPropertyList to un/marshall input/output data and can handle:
+ CFData, CFString, CFArray, CFDictionary, CFDate, CFBoolean, and CFNumber
+
+ Currently in need of conversion below:
+ @@@ kSecValueRef allows SecKeychainItemRef and SecIdentityRef
+ @@@ kSecMatchPolicy allows a query with a SecPolicyRef
+ @@@ kSecUseItemList allows a query against a list of itemrefs, this isn't
+ currently implemented at all, but when it is needs to short circuit to
+ local evaluation, different from the sql query abilities
+*/
+
+#ifndef SECITEM_SHIM_OSX
+static CFDictionaryRef
+SecItemCopyAttributeDictionary(CFTypeRef ref) {
+ CFDictionaryRef refDictionary = NULL;
+ CFTypeID typeID = CFGetTypeID(ref);
+ if (typeID == SecKeyGetTypeID()) {
+ refDictionary = SecKeyCopyAttributeDictionary((SecKeyRef)ref);
+ } else if (typeID == SecCertificateGetTypeID()) {
+ refDictionary =
+ SecCertificateCopyAttributeDictionary((SecCertificateRef)ref);
+ } else if (typeID == SecIdentityGetTypeID()) {
+ assert(false);
+ SecIdentityRef identity = (SecIdentityRef)ref;
+ SecCertificateRef cert = NULL;
+ SecKeyRef key = NULL;
+ if (!SecIdentityCopyCertificate(identity, &cert) &&
+ !SecIdentityCopyPrivateKey(identity, &key))
+ {
+ CFDataRef data = SecCertificateCopyData(cert);
+ CFDictionaryRef key_dict = SecKeyCopyAttributeDictionary(key);
+
+ if (key_dict && data) {
+ refDictionary = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, key_dict);
+ CFDictionarySetValue((CFMutableDictionaryRef)refDictionary,
+ CFSTR(CERTIFICATE_DATA_COLUMN_LABEL), data);
+ }
+ CFReleaseNull(key_dict);
+ CFReleaseNull(data);
+ }
+ CFReleaseNull(cert);
+ CFReleaseNull(key);
+ } else {
+ refDictionary = NULL;
+ }
+ return refDictionary;
+}
+
+static CFTypeRef
+SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes) {
+ CFTypeRef ref = NULL;
+ CFStringRef class = CFDictionaryGetValue(refAttributes, kSecClass);
+ if (CFEqual(class, kSecClassCertificate)) {
+ ref = SecCertificateCreateFromAttributeDictionary(refAttributes);
+ } else if (CFEqual(class, kSecClassKey)) {
+ ref = SecKeyCreateFromAttributeDictionary(refAttributes);
+ } else if (CFEqual(class, kSecClassIdentity)) {
+ CFAllocatorRef allocator = NULL;
+ CFDataRef data = CFDictionaryGetValue(refAttributes, CFSTR(CERTIFICATE_DATA_COLUMN_LABEL));
+ SecCertificateRef cert = SecCertificateCreateWithData(allocator, data);
+ SecKeyRef key = SecKeyCreateFromAttributeDictionary(refAttributes);
+ if (key && cert)
+ ref = SecIdentityCreate(allocator, cert, key);
+ CFReleaseSafe(cert);
+ CFReleaseSafe(key);
+#if 0
+ /* We don't support SecKeychainItemRefs yet. */
+ } else if (CFEqual(class, kSecClassGenericPassword)) {
+ } else if (CFEqual(class, kSecClassInternetPassword)) {
+ } else if (CFEqual(class, kSecClassAppleSharePassword)) {
+#endif
+ } else {
+ ref = NULL;
+ }
+ return ref;
+}
+#else
+
+extern CFTypeRef SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes);
+
+#endif
+
+OSStatus
+SecItemCopyDisplayNames(CFArrayRef items, CFArrayRef *displayNames)
+{
+ // @@@ TBI
+ return -1 /* errSecUnimplemented */;
+}
+
+#ifndef SECITEM_SHIM_OSX
+typedef OSStatus (*secitem_operation)(CFDictionaryRef attributes, CFTypeRef *result);
+
+static bool explode_identity(CFDictionaryRef attributes, secitem_operation operation,
+ OSStatus *return_status, CFTypeRef *return_result)
+{
+ bool handled = false;
+ CFTypeRef value = CFDictionaryGetValue(attributes, kSecValueRef);
+ if (value) {
+ CFTypeID typeID = CFGetTypeID(value);
+ if (typeID == SecIdentityGetTypeID()) {
+ handled = true;
+ OSStatus status = errSecSuccess;
+ SecIdentityRef identity = (SecIdentityRef)value;
+ SecCertificateRef cert = NULL;
+ SecKeyRef key = NULL;
+ if (!SecIdentityCopyCertificate(identity, &cert) &&
+ !SecIdentityCopyPrivateKey(identity, &key))
+ {
+ CFMutableDictionaryRef partial_query =
+ CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, attributes);
+ CFDictionarySetValue(partial_query, kSecValueRef, cert);
+ CFTypeRef result = NULL;
+ bool duplicate_cert = false;
+ /* an identity is first and foremost a key, but it can have multiple
+ certs associated with it: so we identify it by the cert */
+ status = operation(partial_query, return_result ? &result : NULL);
+ if ((operation == (secitem_operation)SecItemAdd) &&
+ (status == errSecDuplicateItem)) {
+ duplicate_cert = true;
+ status = errSecSuccess;
+ }
+
+ if (!status || status == errSecItemNotFound) {
+ bool skip_key_operation = false;
+
+ /* if the key is still in use, skip deleting it */
+ if (operation == (secitem_operation)SecItemDelete) {
+ // find certs with cert.pkhh == keys.klbl
+ CFDictionaryRef key_dict = NULL, query_dict = NULL;
+ CFDataRef pkhh = NULL;
+
+ key_dict = SecKeyCopyAttributeDictionary(key);
+ if (key_dict)
+ pkhh = (CFDataRef)CFDictionaryGetValue(key_dict, kSecAttrApplicationLabel);
+ const void *keys[] = { kSecClass, kSecAttrPublicKeyHash };
+ const void *vals[] = { kSecClassCertificate, pkhh };
+ if (pkhh)
+ query_dict = CFDictionaryCreate(NULL, keys,
+ vals, (array_size(keys)),
+ NULL, NULL);
+ if (query_dict)
+ if (errSecSuccess == SecItemCopyMatching(query_dict, NULL))
+ skip_key_operation = true;
+ CFReleaseSafe(query_dict);
+ CFReleaseSafe(key_dict);
+ }
+
+ if (!skip_key_operation) {
+ /* now perform the operation for the key */
+ CFDictionarySetValue(partial_query, kSecValueRef, key);
+ CFDictionarySetValue(partial_query, kSecReturnPersistentRef, kCFBooleanFalse);
+ status = operation(partial_query, NULL);
+ if ((operation == (secitem_operation)SecItemAdd) &&
+ (status == errSecDuplicateItem) &&
+ !duplicate_cert)
+ status = errSecSuccess;
+ }
+
+ /* add and copy matching for an identityref have a persistent ref result */
+ 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);
+ }
+ }
+ CFRelease(result);
+ }
+ }
+ CFReleaseNull(partial_query);
+ }
+ else
+ status = errSecInvalidItemRef;
+
+ CFReleaseNull(cert);
+ CFReleaseNull(key);
+ *return_status = status;
+ }
+ } else {
+ value = CFDictionaryGetValue(attributes, kSecClass);
+ if (value && CFEqual(kSecClassIdentity, value) &&
+ (operation == (secitem_operation)SecItemDelete)) {
+ CFMutableDictionaryRef dict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, attributes);
+ CFDictionaryRemoveValue(dict, kSecClass);
+ CFDictionarySetValue(dict, kSecClass, kSecClassCertificate);
+ OSStatus status = SecItemDelete(dict);
+ if (!status) {
+ CFDictionarySetValue(dict, kSecClass, kSecClassKey);
+ status = SecItemDelete(dict);
+ }
+ CFRelease(dict);
+ *return_status = status;
+ handled = true;
+ }
+ }
+ return handled;
+}
+
+static void infer_cert_label(SecCFDictionaryCOW *attributes)
+{
+ if (!CFDictionaryContainsKey(attributes->dictionary, kSecAttrLabel)) {
+ CFTypeRef value_ref = CFDictionaryGetValue(attributes->dictionary, kSecValueRef);
+ if (value_ref && CFGetTypeID(value_ref) == SecCertificateGetTypeID()) {
+ SecCertificateRef certificate = (SecCertificateRef)value_ref;
+ CFStringRef label = SecCertificateCopySubjectSummary(certificate);
+ if (label) {
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes), kSecAttrLabel, label);
+ CFReleaseNull(label);
+ }
+ }
+ }
+}
+
+/* A persistent ref is just the class and the rowid of the record. */
+CF_RETURNS_RETAINED CFDataRef _SecItemMakePersistentRef(CFTypeRef class, sqlite_int64 rowid)
+{
+ 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));
+ }
+ return NULL;
+}
+
+/* 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 valid_ref = false;
+ if (CFGetTypeID(persistent_ref) == CFDataGetTypeID() &&
+ 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;
+ }
+ }
+ CFRelease(class);
+ }
+ return valid_ref;
+}
+
+#endif // *** END SECITEM_SHIM_OSX ***
+
+static bool cf_bool_value(CFTypeRef cf_bool)
+{
+ return (cf_bool && CFEqual(kCFBooleanTrue, cf_bool));
+}
+
+CFMutableDictionaryRef SecCFDictionaryCOWGetMutable(SecCFDictionaryCOW *cow_dictionary) {
+ if (cow_dictionary->mutable_dictionary == NULL) {
+ cow_dictionary->mutable_dictionary = CFDictionaryCreateMutableForCFTypes(NULL);
+ if (cow_dictionary->dictionary != NULL) {
+ CFDictionaryForEach(cow_dictionary->dictionary, ^(const void *key, const void *value) {
+ CFDictionarySetValue(cow_dictionary->mutable_dictionary, key, value);
+ });
+ }
+ cow_dictionary->dictionary = cow_dictionary->mutable_dictionary;
+ }
+
+ 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) {
+ CFMutableDictionaryRef value = NULL;
+ value = CFDictionaryCreateMutableForCFTypesWith(NULL,
+ kSecTokenValueObjectIDKey, oid,
+ kSecTokenValueAccessControlKey, access_control,
+ NULL);
+ if (object_value != NULL) {
+ CFDictionarySetValue(value, kSecTokenValueDataKey, object_value);
+ }
+
+ CFDataRef value_data = CFPropertyListCreateDERData(NULL, value, error);
+ CFRelease(value);
+ return value_data;
+}
+
+static CFDictionaryRef SecTokenItemValueCopy(CFDataRef db_value, CFErrorRef *error) {
+ CFPropertyListRef plist = NULL;
+ 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_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,
+ SecError(errSecInternal, error, CFSTR("token based item data does not have OID")));
+
+out:
+ return plist;
+}
+
+TKTokenRef SecTokenCreate(CFStringRef token_id, CFDictionaryRef auth_params, CFErrorRef *error) {
+ CFMutableDictionaryRef token_attrs = NULL;
+ TKTokenRef token = NULL;
+ token_attrs = (auth_params != NULL) ?
+ CFDictionaryCreateMutableCopy(NULL, 0, auth_params) :
+ CFDictionaryCreateMutableForCFTypes(NULL);
+ CFDictionarySetValue(token_attrs, kSecAttrTokenID, token_id);
+
+ CFDictionaryRemoveValue(token_attrs, kSecUseAuthenticationContext);
+ token = TKTokenCreate(token_attrs, error);
+
+ CFReleaseSafe(token_attrs);
+ return token;
+}
+
+static bool SecTokenItemCreateFromAttributes(CFDictionaryRef attributes, CFDictionaryRef auth_params,
+ TKTokenRef token, CFDataRef object_id, CFTypeRef *ref, CFErrorRef *error) {
+ bool ok = false;
+ CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, attributes);
+ CFTypeRef token_id = CFDictionaryGetValue(attributes, kSecAttrTokenID);
+ if (token_id != NULL) {
+ if (CFRetainSafe(token) == NULL) {
+ 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);
+ });
+ }
+ CFDictionarySetValue(attrs, kSecUseToken, token);
+ CFDictionarySetValue(attrs, kSecUseTokenObjectID, object_id);
+ CFRelease(token);
+ }
+ *ref = SecItemCreateFromAttributeDictionary(attrs);
+ ok = true;
+
+out:
+ CFReleaseSafe(attrs);
+ return ok;
+}
+
+
+/* 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,
+ CFTypeRef *result, CFErrorRef *error) {
+ bool ok = false;
+ CFDataRef ac_data = NULL;
+ CFDataRef value = NULL;
+ CFTypeRef persistent_ref = NULL;
+ CFStringRef token_id = NULL;
+ CFDataRef object_id = NULL;
+ CFMutableDictionaryRef attrs = NULL;
+
+ bool wants_ref = cf_bool_value(CFDictionaryGetValue(query, kSecReturnRef));
+ bool wants_data = cf_bool_value(CFDictionaryGetValue(query, kSecReturnData));
+ bool wants_attributes = cf_bool_value(CFDictionaryGetValue(query, kSecReturnAttributes));
+ bool wants_persistent_ref = cf_bool_value(CFDictionaryGetValue(query, kSecReturnPersistentRef));
+
+ // Get token value if not provided by the caller.
+ bool token_item = false;
+ if (token == NULL) {
+ if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID()) {
+ token_id = CFDictionaryGetValue(raw_result, kSecAttrTokenID);
+ token_item = (token_id != NULL);
+ }
+ } else {
+ token_item = true;
+ CFRetain(token);
+ }
+
+ // 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())
+ value = CFRetainSafe(CFDictionaryGetValue(raw_result, kSecValueData));
+ else
+ value = CFRetainSafe(raw_result);
+ if (token_item && value != NULL) {
+ // Parse token-based item's data field.
+ CFDataRef object_value = NULL;
+ CFDictionaryRef parsed_value = NULL;
+ require_quiet(parsed_value = SecTokenItemValueCopy(value, error), out);
+ object_id = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueObjectIDKey));
+ ac_data = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueAccessControlKey));
+ object_value = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueDataKey));
+ CFRelease(parsed_value);
+ if (wants_data && object_value == NULL) {
+ // Retrieve value directly from the token.
+ if (token == NULL) {
+ 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))
+ CFReleaseNull(object_value);
+ }
+ CFAssignRetained(value, object_value);
+ }
+
+ // If only thing requested is data, return them directly.
+ if (!(wants_attributes || wants_persistent_ref || wants_ref)) {
+ *result = CFRetainSafe(value);
+ ok = true;
+ goto out;
+ }
+ }
+
+ // Extract persistent_ref, if caller wants it.
+ if (wants_persistent_ref) {
+ if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID())
+ persistent_ref = CFRetainSafe(CFDictionaryGetValue(raw_result, kSecValuePersistentRef));
+ else
+ persistent_ref = CFRetainSafe(raw_result);
+
+ // If only thing requested is persistentref, extract it from dictionary if needed and return it.
+ if (!(wants_attributes || wants_data || wants_ref)) {
+ *result = CFRetainSafe(persistent_ref);
+ ok = true;
+ goto out;
+ }
+ }
+
+ 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_persistent_ref && persistent_ref != NULL)
+ CFDictionarySetValue(output, kSecValuePersistentRef, persistent_ref);
+ else
+ CFDictionaryRemoveValue(output, kSecValuePersistentRef);
+
+ 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_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);
+ }
+ }
+ } else {
+ *result = NULL;
+ }
+
+ ok = true;
+
+out:
+ CFReleaseSafe(ac_data);
+ CFReleaseSafe(value);
+ CFReleaseSafe(persistent_ref);
+ CFReleaseSafe(object_id);
+ CFReleaseSafe(attrs);
+ CFReleaseSafe(token);
+ return ok;
+}
+
+static 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);
+
+ if (CFGetTypeID(raw_result) == CFArrayGetTypeID()) {
+ CFIndex i, count = CFArrayGetCount(raw_result);
+ *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) {
+ CFArrayAppendValue((CFMutableArrayRef)*result, ref);
+ CFRelease(ref);
+ }
+ }
+ } else {
+ require_quiet(SecItemResultCopyPrepared(raw_result, token, query, auth_params, result, error), out);
+ }
+
+ ok = true;
+
+out:
+ return ok;
+}
+
+static bool SecItemAttributesPrepare(SecCFDictionaryCOW *attrs, CFErrorRef *error) {
+ bool ok = false;
+ CFDataRef ac_data = NULL, acm_context = NULL;
+ void *la_lib = NULL;
+
+ SecAccessControlRef access_control = (SecAccessControlRef)CFDictionaryGetValue(attrs->dictionary, kSecAttrAccessControl);
+ if (access_control != NULL) {
+ require_quiet(ac_data = SecAccessControlCopyData(access_control), out);
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecAttrAccessControl, ac_data);
+ }
+
+ const CFTypeRef la_context = CFDictionaryGetValue(attrs->dictionary, kSecUseAuthenticationContext);
+ if (la_context) {
+ require_action_quiet(!CFDictionaryContainsKey(attrs->dictionary, kSecUseCredentialReference), out,
+ SecError(errSecParam, error, CFSTR("kSecUseAuthenticationContext cannot be used together with kSecUseCredentialReference")));
+ 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")));
+ CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseAuthenticationContext);
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseCredentialReference, acm_context);
+ }
+
+#ifndef SECITEM_SHIM_OSX
+ // If a ref was specified we get it's attribute dictionary and parse it.
+ CFTypeRef value = CFDictionaryGetValue(attrs->dictionary, kSecValueRef);
+ if (value) {
+ CFDictionaryRef ref_attributes;
+ require_action_quiet(ref_attributes = SecItemCopyAttributeDictionary(value), 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;
+ }
+ 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) };
+ DERDecodedInfo content;
+ if (!DERDecodeItem(&name, &content) &&
+ (content.tag == ASN1_CONSTR_SEQUENCE))
+ {
+ CFDataRef canonical_issuer = createNormalizedX501Name(kCFAllocatorDefault, &content.content);
+ if (canonical_issuer) {
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecAttrIssuer, canonical_issuer);
+ CFRelease(canonical_issuer);
+ }
+ }
+ }
+#endif
+
+ ok = true;
+
+out:
+ if (la_lib != NULL) {
+ dlclose(la_lib);
+ }
+ CFReleaseSafe(ac_data);
+ CFReleaseSafe(acm_context);
+ return ok;
+}
+
+static bool SecItemAuthMaxAttemptsReached(CFArrayRef ac_pairs, CFErrorRef *error)
+{
+ CFMutableStringRef log_string = CFStringCreateMutable(kCFAllocatorDefault, 0);
+ CFArrayRef ac_pair;
+ CFArrayForEachC(ac_pairs, ac_pair) {
+ CFStringRef acl_hex_string = CFDataCopyHexString(CFArrayGetValueAtIndex(ac_pair, 0));
+ CFStringRef str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("operation: %@ acl:%@\n"), CFArrayGetValueAtIndex(ac_pair, 1), acl_hex_string);
+ CFStringAppend(log_string, str);
+ CFRelease(acl_hex_string);
+ CFRelease(str);
+ }
+
+ CFStringRef reason = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("Reached maximum count of authentication attempts\n %@"), log_string);
+ SecError(errSecAuthFailed, error, 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 ok = false;
+ CFArrayRef ac_pairs = NULL;
+ SecCFDictionaryCOW auth_options = { NULL };
+
+ 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);
+ require_quiet(auth_result != kSecItemAuthResultError, out);
+ require_action_quiet(auth_result == kSecItemAuthResultNeedAuth, out, ok = true);
+
+ // If auth_params were not created up to now, do create them because we will definitely need them.
+ SecCFDictionaryCOWGetMutable(auth_params);
+
+ // Retrieve or create authentication handle and/or ACM context.
+ CFTypeRef auth_handle = CFDictionaryGetValue(auth_params->dictionary, kSecUseAuthenticationContext);
+ if (auth_handle == NULL) {
+ CFDataRef acm_context = CFDictionaryGetValue(auth_params->dictionary, kSecUseCredentialReference);
+ require_quiet(auth_handle = LACreateNewContextWithACMContext(acm_context, error), out);
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseAuthenticationContext, auth_handle);
+ CFRelease(auth_handle);
+ if (acm_context == NULL) {
+ require_quiet(acm_context = LACopyACMContext(auth_handle, error), out);
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseCredentialReference, acm_context);
+ CFRelease(acm_context);
+ }
+ }
+
+ // Throttle max authentication attempts. This is mainly protection against exceptional states, not ordinary
+ // user retry limit.
+ require_action(i < 20, out, SecItemAuthMaxAttemptsReached(ac_pairs, error));
+
+ // Prepare auth options dictionary.
+ if (auth_options.dictionary == NULL) {
+ CFStringRef operation_prompt = CFDictionaryGetValue(auth_params->dictionary, kSecUseOperationPrompt);
+ if (operation_prompt != NULL) {
+ CFNumberRef key = CFNumberCreateWithCFIndex(NULL, kLAOptionAuthenticationReason);
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options), key, operation_prompt);
+ CFRelease(key);
+ }
+
+ CFTypeRef auth_ui = CFDictionaryGetValue(auth_params->dictionary, kSecUseAuthenticationUI);
+ if (CFEqualSafe(auth_ui, kSecUseAuthenticationUIFail)) {
+ CFNumberRef key = CFNumberCreateWithCFIndex(NULL, kLAOptionNotInteractive);
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options), key, kCFBooleanTrue);
+ CFRelease(key);
+ }
+ }
+
+ // Go through all access_control-operation pairs and evaluate them.
+ CFArrayRef ac_pair;
+ CFArrayForEachC(ac_pairs, ac_pair) {
+ CFDataRef updated_acl = NULL;
+ require_quiet(LAEvaluateAndUpdateACL(auth_handle,
+ CFArrayGetValueAtIndex(ac_pair, 0), CFArrayGetValueAtIndex(ac_pair, 1),
+ auth_options.dictionary, &updated_acl, error), out);
+
+ if (updated_acl || CFEqual(CFArrayGetValueAtIndex(ac_pair, 1), CFSTR(""))) {
+ // we assume that only one ACL can be modified during ItemAdd or ItemUpdate
+ SecAccessControlRef ac = NULL;
+ require(ac = SecAccessControlCreateFromData(kCFAllocatorDefault,
+ updated_acl ? updated_acl : CFArrayGetValueAtIndex(ac_pair, 0), error), out);
+ SecAccessControlSetBound(ac, true);
+ CFAssignRetained(updated_acl, SecAccessControlCopyData(ac));
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecAttrAccessControl, updated_acl);
+ CFRelease(updated_acl);
+ CFRelease(ac);
+ }
+ }
+ }
+
+ ok = true;
+
+out:
+ CFReleaseSafe(auth_options.mutable_dictionary);
+ CFReleaseSafe(ac_pairs);
+ return ok;
+}
+
+void SecItemAuthCopyParams(SecCFDictionaryCOW *auth_params, SecCFDictionaryCOW *query) {
+ // Store operation prompt.
+ CFStringRef operation_prompt = CFDictionaryGetValue(query->dictionary, kSecUseOperationPrompt);
+ if (operation_prompt != NULL) {
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseOperationPrompt, operation_prompt);
+ CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecUseOperationPrompt);
+ }
+
+ // Find out whether we are allowed to pop up a UI.
+ CFTypeRef auth_ui = CFDictionaryGetValue(query->dictionary, kSecUseAuthenticationUI) ?:
+ (CFEqualSafe(CFDictionaryGetValue(query->dictionary, kSecUseNoAuthenticationUI), kCFBooleanTrue) ?
+ kSecUseAuthenticationUIFail : kSecUseAuthenticationUIAllow);
+ if (!CFEqual(auth_ui, kSecUseAuthenticationUISkip) || CFDictionaryGetValue(query->dictionary, kSecUseNoAuthenticationUI)) {
+ if (CFDictionaryContainsKey(query->dictionary, kSecUseNoAuthenticationUI))
+ CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecUseNoAuthenticationUI);
+ if (!CFEqualSafe(auth_ui, kSecUseAuthenticationUISkip) && CFDictionaryContainsKey(query->dictionary, kSecUseAuthenticationUI))
+ CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecUseAuthenticationUI);
+ }
+
+ if (!CFEqual(auth_ui, kSecUseAuthenticationUIAllow)) {
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseAuthenticationUI, auth_ui);
+ }
+
+ CFDataRef acm_context = CFDictionaryGetValue(query->dictionary, kSecUseCredentialReference);
+ if (acm_context != NULL) {
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseCredentialReference, acm_context);
+ }
+}
+
+static SecItemAuthResult SecItemCreatePairsFromError(CFErrorRef *error, CFArrayRef *ac_pairs)
+{
+ if (error && CFErrorGetCode(*error) == errSecAuthNeeded && CFEqualSafe(CFErrorGetDomain(*error), kSecErrorDomain)) {
+ // Extract ACLs to be verified from the error.
+ CFDictionaryRef user_info = CFErrorCopyUserInfo(*error);
+ CFNumberRef key = CFNumberCreateWithCFIndex(NULL, errSecAuthNeeded);
+ CFRetainAssign(*ac_pairs, CFDictionaryGetValue(user_info, key));
+ if (*ac_pairs == NULL)
+ CFAssignRetained(*ac_pairs, CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
+
+ CFRelease(key);
+ CFRelease(user_info);
+ CFReleaseNull(*error);
+ return kSecItemAuthResultNeedAuth;
+ }
+ return kSecItemAuthResultError;
+}
+
+// Wrapper to handle automatic authentication and token/secd case switching.
+static 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 TKTokenRef token = NULL;
+
+ 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);
+ }
+
+ // Perform initial surgery on query/attributes (resolve LAContext to serialized ACM handle, resolve
+ // SecAccessControlRef to serialized forms, expand kSecValueRef etc.)
+ require_quiet(SecItemAttributesPrepare(query, error), out);
+ if (attributes != NULL)
+ require_quiet(SecItemAttributesPrepare(attributes, error), out);
+
+ // Populate auth_params dictionary according to initial query contents.
+ SecItemAuthCopyParams(&auth_params, query);
+
+ if (secItemOperation != SecItemCopyMatching) {
+ // UISkip is allowed only for CopyMatching.
+ require_action_quiet(!CFEqualSafe(CFDictionaryGetValue(query->dictionary, kSecUseAuthenticationUI), kSecUseAuthenticationUISkip), out,
+ SecError(errSecParam, error,
+ CFSTR("kSecUseAuthenticationUISkip is allowed only for SecItemCopyMatching")));
+ }
+
+ ok = SecItemAuthDo(&auth_params, error, ^SecItemAuthResult(CFDictionaryRef auth_params, 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 (acm_context != NULL) {
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query), kSecUseCredentialReference, acm_context);
+ }
+
+ CFDataRef acl_data_ref = CFDictionaryGetValue(auth_params, kSecAttrAccessControl);
+ if (acl_data_ref != NULL) {
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes ?: query), kSecAttrAccessControl, acl_data_ref);
+ }
+ }
+
+ // 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);
+ }
+
+ 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);
+ }
+
+ result = kSecItemAuthResultOK;
+
+ out:
+ return result;
+ });
+ require_quiet(ok, out);
+
+ ok = true;
+
+out:
+ CFReleaseSafe(token);
+ CFReleaseSafe(auth_params.mutable_dictionary);
+ return ok;
+}
+
+#if SECITEM_SHIM_OSX
+/* TODO: Should be in some header */
+OSStatus SecItemAdd_ios(CFDictionaryRef attributes, CFTypeRef *result);
+OSStatus SecItemCopyMatching_ios(CFDictionaryRef query, CFTypeRef *result);
+OSStatus SecItemUpdate_ios(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
+OSStatus SecItemDelete_ios(CFDictionaryRef query);
+#endif
+
+static bool cftype_to_bool_cftype_error_request(enum SecXPCOperation op, CFTypeRef attributes, CFTypeRef *result, CFErrorRef *error)
+{
+ return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
+ return SecXPCDictionarySetPList(message, kSecXPCKeyQuery, attributes, error);
+ }, ^bool(xpc_object_t response, CFErrorRef *error) {
+ if (result) {
+ return SecXPCDictionaryCopyPListOptional(response, kSecXPCKeyResult, result, error);
+ }
+ return true;
+ });
+}
+
+static CFArrayRef dict_to_array_error_request(enum SecXPCOperation op, CFDictionaryRef attributes, CFErrorRef *error)
+{
+ CFArrayRef result = NULL;
+ bool success = cftype_to_bool_cftype_error_request(op, attributes, (CFTypeRef*)&result, error);
+ if(success && !isArray(result)){
+ SecError(errSecUnimplemented, error, CFSTR("Unexpected nonarray returned: %@"), result);
+ CFReleaseNull(result);
+ }
+ return result;
+}
+
+bool cftype_ag_to_bool_cftype_error_request(enum SecXPCOperation op, CFTypeRef attributes, __unused CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error) {
+ return cftype_to_bool_cftype_error_request(op, attributes, result, error);
+}
+
+static bool dict_to_error_request(enum SecXPCOperation op, CFDictionaryRef query, CFErrorRef *error)
+{
+ return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
+ return SecXPCDictionarySetPList(message, kSecXPCKeyQuery, query, error);
+ }, NULL);
+}
+
+static bool dict_ag_to_error_request(enum SecXPCOperation op, CFDictionaryRef query, __unused CFArrayRef accessGroups, CFErrorRef *error)
+{
+ return dict_to_error_request(op, query, error);
+}
+
+static bool SecTokenCreateAccessControlError(CFStringRef operation, CFDataRef access_control, CFErrorRef *error) {
+ CFArrayRef ac_pair = CFArrayCreateForCFTypes(NULL, access_control, operation, NULL);
+ const void *ac_pairs[] = { CFArrayCreateForCFTypes(NULL, ac_pair, NULL) };
+ const void *keys[] = { CFNumberCreateWithCFIndex(NULL, errSecAuthNeeded) };
+ CFAssignRetained(*error, CFErrorCreateWithUserInfoKeysAndValues(NULL, kSecErrorDomain, errSecAuthNeeded,
+ keys, ac_pairs, 1));
+ CFRelease(keys[0]);
+ CFRelease(ac_pairs[0]);
+ CFRelease(ac_pair);
+ return false;
+}
+
+static bool SecTokenProcessError(CFStringRef operation, TKTokenRef token, CFTypeRef object_or_attrs, CFErrorRef *error) {
+ if (CFEqualSafe(CFErrorGetDomain(*error), CFSTR(kTKErrorDomain)) &&
+ CFErrorGetCode(*error) == kTKErrorCodeAuthenticationFailed) {
+ // 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);
+ if (access_control != NULL) {
+ SecTokenCreateAccessControlError(operation, access_control, error);
+ CFRelease(access_control);
+ }
+ }
+ return false;
+}
+
+static CFTypeRef SecTokenCopyUpdatedObjectID(TKTokenRef token, CFDataRef object_id, CFMutableDictionaryRef attributes, CFErrorRef *error) {
+ CFDataRef access_control = NULL, db_value = NULL, new_object_id = NULL;
+ SecAccessControlRef ac = NULL;
+
+ // Make sure that ACL is bound - if not, generate an error which will trigger binding.
+ CFDataRef ac_data = CFDictionaryGetValue(attributes, kSecAttrAccessControl);
+ if (ac_data != NULL) {
+ require_quiet(ac = SecAccessControlCreateFromData(NULL, ac_data, error), out);
+ require_action_quiet(SecAccessControlIsBound(ac), out,
+ SecTokenCreateAccessControlError(CFSTR(""), ac_data, error));
+ }
+
+ // Create or update the object on the token.
+ require_action_quiet(new_object_id = TKTokenCreateOrUpdateObject(token, object_id, attributes, error), out,
+ SecTokenProcessError(kAKSKeyOpEncrypt, token, object_id ?: (CFTypeRef)attributes, error));
+
+ // Prepare kSecValueData field for the item to be stored into the keychain DB.
+ require_quiet(access_control = TKTokenCopyObjectAccessControl(token, new_object_id, error), out);
+ require_quiet(db_value = SecTokenItemValueCreate(new_object_id, access_control,
+ CFDictionaryGetValue(attributes, kSecValueData), error), out);
+ CFDictionarySetValue(attributes, kSecValueData, db_value);
+
+ // kSecAttrAccessControl is handled directly by the token and stored inside data field.
+ CFDictionaryRemoveValue(attributes, kSecAttrAccessControl);
+
+out:
+ CFReleaseSafe(ac);
+ CFReleaseSafe(access_control);
+ CFReleaseSafe(db_value);
+ return new_object_id;
+}
+
+static bool SecTokenItemAdd(TKTokenRef token, CFDictionaryRef attributes, CFDictionaryRef auth_params,
+ CFTypeRef *result, CFErrorRef *error) {
+ bool ok = false;
+ CFTypeRef object_id = NULL, ref = NULL;
+ CFDictionaryRef ref_attrs = NULL;
+ CFTypeRef db_result = NULL;
+
+ CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, attributes);
+ require_quiet(object_id = SecTokenCopyUpdatedObjectID(token, NULL, attrs, error), out);
+
+#ifndef SECITEM_SHIM_OSX
+ // 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)) != NULL) {
+ CFDictionaryForEach(ref_attrs, ^(const void *key, const void *value) {
+ if (!CFEqual(key, kSecValueData)) {
+ CFDictionaryAddValue(attrs, key, value);
+ }
+ });
+ }
+ }
+#endif
+
+ // Make sure that both attributes and data are returned.
+ CFDictionarySetValue(attrs, kSecReturnAttributes, kCFBooleanTrue);
+ CFDictionarySetValue(attrs, kSecReturnData, kCFBooleanTrue);
+
+ if (!CFEqualSafe(CFDictionaryGetValue(attrs, kSecAttrIsPermanent), kCFBooleanFalse)) {
+ // IsPermanent is not present or is true, so add item to the db.
+ require_quiet(SECURITYD_XPC(sec_item_add, cftype_ag_to_bool_cftype_error_request, attrs,
+ SecAccessGroupsGetCurrent(), &db_result, error), out);
+ } else {
+ // Process directly result of token call.
+ db_result = CFRetain(attrs);
+ }
+ require_quiet(SecItemResultProcess(attributes, auth_params, token, db_result, result, error), out);
+
+ ok = true;
+
+out:
+ CFReleaseSafe(db_result);
+ CFReleaseSafe(attrs);
+ CFReleaseSafe(ref_attrs);
+ CFReleaseSafe(object_id);
+ CFReleaseSafe(ref);
+ return ok;
+}
+
+OSStatus
+#if SECITEM_SHIM_OSX
+SecItemAdd_ios(CFDictionaryRef attributes, CFTypeRef *result)
+#else
+SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result)
+#endif // *** END SECITEM_SHIM_OSX ***
+{
+ __block SecCFDictionaryCOW attrs = { attributes };
+ OSStatus status;
+
+ os_activity_t trace_activity = os_activity_start("SecItemAdd_ios", OS_ACTIVITY_FLAG_DEFAULT);
+
+#ifndef SECITEM_SHIM_OSX
+ require_quiet(!explode_identity(attrs.dictionary, (secitem_operation)SecItemAdd, &status, result), errOut);
+ infer_cert_label(&attrs);
+#endif // *** END SECITEM_SHIM_OSX ***
+
+ status = SecOSStatusWith(^bool(CFErrorRef *error) {
+ 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_ag_to_bool_cftype_error_request, attributes, SecAccessGroupsGetCurrent(), &raw_result, error))
+ return false;
+
+ bool ok = SecItemResultProcess(attributes, auth_params, token, raw_result, result, error);
+ CFReleaseSafe(raw_result);
+ return ok;
+ } else {
+ // Send request to an appropriate token instead of secd.
+ return SecTokenItemAdd(token, attributes, auth_params, result, error);
+ }
+ });
+ });
+
+#ifndef SECITEM_SHIM_OSX
+errOut:
+#endif
+ CFReleaseSafe(attrs.mutable_dictionary);
+
+ os_activity_end(trace_activity);
+ return status;
+}
+
+
+OSStatus
+#if SECITEM_SHIM_OSX
+SecItemCopyMatching_ios(CFDictionaryRef inQuery, CFTypeRef *result)
+#else
+SecItemCopyMatching(CFDictionaryRef inQuery, CFTypeRef *result)
+#endif // *** END SECITEM_SHIM_OSX ***
+{
+ OSStatus status;
+ __block SecCFDictionaryCOW query = { inQuery };
+
+ os_activity_t trace_activity = os_activity_start("SecItemCopyMatching_ios", OS_ACTIVITY_FLAG_DEFAULT);
+
+#ifndef SECITEM_SHIM_OSX
+ require_quiet(!explode_identity(query.dictionary, (secitem_operation)SecItemCopyMatching, &status, result), errOut);
+#endif // *** END SECITEM_SHIM_OSX ***
+
+ 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)) {
+ // 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_ag_to_bool_cftype_error_request, query, SecAccessGroupsGetCurrent(), &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
+ // to currently processed item.
+ bool ok = SecItemResultProcess(inQuery, auth_params, NULL, raw_result, result, error);
+ CFReleaseSafe(raw_result);
+ return ok;
+ });
+ });
+
+#ifndef SECITEM_SHIM_OSX
+errOut:
+#endif
+
+ CFReleaseSafe(query.mutable_dictionary);
+ os_activity_end(trace_activity);
+ return status;
+}
+
+// Invokes token-object handler for each item matching specified query.
+static bool SecTokenItemForEachMatching(CFDictionaryRef query, CFErrorRef *error,
+ bool (^perform)(CFDictionaryRef item_value, CFDictionaryRef item_query,
+ CFErrorRef *error)) {
+ bool ok = false;
+ CFMutableDictionaryRef list_query = NULL;
+ CFTypeRef items = NULL;
+ CFArrayRef ref_array = NULL;
+ CFDictionaryRef item_query = NULL, item_data = NULL;
+
+ // Query all items with data and persistent_ref, so that we can extract objectIDs and also identify originating
+ // items in the keychain.
+ list_query = CFDictionaryCreateMutableCopy(NULL, 0, query);
+ if (CFDictionaryGetValue(list_query, kSecMatchLimit) == NULL) {
+ CFDictionarySetValue(list_query, kSecMatchLimit, kSecMatchLimitAll);
+ }
+ CFDictionarySetValue(list_query, kSecReturnData, kCFBooleanTrue);
+ CFDictionarySetValue(list_query, kSecReturnPersistentRef, kCFBooleanTrue);
+ require_quiet(SECURITYD_XPC(sec_item_copy_matching, cftype_ag_to_bool_cftype_error_request, list_query,
+ SecAccessGroupsGetCurrent(), &items, error), out);
+ if (CFGetTypeID(items) != CFArrayGetTypeID()) {
+ // Wrap single returned item into the array.
+ CFArrayRef item_array = CFArrayCreateForCFTypes(NULL, items, NULL);
+ CFAssignRetained(items, item_array);
+ }
+
+ CFTypeRef item;
+ CFArrayForEachC(items, item) {
+ CFDataRef data = CFDictionaryGetValue(item, kSecValueData);
+ require_action_quiet(data != NULL, out, SecError(errSecInternal, error, CFSTR("value not present for token item")));
+
+ CFAssignRetained(item_data, SecTokenItemValueCopy(data, error));
+ require_quiet(item_data, out);
+
+ CFAssignRetained(item_query,
+ CFDictionaryCreateForCFTypes(NULL,
+ kSecValuePersistentRef, CFDictionaryGetValue(item, kSecValuePersistentRef),
+ NULL));
+ require_quiet(perform(item_data, item_query, error), out);
+ }
+
+ ok = true;
+
+out:
+ CFReleaseSafe(list_query);
+ CFReleaseSafe(items);
+ CFReleaseSafe(item_data);
+ CFReleaseSafe(ref_array);
+ CFReleaseSafe(item_query);
+ return ok;
+}
+
+static bool SecItemRawUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate, CFErrorRef *error) {
+ bool ok = false;
+ if (gSecurityd) {
+ // Ensure the dictionary passed to securityd has proper kCFTypeDictionaryKeyCallBacks.
+ CFMutableDictionaryRef tmp = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
+ CFDictionaryForEach(attributesToUpdate, ^(const void *key, const void *value) { CFDictionaryAddValue(tmp, key, value); });
+ ok = gSecurityd->sec_item_update(query, tmp, SecAccessGroupsGetCurrent(), error);
+ CFRelease(tmp);
+ } else {
+ xpc_object_t message = securityd_create_message(sec_item_update_id, error);
+ if (message) {
+ if (SecXPCDictionarySetPList(message, kSecXPCKeyQuery, query, error) &&
+ SecXPCDictionarySetPList(message, kSecXPCKeyAttributesToUpdate, attributesToUpdate, error)) {
+ xpc_object_t reply = securityd_message_with_reply_sync(message, error);
+ if (reply) {
+ ok = securityd_message_no_error(reply, error);
+ xpc_release(reply);
+ }
+ }
+ xpc_release(message);
+ }
+ }
+ return ok;
+}
+
+static bool SecTokenItemUpdate(TKTokenRef token, CFDictionaryRef query, CFDictionaryRef attributesToUpdate, CFErrorRef *error) {
+ return SecTokenItemForEachMatching(query, error, ^bool(CFDictionaryRef object_data, CFDictionaryRef item_query, CFErrorRef *error) {
+ bool ok = false;
+ CFDataRef object_id = NULL;
+ CFMutableDictionaryRef db_value = NULL;
+
+ // Update attributes on the token.
+ CFMutableDictionaryRef attributes = CFDictionaryCreateMutableCopy(NULL, 0, attributesToUpdate);
+ require_quiet(object_id = SecTokenCopyUpdatedObjectID(token, CFDictionaryGetValue(object_data, kSecTokenValueObjectIDKey),
+ attributes, error), out);
+
+ // Update attributes in the database.
+ require_quiet(SecItemRawUpdate(item_query, attributes, error), out);
+
+ ok = true;
+
+ out:
+ CFReleaseSafe(object_id);
+ CFReleaseSafe(attributes);
+ CFReleaseSafe(db_value);
+ return ok;
+ });
+}
+
+OSStatus
+#if SECITEM_SHIM_OSX
+SecItemUpdate_ios(CFDictionaryRef inQuery, CFDictionaryRef inAttributesToUpdate)
+#else
+SecItemUpdate(CFDictionaryRef inQuery, CFDictionaryRef inAttributesToUpdate)
+#endif // *** END SECITEM_SHIM_OSX ***
+{
+ OSStatus status;
+ __block SecCFDictionaryCOW query = { inQuery };
+ __block SecCFDictionaryCOW attributesToUpdate = { inAttributesToUpdate };
+
+ status = SecOSStatusWith(^bool(CFErrorRef *error) {
+ return SecItemAuthDoQuery(&query, &attributesToUpdate, SecItemUpdate, error, ^bool(TKTokenRef token, CFDictionaryRef query, CFDictionaryRef attributes, CFDictionaryRef auth_params, CFErrorRef *error) {
+ if (token == NULL) {
+ return SecItemRawUpdate(query, attributes, error);
+ } else {
+ return SecTokenItemUpdate(token, query, attributes, error);
+ }
+ });
+ });
+
+ CFReleaseSafe(query.mutable_dictionary);
+ CFReleaseSafe(attributesToUpdate.mutable_dictionary);
+ return status;
+}
+
+#ifndef SECITEM_SHIM_OSX
+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)
+ && CFEqual(class, kSecClassIdentity)) {
+ const void *keys[] = { kSecReturnRef, kSecValuePersistentRef };
+ const void *vals[] = { kCFBooleanTrue, persist };
+ CFDictionaryRef persistent_query = CFDictionaryCreate(NULL, keys,
+ vals, (array_size(keys)), NULL, NULL);
+ CFTypeRef item_query = NULL;
+ status = SecItemCopyMatching(persistent_query, &item_query);
+ CFReleaseNull(persistent_query);
+ if (status)
+ return status;
+
+ CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecValuePersistentRef);
+ CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query), kSecValueRef, item_query);
+ CFRelease(item_query);
+ }
+
+ return status;
+}
+#endif
+
+OSStatus
+#if SECITEM_SHIM_OSX
+SecItemDelete_ios(CFDictionaryRef inQuery)
+#else
+SecItemDelete(CFDictionaryRef inQuery)
+#endif // *** END SECITEM_SHIM_OSX ***
+{
+ OSStatus status;
+ __block SecCFDictionaryCOW query = { inQuery };
+
+ os_activity_t trace_activity = os_activity_start("SecItemDelete_ios", OS_ACTIVITY_FLAG_DEFAULT);
+
+#ifndef SECITEM_SHIM_OSX
+ require_noerr_quiet(status = explode_persistent_identity_ref(&query), errOut);
+ require_quiet(!explode_identity(query.dictionary, (secitem_operation)SecItemDelete, &status, NULL), errOut);
+#endif // *** END SECITEM_SHIM_OSX ***
+
+ status = SecOSStatusWith(^bool(CFErrorRef *error) {
+ return SecItemAuthDoQuery(&query, NULL, SecItemDelete, error, ^bool(TKTokenRef token, CFDictionaryRef query, CFDictionaryRef attributes, CFDictionaryRef auth_params, CFErrorRef *error) {
+ if (token == NULL) {
+ return SECURITYD_XPC(sec_item_delete, dict_ag_to_error_request, query, SecAccessGroupsGetCurrent(), error);
+ } else {
+ return SecTokenItemForEachMatching(query, error, ^bool(CFDictionaryRef object_data, CFDictionaryRef item_query, CFErrorRef *error) {
+ bool ok = false;
+
+ // 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));
+
+ // Delete the item from the keychain.
+ require_quiet(SECURITYD_XPC(sec_item_delete, dict_ag_to_error_request, item_query,
+ SecAccessGroupsGetCurrent(), error), out);
+ ok = true;
+
+ out:
+ return ok;
+ });
+ }
+ });
+ });
+
+#ifndef SECITEM_SHIM_OSX
+errOut:
+#endif
+
+ CFReleaseSafe(query.mutable_dictionary);
+
+ os_activity_end(trace_activity);
+
+ return status;
+}
+
+OSStatus
+SecItemDeleteAll(void)
+{
+ return SecOSStatusWith(^bool (CFErrorRef *error) {
+ bool ok;
+ if (gSecurityd) {
+ ok = true;
+#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 {
+ ok = securityd_send_sync_and_do(sec_delete_all_id, error, NULL, NULL);
+ }
+ return ok;
+ });
+}
+
+
+CFArrayRef _SecKeychainSyncUpdateMessage(CFDictionaryRef updates, CFErrorRef *error) {
+ __block CFArrayRef result;
+ os_activity_initiate("_SecKeychainSyncUpdateMessage", OS_ACTIVITY_FLAG_DEFAULT, ^{
+ result = SECURITYD_XPC(sec_keychain_sync_update_message, dict_to_array_error_request, updates, error);
+ });
+ 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)
+{
+ do_if_registered(sec_roll_keys, force, error);
+
+ __block bool result = false;
+
+ secdebug("secitem","enter - %s", __FUNCTION__);
+ securityd_send_sync_and_do(kSecXPCOpRollKeys, error,
+ ^bool(xpc_object_t message, CFErrorRef *error) {
+ xpc_dictionary_set_bool(message, "force", force);
+ return true;
+ },
+ ^bool(xpc_object_t response, __unused CFErrorRef *error) {
+ result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
+ return result;
+ });
+ return result;
+}
+
+