#include <Security/SecBasePriv.h>
#include <Security/SecItem.h>
#include <Security/SecItemPriv.h>
#include <Security/SecItemInternal.h>
#include <Security/SecItemShim.h>
#include <Security/SecAccessControl.h>
#include <Security/SecAccessControlPriv.h>
#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>
#include <Security/SecCTKKeyPriv.h>
#include <Security/SecTask.h>
#include <Security/SecPolicyInternal.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 <pthread.h>
#include <os/lock.h>
#include <Security/SecInternal.h>
#include "SOSInternal.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>
#include <libDER/asn1Types.h>
#include <utilities/SecDb.h>
#include <IOKit/IOReturn.h>
#include <coreauthd_spi.h>
#include <LocalAuthentication/LAPrivateDefines.h>
#include <LocalAuthentication/LACFSupport.h>
#include <ctkclient.h>
const CFStringRef kSecNetworkExtensionAccessGroupSuffix = CFSTR("com.apple.networkextensionsharing");
static OSStatus osstatus_for_s3e(int s3e)
{
switch (s3e)
{
case SQLITE_OK:
case SQLITE_DONE:
return errSecSuccess;
case SQLITE_READONLY:
return errSecReadOnly;
case SQLITE_CONSTRAINT:
return errSecDuplicateItem;
case SQLITE_ABORT: return -1;
case SQLITE_MISMATCH:
return errSecNoSuchAttr;
case SQLITE_NOMEM:
return errSecAllocate;
case SQLITE_IOERR:
return errSecIO;
case SQLITE_INTERNAL:
return errSecInternalComponent;
case SQLITE_FULL: case SQLITE_PERM: case SQLITE_AUTH: case SQLITE_CANTOPEN: case SQLITE_EMPTY: case SQLITE_ERROR:
default:
return errSecNotAvailable;
}
}
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;
}
}
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_localauthentication_error(CFIndex laError) {
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) {
switch (ctkError) {
case kTKErrorCodeBadParameter:
return errSecParam;
case kTKErrorCodeNotImplemented:
return errSecUnimplemented;
case kTKErrorCodeCanceledByUser:
return errSecUserCanceled;
case kTKErrorCodeCorruptedData:
return errSecDecode;
default:
return errSecInternal;
}
}
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(CFSTR(kLAErrorDomain), domain)) {
status = osstatus_for_localauthentication_error(CFErrorGetCode(error));
} else if (CFEqual(CFSTR(kTKErrorDomain), domain)) {
status = osstatus_for_ctk_error(CFErrorGetCode(error));
} else if (CFEqual(kSOSErrorDomain, domain)) {
status = errSecInternal;
} else {
secnotice("securityd", "unknown error domain: %@ for error: %@", domain, error);
status = errSecInternal;
}
}
return status;
}
static void
lastErrorReleaseError(void *value)
{
if (value)
CFRelease(value);
}
static bool
getLastErrorKey(pthread_key_t *kv)
{
static pthread_key_t key;
static bool haveKey = false;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (pthread_key_create(&key, lastErrorReleaseError) == 0)
haveKey = true;
});
*kv = key;
return haveKey;
}
static void
SetLastError(CFErrorRef newError)
{
pthread_key_t key;
if (!getLastErrorKey(&key))
return;
CFErrorRef oldError = pthread_getspecific(key);
if (oldError)
CFRelease(oldError);
if (newError)
CFRetain(newError);
pthread_setspecific(key, newError);
}
CFErrorRef
SecCopyLastError(OSStatus status)
{
pthread_key_t key;
CFErrorRef error;
if (!getLastErrorKey(&key))
return NULL;
error = pthread_getspecific(key);
if (error) {
if (status && status != SecErrorGetOSStatus(error)) {
error = NULL;
} else {
CFRetain(error);
}
}
return error;
}
OSStatus SecOSStatusWith(bool (^perform)(CFErrorRef *error)) {
CFErrorRef error = NULL;
OSStatus status;
if (perform(&error)) {
assert(error == NULL);
SetLastError(NULL);
status = errSecSuccess;
} else {
assert(error);
SetLastError(error);
status = SecErrorGetOSStatus(error);
if (status != errSecItemNotFound) secinfo("OSStatus", "error:[%" PRIdOSStatus "] %@", status, error);
CFReleaseNull(error);
}
return status;
}
static CFDictionaryRef
AttributeCreateFilteredOutSecAttrs(CFDictionaryRef attributes)
{
CFMutableDictionaryRef filtered = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, attributes);
if (filtered == NULL)
return NULL;
CFDictionaryRemoveValue(filtered, kSecAttrCanSign);
CFDictionaryRemoveValue(filtered, kSecAttrCanVerify);
CFDictionaryRemoveValue(filtered, kSecAttrCanEncrypt);
CFDictionaryRemoveValue(filtered, kSecAttrCanDecrypt);
CFDictionaryRemoveValue(filtered, kSecAttrCanDerive);
CFDictionaryRemoveValue(filtered, kSecAttrCanWrap);
CFDictionaryRemoveValue(filtered, kSecAttrCanUnwrap);
CFDictionaryRemoveValue(filtered, kSecAttrCanSignRecover);
CFDictionaryRemoveValue(filtered, kSecAttrCanVerifyRecover);
CFDictionaryRemoveValue(filtered, kSecAttrIsPermanent);
return filtered;
}
static CFDictionaryRef
SecItemCopyAttributeDictionary(CFTypeRef ref, bool forQuery) {
CFDictionaryRef refDictionary = NULL;
CFTypeID typeID = CFGetTypeID(ref);
if (typeID == SecKeyGetTypeID()) {
refDictionary = SecKeyCopyAttributeDictionary((SecKeyRef)ref);
if (refDictionary && forQuery) {
CFDictionaryRef filtered = AttributeCreateFilteredOutSecAttrs(refDictionary);
CFAssignRetained(refDictionary, filtered);
}
} 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, kSecAttrIdentityCertificateData, data);
}
CFReleaseNull(key_dict);
CFReleaseNull(data);
}
CFReleaseNull(cert);
CFReleaseNull(key);
}
return refDictionary;
}
#ifdef SECITEM_SHIM_OSX
extern CFTypeRef SecItemCreateFromAttributeDictionary_osx(CFDictionaryRef refAttributes);
#endif
static CFTypeRef
SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes) {
CFTypeRef ref = NULL;
CFStringRef class = CFDictionaryGetValue(refAttributes, kSecClass);
if (CFEqual(class, kSecClassKey)) {
ref = SecKeyCreateFromAttributeDictionary(refAttributes);
} else if (CFEqual(class, kSecClassCertificate)) {
ref = SecCertificateCreateFromAttributeDictionary(refAttributes);
} else if (CFEqual(class, kSecClassIdentity)) {
CFDataRef data = CFDictionaryGetValue(refAttributes, kSecAttrIdentityCertificateData);
SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, data);
SecKeyRef key = SecKeyCreateFromAttributeDictionary(refAttributes);
if (key && cert) {
ref = SecIdentityCreate(kCFAllocatorDefault, cert, key);
}
else {
secerror("SecItem: failed to create identity");
}
CFReleaseSafe(key);
CFReleaseSafe(cert);
#ifdef SECITEM_SHIM_OSX
} else {
ref = SecItemCreateFromAttributeDictionary_osx(refAttributes);
#endif
}
return ref;
}
#if !TARGET_OS_OSX
OSStatus
SecItemCopyDisplayNames(CFArrayRef items, CFArrayRef *displayNames)
{
return -1 ;
}
#endif // TARGET_OS_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;
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 (operation == (secitem_operation)SecItemDelete) {
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) {
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;
}
if (result) {
if (!status) {
sqlite_int64 rowid;
CFDictionaryRef tokenAttrs = NULL;
if (_SecItemParsePersistentRef(result, NULL, &rowid, &tokenAttrs)) {
*return_result = _SecItemCreatePersistentRef(kSecClassIdentity, rowid, tokenAttrs);
}
CFReleaseNull(tokenAttrs);
}
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 bool
SecErrorPropagateLastError(OSStatus status, CFErrorRef *error)
{
if (status) {
CFErrorRef lastError = SecCopyLastError(status);
if (lastError)
CFErrorPropagate(lastError, error);
else
SecError(status, error, CFSTR("SecError: error not captured, OSStatus was: %d"), (int)status);
return false;
}
return true;
}
static bool
handleUpdateIdentity(CFDictionaryRef query,
CFDictionaryRef update,
bool *result,
CFErrorRef *error)
{
CFMutableDictionaryRef updatedQuery = NULL;
SecCertificateRef cert = NULL;
SecKeyRef key = NULL;
bool handled = false;
*result = false;
CFTypeRef value = CFDictionaryGetValue(query, kSecValueRef);
if (value) {
CFTypeID typeID = CFGetTypeID(value);
if (typeID == SecIdentityGetTypeID()) {
SecIdentityRef identity = (SecIdentityRef)value;
OSStatus status;
handled = true;
status = SecIdentityCopyCertificate(identity, &cert);
require_noerr_action_quiet(status, errOut, SecErrorPropagateLastError(status, error));
status = SecIdentityCopyPrivateKey(identity, &key);
require_noerr_action_quiet(status, errOut, SecErrorPropagateLastError(status, error));
updatedQuery = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query);
require_action_quiet(updatedQuery, errOut, *result = false);
CFDictionarySetValue(updatedQuery, kSecValueRef, cert);
require_quiet(SecItemUpdateWithError(updatedQuery, update, error), errOut);
CFDictionarySetValue(updatedQuery, kSecValueRef, key);
require_quiet(SecItemUpdateWithError(updatedQuery, update, error), errOut);
}
} else {
value = CFDictionaryGetValue(query, kSecClass);
if (value && CFEqual(kSecClassIdentity, value)) {
handled = true;
updatedQuery = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query);
require_action_quiet(updatedQuery, errOut, *result = false);
CFDictionarySetValue(updatedQuery, kSecClass, kSecClassCertificate);
require_quiet(SecItemUpdateWithError(updatedQuery, update, error), errOut);
CFDictionarySetValue(updatedQuery, kSecClass, kSecClassKey);
require_quiet(SecItemUpdateWithError(updatedQuery, update, error), errOut);
CFReleaseNull(updatedQuery);
}
}
*result = true;
errOut:
CFReleaseNull(updatedQuery);
CFReleaseNull(cert);
CFReleaseNull(key);
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);
}
}
}
}
static CFDataRef CreateTokenPersistentRefData(CFTypeRef class, CFDictionaryRef attributes)
{
CFDataRef tokenPersistentRef = NULL;
CFStringRef tokenId = CFDictionaryGetValue(attributes, kSecAttrTokenID);
CFDictionaryRef itemValue = NULL;
if (CFEqual(class, kSecClassIdentity)) {
itemValue = SecTokenItemValueCopy(CFDictionaryGetValue(attributes, kSecAttrIdentityCertificateData), NULL);
} else {
itemValue = SecTokenItemValueCopy(CFDictionaryGetValue(attributes, kSecValueData), NULL);
}
require(itemValue, out);
CFDataRef oid = CFDictionaryGetValue(itemValue, kSecTokenValueObjectIDKey);
require(oid, 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'};
CFDataRef _SecItemCreatePersistentRef(CFTypeRef class, sqlite_int64 rowid, CFDictionaryRef attributes)
{
CFDataRef result = NULL;
if (attributes && CFDictionaryContainsKey(attributes, CFEqual(class, kSecClassIdentity) ? kSecAttrIdentityCertificateTokenID : kSecAttrTokenID)) {
CFDataRef tokenPersistentRef = CreateTokenPersistentRefData(class, attributes);
require(tokenPersistentRef, out);
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 ,
kCFStringEncodingUTF8))
{
OSWriteBigInt64(bytes + 4, 0, rowid);
result = CFDataCreate(NULL, bytes, sizeof(bytes));
}
}
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, kCFPropertyListImmutable, &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;
}
bool _SecItemParsePersistentRef(CFDataRef persistent_ref, CFStringRef *return_class, sqlite_int64 *return_rowid, CFDictionaryRef *return_token_attrs)
{
bool valid_ref = false;
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);
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 && CFBooleanGetValue(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;
}
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;
}
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, SecCFDictionaryCOW *auth_params, CFErrorRef *error) {
CFMutableDictionaryRef token_attrs = NULL;
TKTokenRef token = NULL;
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);
CFDictionaryRemoveValue(token_attrs, kSecUseAuthenticationContext);
token = TKTokenCreate(token_attrs, error);
CFReleaseSafe(token_attrs);
return token;
}
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) {
if (CFRetainSafe(token) == NULL) {
require_quiet(token = SecTokenCreate(token_id, &auth_params, error), out);
}
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, kSecAttrTokenOID, object_id);
CFRelease(token);
}
*ref = SecItemCreateFromAttributeDictionary(attrs);
ok = true;
out:
CFReleaseSafe(attrs);
CFReleaseSafe(auth_params.mutable_dictionary);
return ok;
}
static bool SecItemResultCopyPrepared(CFTypeRef raw_result, TKTokenRef token,
CFDictionaryRef query, CFDictionaryRef auth_params_dict,
CFTypeRef *result, CFErrorRef *error) {
bool ok = false;
CFDataRef ac_data = NULL;
CFDataRef value = NULL;
CFTypeRef persistent_ref = NULL;
CFStringRef token_id = NULL;
CFStringRef cert_token_id = NULL;
CFDataRef object_id = NULL;
CFMutableDictionaryRef attrs = 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));
bool wants_attributes = cf_bool_value(CFDictionaryGetValue(query, kSecReturnAttributes));
bool wants_persistent_ref = cf_bool_value(CFDictionaryGetValue(query, kSecReturnPersistentRef));
bool token_item = false;
bool cert_token_item = false;
if (token == NULL) {
if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID()) {
token_id = CFDictionaryGetValue(raw_result, kSecAttrTokenID);
token_item = (token_id != NULL);
cert_token_id = CFDictionaryGetValue(raw_result, kSecAttrIdentityCertificateTokenID);
cert_token_item = (cert_token_id != NULL);
}
} else {
token_item = true;
cert_token_item = true;
CFRetain(token);
CFRetainAssign(cert_token, token);
}
if ((token_item || cert_token_item) && cf_bool_value(CFDictionaryGetValue(query, kSecUseTokenRawItems))) {
token_item = false;
cert_token_item = false;
}
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) {
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 || wants_ref) && object_value == NULL) {
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 (!(wants_attributes || wants_persistent_ref || wants_ref)) {
*result = CFRetainSafe(value);
ok = true;
goto out;
}
}
if (wants_persistent_ref) {
if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID())
persistent_ref = CFRetainSafe(CFDictionaryGetValue(raw_result, kSecValuePersistentRef));
else
persistent_ref = CFRetainSafe(raw_result);
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)) {
*result = NULL;
ok = true;
goto out;
}
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)) {
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) {
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 || wants_attributes) {
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 (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) {
CFDictionaryRemoveValue(output, kSecValueData);
}
}
}
ok = true;
out:
CFReleaseSafe(cert_object_id);
CFReleaseSafe(cert_data);
CFReleaseSafe(ac_data);
CFReleaseSafe(value);
CFReleaseSafe(persistent_ref);
CFReleaseSafe(object_id);
CFReleaseSafe(attrs);
CFReleaseSafe(token);
CFReleaseSafe(cert_token);
CFReleaseSafe(auth_params.mutable_dictionary);
return ok;
}
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;
}
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;
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")));
CFDictionaryForEach(ref_attributes, ^(const void *key, const void *value) {
CFDictionaryAddValue(SecCFDictionaryCOWGetMutable(attrs), key, value);
});
CFRelease(ref_attributes);
if (forQuery) {
CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs), kSecAttrTokenOID);
}
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);
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_quiet(acm_context = SecItemAttributesCopyPreparedAuthContext(la_context, error), out);
CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseAuthenticationContext);
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseCredentialReference, acm_context);
}
CFTypeRef policy = CFDictionaryGetValue(attrs->dictionary, kSecMatchPolicy);
if (policy) {
require_action_quiet(CFGetTypeID(policy) == SecPolicyGetTypeID(), out,
SecError(errSecParam, error, CFSTR("unsupported kSecMatchPolicy in query")));
CFTypeRef values[] = { policy };
CFArrayRef policiesArray = CFArrayCreate(kCFAllocatorDefault, values, 1, &kCFTypeArrayCallBacks);
xpc_object_t policiesArrayXPC = SecPolicyArrayCopyXPCArray(policiesArray, error);
CFReleaseSafe(policiesArray);
require_action_quiet(policiesArrayXPC, out,
SecError(errSecInternal, error, CFSTR("Failed to copy XPC policy")));
CFTypeRef objectReadyForXPC = _CFXPCCreateCFObjectFromXPCObject(policiesArrayXPC);
xpc_release(policiesArrayXPC);
require_action_quiet(objectReadyForXPC, out,
SecError(errSecInternal, error, CFSTR("Failed to create CFObject from XPC policy")));
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecMatchPolicy, objectReadyForXPC);
CFRelease(objectReadyForXPC);
}
value = CFDictionaryGetValue(attrs->dictionary, kSecAttrIssuer);
if (value) {
const DERItem name = { (unsigned char *)CFDataGetBytePtr(value), CFDataGetLength(value) };
DERDecodedInfo content;
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);
CFRelease(canonical_issuer);
}
}
}
if (CFDictionaryContainsKey(attrs->dictionary, kSecUseTokenRawItems)) {
CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseTokenRawItems);
}
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, 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)(CFArrayRef *ac_pairs, CFErrorRef *error),
void (^newCredentialRefAdded)(void)) {
bool ok = false;
CFArrayRef ac_pairs = NULL;
SecCFDictionaryCOW auth_options = { NULL };
for (uint32_t i = 0;; ++i) {
SecItemAuthResult auth_result = perform(&ac_pairs, error);
require_quiet(auth_result != kSecItemAuthResultError, out);
require_action_quiet(auth_result == kSecItemAuthResultNeedAuth, out, ok = true);
SecCFDictionaryCOWGetMutable(auth_params);
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);
if (newCredentialRefAdded) {
newCredentialRefAdded();
}
}
}
require_action(i < 20, out, SecItemAuthMaxAttemptsReached(ac_pairs, error));
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);
}
CFStringRef caller_name = CFDictionaryGetValue(auth_params->dictionary, kSecUseCallerName);
if (caller_name != NULL) {
CFNumberRef key = CFNumberCreateWithCFIndex(NULL, kLAOptionCallerName);
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options), key, caller_name);
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);
}
}
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(""))) {
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) {
CFStringRef operation_prompt = CFDictionaryGetValue(query->dictionary, kSecUseOperationPrompt);
if (operation_prompt != NULL) {
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseOperationPrompt, operation_prompt);
CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecUseOperationPrompt);
}
CFStringRef caller_name = CFDictionaryGetValue(query->dictionary, kSecUseCallerName);
if (caller_name != NULL) {
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseCallerName, caller_name);
CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecUseCallerName);
}
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 && *error && CFErrorGetCode(*error) == errSecAuthNeeded && CFEqualSafe(CFErrorGetDomain(*error), kSecErrorDomain)) {
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;
}
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;
__block 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);
}
bool forQuery =
secItemOperation == SecItemCopyMatching ||
secItemOperation == SecItemUpdate ||
secItemOperation == SecItemDelete;
require_quiet(SecItemAttributesPrepare(query, forQuery, error), out);
if (attributes != NULL)
require_quiet(SecItemAttributesPrepare(attributes, false, error), out);
SecItemAuthCopyParams(&auth_params, query);
if (secItemOperation != SecItemCopyMatching) {
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(CFArrayRef *ac_pairs, CFErrorRef *error) {
SecItemAuthResult result = kSecItemAuthResultError;
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.dictionary, kSecAttrAccessControl);
if (acl_data_ref != NULL) {
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes ?: query), kSecAttrAccessControl, acl_data_ref);
}
}
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.dictionary, error)) {
require_quiet((result = SecItemCreatePairsFromError(error, ac_pairs)) == kSecItemAuthResultOK, out);
}
result = kSecItemAuthResultOK;
out:
return result;
}, NULL);
require_quiet(ok, out);
ok = true;
out:
CFReleaseSafe(token);
CFReleaseSafe(auth_params.mutable_dictionary);
return ok;
}
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_client_to_bool_cftype_error_request(enum SecXPCOperation op, CFTypeRef attributes, __unused SecurityClient *client, 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 cfstring_array_to_error_request(enum SecXPCOperation op, CFStringRef string, CFArrayRef attributes, __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 (attributes) {
if (!SecXPCDictionarySetPList(message, kSecXPCKeyQuery, attributes, error))
return false;
}
return true;
}, NULL);
}
static bool dict_client_to_error_request(enum SecXPCOperation op, CFDictionaryRef query, __unused SecurityClient *client, 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) {
CFDataRef access_control = TKTokenCopyObjectAccessControl(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;
CFDictionaryRef old_attrs = NULL;
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));
}
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);
}
});
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);
CFDictionaryRemoveValue(attributes, kSecAttrAccessControl);
out:
CFReleaseSafe(ac);
CFReleaseSafe(access_control);
CFReleaseSafe(db_value);
CFReleaseSafe(old_attrs);
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;
CFDataRef db_value = NULL;
CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, attributes);
CFDictionarySetValue(attrs, kSecAttrAccessible, kSecAttrAccessibleAlwaysPrivate); object_id = CFRetainSafe(CFDictionaryGetValue(attrs, kSecAttrTokenOID));
CFDictionaryRemoveValue(attrs, kSecAttrTokenOID);
require_quiet(CFAssignRetained(object_id, SecTokenCopyUpdatedObjectID(token, object_id, attrs, error)), out);
if (CFDictionaryContainsKey(attrs, kSecValueRef)) {
CFDictionaryRemoveValue(attrs, kSecValueRef);
} else {
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, kSecReturnAttributes, kCFBooleanTrue);
CFDictionarySetValue(attrs, kSecReturnData, kCFBooleanTrue);
if (!CFEqualSafe(CFDictionaryGetValue(attrs, kSecAttrIsPermanent), kCFBooleanFalse)) {
require_quiet(SECURITYD_XPC(sec_item_add, cftype_client_to_bool_cftype_error_request, attrs,
SecSecurityClientGet(), &db_result, error), out);
} else {
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);
CFReleaseSafe(ref);
return ok;
}
OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) {
__block SecCFDictionaryCOW attrs = { attributes };
OSStatus status;
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);
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_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);
return ok;
} else {
return SecTokenItemAdd(token, attributes, auth_params, result, error);
}
});
});
errOut:
CFReleaseSafe(attrs.mutable_dictionary);
return status;
}
OSStatus SecItemCopyMatching(CFDictionaryRef inQuery, CFTypeRef *result) {
OSStatus status;
__block SecCFDictionaryCOW query = { inQuery };
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)) {
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query), kSecReturnAttributes, 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))
return false;
bool ok = SecItemResultProcess(inQuery, auth_params, NULL, raw_result, result, error);
CFReleaseSafe(raw_result);
return ok;
});
});
errOut:
CFReleaseSafe(query.mutable_dictionary);
return status;
}
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;
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_client_to_bool_cftype_error_request, list_query,
SecSecurityClientGet(), &items, error), out);
if (CFGetTypeID(items) != CFArrayGetTypeID()) {
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) {
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, SecSecurityClientGet(), 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;
CFMutableDictionaryRef attributes = CFDictionaryCreateMutableCopy(NULL, 0, attributesToUpdate);
require_quiet(object_id = SecTokenCopyUpdatedObjectID(token, CFDictionaryGetValue(object_data, kSecTokenValueObjectIDKey),
attributes, error), out);
require_quiet(SecItemRawUpdate(item_query, attributes, error), out);
ok = true;
out:
CFReleaseSafe(object_id);
CFReleaseSafe(attributes);
CFReleaseSafe(db_value);
return ok;
});
}
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);
});
}
bool
SecItemUpdateWithError(CFDictionaryRef inQuery,
CFDictionaryRef inAttributesToUpdate,
CFErrorRef *error)
{
__block SecCFDictionaryCOW query = { inQuery };
__block SecCFDictionaryCOW attributesToUpdate = { inAttributesToUpdate };
bool result = false;
if (handleUpdateIdentity(inQuery, inAttributesToUpdate, &result, error))
goto errOut;
result = 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);
}
});
errOut:
CFReleaseSafe(query.mutable_dictionary);
CFReleaseSafe(attributesToUpdate.mutable_dictionary);
return result;
}
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, 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;
if (item_query == NULL)
return errSecItemNotFound;
CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecValuePersistentRef);
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query), kSecValueRef, item_query);
CFRelease(item_query);
}
return status;
}
OSStatus SecItemDelete(CFDictionaryRef inQuery) {
OSStatus status;
__block SecCFDictionaryCOW query = { inQuery };
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) {
if (token == NULL) {
return SECURITYD_XPC(sec_item_delete, dict_client_to_error_request, query, SecSecurityClientGet(), error);
} else {
return SecTokenItemForEachMatching(query, error, ^bool(CFDictionaryRef object_data, CFDictionaryRef item_query, CFErrorRef *error) {
bool ok = false;
CFDataRef object_id = CFDictionaryGetValue(object_data, kSecTokenValueObjectIDKey);
require_action_quiet(TKTokenDeleteObject(token, object_id, error), out,
SecTokenProcessError(kAKSKeyOpDelete, token, object_id, error));
require_quiet(SECURITYD_XPC(sec_item_delete, dict_client_to_error_request, item_query,
SecSecurityClientGet(), error), out);
ok = true;
out:
return ok;
});
}
});
});
errOut:
CFReleaseSafe(query.mutable_dictionary);
return status;
}
OSStatus
SecItemDeleteAll(void)
{
return SecOSStatusWith(^bool (CFErrorRef *error) {
bool ok = true;
if (gSecurityd) {
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;
});
}
#if 0
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);
}
#endif
bool SecItemDeleteAllWithAccessGroups(CFArrayRef accessGroups, CFErrorRef *error) {
#if 0
os_activity_t activity = os_activity_create("SecItemDeleteAllWithAccessGroups", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_scope(activity);
os_release(activity);
return SECURITYD_XPC(sec_delete_items_with_access_groups, agrps_client_to_error_request, accessGroups,
SecSecurityClientGet(), error);
#else
return true;
#endif
}
OSStatus
#if SECITEM_SHIM_OSX
SecItemUpdateTokenItems_ios(CFTypeRef tokenID, CFArrayRef tokenItemsAttributes)
#else
SecItemUpdateTokenItems(CFTypeRef tokenID, CFArrayRef tokenItemsAttributes)
#endif
{
OSStatus status;
os_activity_t activity = os_activity_create("SecItemDelete_ios", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
os_activity_scope(activity);
os_release(activity);
status = SecOSStatusWith(^bool(CFErrorRef *error) {
CFArrayRef tmpArrayRef = tokenItemsAttributes;
if (tokenItemsAttributes) {
CFMutableArrayRef tokenItems = 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;
}
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);
}
else
CFArrayAppendValue(tokenItems, itemAttributes);
}
tmpArrayRef = tokenItems;
}
return SECURITYD_XPC(sec_item_update_token_items, cfstring_array_to_error_request, tokenID, tmpArrayRef, SecSecurityClientGet(), error);
});
return status;
}
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;
}
#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;
}
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("SecItemCopyParentCertificates_ios", 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;
}