#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)
}
}
-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) {
return errSecUserCanceled;
case kTKErrorCodeCorruptedData:
return errSecDecode;
+ case kTKErrorCodeTokenNotFound:
+ case kTKErrorCodeObjectNotFound:
+ return errSecItemNotFound;
default:
return errSecInternal;
}
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)) {
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>.
*/
CFDictionaryRemoveValue(filtered, kSecAttrCanUnwrap);
CFDictionaryRemoveValue(filtered, kSecAttrCanSignRecover);
CFDictionaryRemoveValue(filtered, kSecAttrCanVerifyRecover);
+ CFDictionaryRemoveValue(filtered, kSecAttrIsPermanent);
return filtered;
}
} 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);
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,
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);
}
}
}
-/* 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) {
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) {
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);
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);
out:
CFReleaseSafe(attrs);
+ CFReleaseSafe(auth_params.mutable_dictionary);
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,
+ CFDictionaryRef query, CFDictionaryRef auth_params_dict,
CFTypeRef *result, CFErrorRef *error) {
bool ok = false;
CFDataRef ac_data = NULL;
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));
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 {
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())
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))
}
}
- 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;
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);
*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);
}
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);
}
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,
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);
}
}
+ 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;
}
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);
require_quiet(acm_context = LACopyACMContext(auth_handle, error), out);
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseCredentialReference, acm_context);
CFRelease(acm_context);
+ if (newCredentialRefAdded) {
+ newCredentialRefAdded();
+ }
}
}
}
// 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);
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);
}
// 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;
}, 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;
}
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);
}
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);
}
// 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);
// 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,
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);
+ }
+ });
+ }
}
}
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);
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);
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);
errOut:
CFReleaseSafe(attrs.mutable_dictionary);
+ secdebug("secitem", "SecItemAdd returned: %d", (int)status);
- os_activity_end(trace_activity);
return status;
}
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
});
errOut:
-
+ secdebug("secitem", "SecItemCopyMatching_ios returned: %d", (int)status);
CFReleaseSafe(query.mutable_dictionary);
- os_activity_end(trace_activity);
return status;
}
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);
}
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);
});
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 {
errOut:
CFReleaseSafe(query.mutable_dictionary);
CFReleaseSafe(attributesToUpdate.mutable_dictionary);
+ secdebug("secitem", "SecItemUpdateWithError returned: %d", (int)result);
return result;
}
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 };
CFReleaseNull(persistent_query);
if (status)
return status;
+ if (item_query == NULL)
+ return errSecItemNotFound;
CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecValuePersistentRef);
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query), kSecValueRef, item_query);
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 {
// 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,
errOut:
CFReleaseSafe(query.mutable_dictionary);
-
- os_activity_end(trace_activity);
-
+ secdebug("secitem", "SecItemDelete returned: %d", (int)status);
+
return status;
}
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 {
});
}
-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;
}
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)
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;
+}