#include <Security/SecBasePriv.h>
#include <Security/SecItem.h>
#include <Security/SecItemPriv.h>
#include <Security/SecItemInternal.h>
#ifndef SECITEM_SHIM_OSX
#include <Security/SecKey.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecCertificateInternal.h>
#include <Security/SecIdentity.h>
#include <Security/SecIdentityPriv.h>
#include <Security/SecRandom.h>
#include <Security/SecBasePriv.h>
#endif // *** END SECITEM_SHIM_OSX ***
#include <Security/SecTask.h>
#include <errno.h>
#include <limits.h>
#include <sqlite3.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <Security/SecBase.h>
#include <CoreFoundation/CFData.h>
#include <CoreFoundation/CFDate.h>
#include <CoreFoundation/CFDictionary.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFURL.h>
#include <CommonCrypto/CommonDigest.h>
#include <libkern/OSByteOrder.h>
#include <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 <assert.h>
#include <Security/SecInternal.h>
#include <TargetConditionals.h>
#include "securityd_client.h"
#include "SecuritydXPC.h"
#include <AssertMacros.h>
#include <asl.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#ifndef SECITEM_SHIM_OSX
#include <libDER/asn1Types.h>
#endif // *** END SECITEM_SHIM_OSX ***
#define CERTIFICATE_DATA_COLUMN_LABEL "certdata"
#include <utilities/SecDb.h>
#include <IOKit/IOReturn.h>
static OSStatus osstatus_for_s3e(int s3e)
{
if (s3e > 0 && s3e <= SQLITE_DONE) switch (s3e)
{
case SQLITE_OK:
return 0;
case SQLITE_ERROR:
return errSecNotAvailable;
case SQLITE_FULL:
return errSecNotAvailable;
case SQLITE_PERM:
case SQLITE_READONLY:
return errSecNotAvailable;
case SQLITE_CANTOPEN:
return errSecNotAvailable;
case SQLITE_EMPTY:
return errSecNotAvailable;
case SQLITE_CONSTRAINT:
return errSecDuplicateItem;
case SQLITE_ABORT:
return -1;
case SQLITE_MISMATCH:
return errSecNoSuchAttr;
case SQLITE_AUTH:
return errSecNotAvailable;
case SQLITE_NOMEM:
return -2;
case SQLITE_INTERNAL:
default:
return errSecNotAvailable;
}
return s3e;
}
static OSStatus osstatus_for_kern_return(CFIndex kernResult)
{
switch (kernResult)
{
case KERN_SUCCESS:
return errSecSuccess;
case kIOReturnNotReadable:
case kIOReturnNotWritable:
return errSecAuthFailed;
case kIOReturnNotPermitted:
case kIOReturnNotPrivileged:
case kIOReturnLockedRead:
case kIOReturnLockedWrite:
return errSecInteractionNotAllowed;
case kIOReturnError:
return errSecDecode;
case kIOReturnBadArgument:
return errSecParam;
default:
return errSecNotAvailable;
}
}
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:
return errSecDecode;
case kSecDERErrorUnsupportedCFObject:
case kSecDERErrorUnsupportedNumberType:
return errSecParam;
case kSecDERErrorAllocationFailure:
case kSecDERErrorUnderlyingError:
return errSecAllocate;
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 {
secerror("unknown error domain: %@ for error: %@", domain, error);
status = errSecInternal;
}
}
return status;
}
OSStatus SecOSStatusWith(bool (^perform)(CFErrorRef *error)) {
CFErrorRef error = NULL;
OSStatus status;
if (perform(&error)) {
assert(error == NULL);
status = errSecSuccess;
} else {
assert(error);
status = SecErrorGetOSStatus(error);
if (status != errSecItemNotFound) secerror("error:[%" PRIdOSStatus "] %@", status, error);
CFReleaseNull(error);
}
return status;
}
#ifndef SECITEM_SHIM_OSX
static CFDictionaryRef
SecItemCopyAttributeDictionary(CFTypeRef ref) {
CFDictionaryRef refDictionary = NULL;
CFTypeID typeID = CFGetTypeID(ref);
if (typeID == SecKeyGetTypeID()) {
refDictionary = SecKeyCopyAttributeDictionary((SecKeyRef)ref);
} else if (typeID == SecCertificateGetTypeID()) {
refDictionary =
SecCertificateCopyAttributeDictionary((SecCertificateRef)ref);
} else if (typeID == SecIdentityGetTypeID()) {
assert(false);
SecIdentityRef identity = (SecIdentityRef)ref;
SecCertificateRef cert = NULL;
SecKeyRef key = NULL;
if (!SecIdentityCopyCertificate(identity, &cert) &&
!SecIdentityCopyPrivateKey(identity, &key))
{
CFDataRef data = SecCertificateCopyData(cert);
CFDictionaryRef key_dict = SecKeyCopyAttributeDictionary(key);
if (key_dict && data) {
refDictionary = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, key_dict);
CFDictionarySetValue((CFMutableDictionaryRef)refDictionary,
CFSTR(CERTIFICATE_DATA_COLUMN_LABEL), data);
}
CFReleaseNull(key_dict);
CFReleaseNull(data);
}
CFReleaseNull(cert);
CFReleaseNull(key);
} else {
refDictionary = NULL;
}
return refDictionary;
}
static CFTypeRef
SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes) {
CFTypeRef ref = NULL;
CFStringRef class = CFDictionaryGetValue(refAttributes, kSecClass);
if (CFEqual(class, kSecClassCertificate)) {
ref = SecCertificateCreateFromAttributeDictionary(refAttributes);
} else if (CFEqual(class, kSecClassKey)) {
ref = SecKeyCreateFromAttributeDictionary(refAttributes);
} else if (CFEqual(class, kSecClassIdentity)) {
CFAllocatorRef allocator = NULL;
CFDataRef data = CFDictionaryGetValue(refAttributes, CFSTR(CERTIFICATE_DATA_COLUMN_LABEL));
SecCertificateRef cert = SecCertificateCreateWithData(allocator, data);
SecKeyRef key = SecKeyCreateFromAttributeDictionary(refAttributes);
if (key && cert)
ref = SecIdentityCreate(allocator, cert, key);
CFReleaseSafe(cert);
CFReleaseSafe(key);
#if 0
} else if (CFEqual(class, kSecClassGenericPassword)) {
} else if (CFEqual(class, kSecClassInternetPassword)) {
} else if (CFEqual(class, kSecClassAppleSharePassword)) {
#endif
} else {
ref = NULL;
}
return ref;
}
#else
extern CFTypeRef SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes);
#endif
static CFTypeRef makeRef(CFTypeRef ref, bool return_data, bool return_attributes)
{
CFTypeRef result = NULL;
if (!ref || (CFGetTypeID(ref) != CFDictionaryGetTypeID()))
return NULL;
CFTypeRef return_ref = SecItemCreateFromAttributeDictionary(ref);
if (return_data || return_attributes) {
if (return_attributes)
result = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, ref);
else
result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (return_ref) {
CFDictionarySetValue((CFMutableDictionaryRef)result, kSecValueRef, return_ref);
CFRelease(return_ref);
}
if (!return_data)
CFDictionaryRemoveValue((CFMutableDictionaryRef)result, kSecValueData);
} else
result = return_ref;
return result;
}
OSStatus
SecItemCopyDisplayNames(CFArrayRef items, CFArrayRef *displayNames)
{
return -1 ;
}
#ifndef SECITEM_SHIM_OSX
static void merge_dictionary_by_overwrite(const void *key, const void *value, void *context)
{
if (!CFEqual(key, kSecValueRef))
CFDictionarySetValue((CFMutableDictionaryRef)context, key, value);
}
static void copy_applier(const void *key, const void *value, void *context)
{
CFDictionarySetValue(context, key, value);
}
static OSStatus cook_query(CFDictionaryRef query, CFMutableDictionaryRef *explode)
{
CFMutableDictionaryRef args = NULL;
CFTypeRef value = CFDictionaryGetValue(query, kSecValueRef);
if (value) {
CFDictionaryRef refAttributes = SecItemCopyAttributeDictionary(value);
if (!refAttributes)
return errSecValueRefUnsupported;
args = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, refAttributes);
CFRelease(refAttributes);
CFDictionaryApplyFunction(query, merge_dictionary_by_overwrite, args);
}
value = CFDictionaryGetValue(query, kSecAttrIssuer);
if (value) {
const DERItem name = { (unsigned char *)CFDataGetBytePtr(value), CFDataGetLength(value) };
DERDecodedInfo content;
if (!DERDecodeItem(&name, &content) &&
(content.tag == ASN1_CONSTR_SEQUENCE))
{
CFDataRef canonical_issuer = createNormalizedX501Name(kCFAllocatorDefault, &content.content);
if (canonical_issuer) {
if (!args) {
args = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryApplyFunction(query, copy_applier, args);
}
CFDictionarySetValue(args, kSecAttrIssuer, canonical_issuer);
CFRelease(canonical_issuer);
}
}
}
*explode = args;
return errSecSuccess;
}
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;
if (_SecItemParsePersistentRef(result, NULL, &rowid)) {
*return_result = _SecItemMakePersistentRef(kSecClassIdentity, rowid);
}
}
CFRelease(result);
}
}
CFReleaseNull(partial_query);
}
else
status = errSecInvalidItemRef;
CFReleaseNull(cert);
CFReleaseNull(key);
*return_status = status;
}
} else {
value = CFDictionaryGetValue(attributes, kSecClass);
if (value && CFEqual(kSecClassIdentity, value) &&
(operation == (secitem_operation)SecItemDelete)) {
CFMutableDictionaryRef dict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, attributes);
CFDictionaryRemoveValue(dict, kSecClass);
CFDictionarySetValue(dict, kSecClass, kSecClassCertificate);
OSStatus status = SecItemDelete(dict);
if (!status) {
CFDictionarySetValue(dict, kSecClass, kSecClassKey);
status = SecItemDelete(dict);
}
CFRelease(dict);
*return_status = status;
handled = true;
}
}
return handled;
}
static void infer_cert_label(CFDictionaryRef attributes, CFMutableDictionaryRef args)
{
if (!args || !attributes)
return;
if (CFDictionaryContainsKey(attributes, kSecAttrLabel))
return;
CFTypeRef value_ref = CFDictionaryGetValue(attributes, kSecValueRef);
if (!value_ref || (CFGetTypeID(value_ref) != SecCertificateGetTypeID()))
return;
SecCertificateRef certificate = (SecCertificateRef)value_ref;
CFStringRef label = SecCertificateCopySubjectSummary(certificate);
if (label) {
CFDictionarySetValue(args, kSecAttrLabel, label);
CFReleaseNull(label);
}
}
CF_RETURNS_RETAINED CFDataRef _SecItemMakePersistentRef(CFTypeRef class, sqlite_int64 rowid)
{
uint8_t bytes[sizeof(sqlite_int64) + 4];
if (rowid < 0)
return NULL;
if (CFStringGetCString(class, (char *)bytes, 4 + 1 ,
kCFStringEncodingUTF8))
{
OSWriteBigInt64(bytes + 4, 0, rowid);
return CFDataCreate(NULL, bytes, sizeof(bytes));
}
return NULL;
}
bool _SecItemParsePersistentRef(CFDataRef persistent_ref, CFStringRef *return_class, sqlite_int64 *return_rowid)
{
bool valid_ref = false;
if (CFGetTypeID(persistent_ref) == CFDataGetTypeID() &&
CFDataGetLength(persistent_ref) == (CFIndex)(sizeof(sqlite_int64) + 4)) {
const uint8_t *bytes = CFDataGetBytePtr(persistent_ref);
sqlite_int64 rowid = OSReadBigInt64(bytes + 4, 0);
CFStringRef class = CFStringCreateWithBytes(kCFAllocatorDefault,
bytes, CFStringGetLength(kSecClassGenericPassword),
kCFStringEncodingUTF8, true);
const void *valid_classes[] = { kSecClassGenericPassword,
kSecClassInternetPassword,
kSecClassAppleSharePassword,
kSecClassCertificate,
kSecClassKey,
kSecClassIdentity };
unsigned i;
for (i=0; i< array_size(valid_classes); i++) {
if (CFEqual(valid_classes[i], class)) {
if (return_class)
*return_class = valid_classes[i];
if (return_rowid)
*return_rowid = rowid;
valid_ref = true;
break;
}
}
CFRelease(class);
}
return valid_ref;
}
#endif // *** END SECITEM_SHIM_OSX ***
static bool cf_bool_value(CFTypeRef cf_bool)
{
return (cf_bool && CFEqual(kCFBooleanTrue, cf_bool));
}
static void
result_post(CFDictionaryRef query, CFTypeRef raw_result, CFTypeRef *result) {
if (!raw_result)
return;
if (!result) {
CFRelease(raw_result);
return;
}
bool return_ref = cf_bool_value(CFDictionaryGetValue(query, kSecReturnRef));
bool return_data = cf_bool_value(CFDictionaryGetValue(query, kSecReturnData));
bool return_attributes = cf_bool_value(CFDictionaryGetValue(query, kSecReturnAttributes));
if (return_ref) {
if (CFGetTypeID(raw_result) == CFArrayGetTypeID()) {
CFIndex i, count = CFArrayGetCount(raw_result);
CFMutableArrayRef tmp_array = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks);
for (i = 0; i < count; i++) {
CFTypeRef ref = makeRef(CFArrayGetValueAtIndex(raw_result, i), return_data, return_attributes);
if (ref) {
CFArrayAppendValue(tmp_array, ref);
CFRelease(ref);
}
}
*result = tmp_array;
} else
*result = makeRef(raw_result, return_data, return_attributes);
CFRelease(raw_result);
} else
*result = raw_result;
}
#if SECITEM_SHIM_OSX
OSStatus SecItemAdd_ios(CFDictionaryRef attributes, CFTypeRef *result);
OSStatus SecItemCopyMatching_ios(CFDictionaryRef query, CFTypeRef *result);
OSStatus SecItemUpdate_ios(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
OSStatus SecItemDelete_ios(CFDictionaryRef query);
#endif
static bool cftype_to_bool_cftype_error_request(enum SecXPCOperation op, CFTypeRef attributes, CFTypeRef *result, CFErrorRef *error)
{
return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
return SecXPCDictionarySetPList(message, kSecXPCKeyQuery, attributes, error);
}, ^bool(xpc_object_t response, CFErrorRef *error) {
if (result) {
return SecXPCDictionaryCopyPListOptional(response, kSecXPCKeyResult, result, error);
}
return true;
});
}
static bool cftype_ag_to_bool_cftype_error_request(enum SecXPCOperation op, CFTypeRef attributes, __unused CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error) {
return cftype_to_bool_cftype_error_request(op, attributes, result, error);
}
static bool dict_to_error_request(enum SecXPCOperation op, CFDictionaryRef query, CFErrorRef *error)
{
return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
return SecXPCDictionarySetPList(message, kSecXPCKeyQuery, query, error);
}, NULL);
}
static bool dict_ag_to_error_request(enum SecXPCOperation op, CFDictionaryRef query, __unused CFArrayRef accessGroups, CFErrorRef *error)
{
return dict_to_error_request(op, query, error);
}
static CFDataRef data_data_to_data_error_request(enum SecXPCOperation op, CFDataRef keybag, CFDataRef passcode, CFErrorRef *error) {
__block CFDataRef result = NULL;
securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
return SecXPCDictionarySetDataOptional(message, kSecXPCKeyKeybag, keybag, error)
&& SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
}, ^bool(xpc_object_t response, CFErrorRef *error) {
return (result = SecXPCDictionaryCopyData(response, kSecXPCKeyResult, error));
});
return result;
}
static bool data_data_data_to_error_request(enum SecXPCOperation op, CFDataRef backup, CFDataRef keybag, CFDataRef passcode, CFErrorRef *error) {
return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
return SecXPCDictionarySetData(message, kSecXPCKeyBackup, backup, error)
&& SecXPCDictionarySetData(message, kSecXPCKeyKeybag, keybag, error)
&& SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
} , NULL);
}
static bool dict_data_data_to_error_request(enum SecXPCOperation op, CFDictionaryRef backup, CFDataRef keybag, CFDataRef passcode, CFErrorRef *error) {
return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
return SecXPCDictionarySetPList(message, kSecXPCKeyBackup, backup, error)
&& SecXPCDictionarySetData(message, kSecXPCKeyKeybag, keybag, error)
&& SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
} , NULL);
}
static CFDictionaryRef data_data_dict_to_dict_error_request(enum SecXPCOperation op, CFDictionaryRef backup, CFDataRef keybag, CFDataRef passcode, CFErrorRef *error) {
__block CFDictionaryRef dict = NULL;
securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
return SecXPCDictionarySetPListOptional(message, kSecXPCKeyBackup, backup, error)
&& SecXPCDictionarySetData(message, kSecXPCKeyKeybag, keybag, error)
&& SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
}, ^bool(xpc_object_t response, CFErrorRef *error) {
return (dict = SecXPCDictionaryCopyDictionary(response, kSecXPCKeyResult, error));
});
return dict;
}
OSStatus
#if SECITEM_SHIM_OSX
SecItemAdd_ios(CFDictionaryRef attributes, CFTypeRef *result)
#else
SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result)
#endif // *** END SECITEM_SHIM_OSX ***
{
CFMutableDictionaryRef args = NULL;
OSStatus status;
#ifndef SECITEM_SHIM_OSX
require_quiet(!explode_identity(attributes, (secitem_operation)SecItemAdd, &status, result), errOut);
require_noerr_quiet(status = cook_query(attributes, &args), errOut);
infer_cert_label(attributes, args);
#endif // *** END SECITEM_SHIM_OSX ***
if (args)
attributes = args;
status = SecOSStatusWith(^bool (CFErrorRef *error) {
CFTypeRef raw_result = NULL;
if (!SECURITYD_XPC(sec_item_add, cftype_ag_to_bool_cftype_error_request, attributes, SecAccessGroupsGetCurrent(), &raw_result, error))
return false;
result_post(attributes, raw_result, result);
return true;
});
#ifndef SECITEM_SHIM_OSX
errOut:
#endif // *** END SECITEM_SHIM_OSX ***
CFReleaseSafe(args);
return status;
}
OSStatus
#if SECITEM_SHIM_OSX
SecItemCopyMatching_ios(CFDictionaryRef inQuery, CFTypeRef *result)
#else
SecItemCopyMatching(CFDictionaryRef inQuery, CFTypeRef *result)
#endif // *** END SECITEM_SHIM_OSX ***
{
__block CFDictionaryRef query = inQuery;
__block CFMutableDictionaryRef args = NULL;
OSStatus status;
#ifndef SECITEM_SHIM_OSX
require_quiet(!explode_identity(query, (secitem_operation)SecItemCopyMatching, &status, result), errOut);
require_noerr_quiet(status = cook_query(query, &args), errOut);
#endif // *** END SECITEM_SHIM_OSX ***
if (args)
query = args;
status = SecOSStatusWith(^bool (CFErrorRef *error) {
CFTypeRef raw_result = NULL;
if (!SECURITYD_XPC(sec_item_copy_matching, cftype_ag_to_bool_cftype_error_request, query, SecAccessGroupsGetCurrent(), &raw_result, error))
return false;
#ifdef SECITEM_SHIM_OSX
if ((!cf_bool_value(CFDictionaryGetValue(query, kSecReturnPersistentRef))) && cf_bool_value(CFDictionaryGetValue(query, kSecReturnRef))) {
CFReleaseSafe(args);
args = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query);
CFDictionaryAddValue(args, kSecReturnPersistentRef, kCFBooleanTrue);
query = args;
}
#endif
result_post(query, raw_result, result);
return true;
});
#ifndef SECITEM_SHIM_OSX
errOut:
#endif // *** END SECITEM_SHIM_OSX ***
CFReleaseSafe(args);
return status;
}
OSStatus
#if SECITEM_SHIM_OSX
SecItemUpdate_ios(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
#else
SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
#endif // *** END SECITEM_SHIM_OSX ***
{
CFMutableDictionaryRef args = NULL;
__block OSStatus status; #ifndef SECITEM_SHIM_OSX
require_noerr_quiet(status = cook_query(query, &args), errOut);
#endif
if (args)
query = args;
status = SecOSStatusWith(^bool (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, SecAccessGroupsGetCurrent(), error);
CFRelease(tmp);
} else {
xpc_object_t message = securityd_create_message(sec_item_update_id, error);
if (message) {
if (SecXPCDictionarySetPList(message, kSecXPCKeyQuery, query, error) &&
SecXPCDictionarySetPList(message, kSecXPCKeyAttributesToUpdate, attributesToUpdate, error)) {
xpc_object_t reply = securityd_message_with_reply_sync(message, error);
if (reply) {
ok = securityd_message_no_error(reply, error);
xpc_release(reply);
}
}
xpc_release(message);
}
}
return ok;
});
#ifndef SECITEM_SHIM_OSX
errOut:
#endif
CFReleaseSafe(args);
return status;
}
#ifndef SECITEM_SHIM_OSX
static void copy_all_keys_and_values(const void *key, const void *value, void *context)
{
CFDictionaryAddValue((CFMutableDictionaryRef)context, key, value);
}
#endif
#ifndef SECITEM_SHIM_OSX
static OSStatus explode_persistent_identity_ref(CFDictionaryRef query, CFMutableDictionaryRef *delete_query)
{
OSStatus status = errSecSuccess;
CFTypeRef persist = CFDictionaryGetValue(query, kSecValuePersistentRef);
CFStringRef class;
if (persist && _SecItemParsePersistentRef(persist, &class, NULL)
&& CFEqual(class, kSecClassIdentity)) {
const void *keys[] = { kSecReturnRef, kSecValuePersistentRef };
const void *vals[] = { kCFBooleanTrue, persist };
CFDictionaryRef persistent_query = CFDictionaryCreate(NULL, keys,
vals, (array_size(keys)), NULL, NULL);
CFTypeRef item_query = NULL;
status = SecItemCopyMatching(persistent_query, &item_query);
CFReleaseNull(persistent_query);
if (status)
return status;
CFMutableDictionaryRef new_query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (new_query) {
CFDictionaryApplyFunction(query, copy_all_keys_and_values, new_query);
CFDictionaryRemoveValue(new_query, kSecValuePersistentRef);
CFDictionarySetValue(new_query, kSecValueRef, item_query);
*delete_query = new_query;
} else
status = errSecAllocate;
CFRelease(item_query);
}
return status;
}
#endif
OSStatus
#if SECITEM_SHIM_OSX
SecItemDelete_ios(CFDictionaryRef query)
#else
SecItemDelete(CFDictionaryRef query)
#endif // *** END SECITEM_SHIM_OSX ***
{
CFMutableDictionaryRef args1 = NULL, args2 = NULL;
OSStatus status;
#ifndef SECITEM_SHIM_OSX
require_noerr_quiet(status = explode_persistent_identity_ref(query, &args1), errOut);
if (args1)
query = args1;
require_quiet(!explode_identity(query, (secitem_operation)SecItemDelete, &status, NULL), errOut);
require_noerr_quiet(status = cook_query(query, &args2), errOut);
#endif // *** END SECITEM_SHIM_OSX ***
if (args2)
query = args2;
status = SecOSStatusWith(^bool (CFErrorRef *error) {
return SECURITYD_XPC(sec_item_delete, dict_ag_to_error_request, query, SecAccessGroupsGetCurrent(), error);
});
#ifndef SECITEM_SHIM_OSX
errOut:
#endif // *** END SECITEM_SHIM_OSX ***
CFReleaseSafe(args1);
CFReleaseSafe(args2);
return status;
}
OSStatus
SecItemDeleteAll(void)
{
return SecOSStatusWith(^bool (CFErrorRef *error) {
bool ok;
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"));
ok = true;
} else {
ok = securityd_send_sync_and_do(sec_delete_all_id, error, NULL, NULL);
}
return ok;
});
}
CFDataRef _SecKeychainCopyOTABackup(void) {
return SECURITYD_XPC(sec_keychain_backup, data_data_to_data_error_request, NULL, NULL, NULL);
}
CFDataRef _SecKeychainCopyBackup(CFDataRef backupKeybag, CFDataRef password) {
return SECURITYD_XPC(sec_keychain_backup, data_data_to_data_error_request, backupKeybag, password, NULL);
}
OSStatus _SecKeychainRestoreBackup(CFDataRef backup, CFDataRef backupKeybag,
CFDataRef password) {
return SecOSStatusWith(^bool (CFErrorRef *error) {
return SECURITYD_XPC(sec_keychain_restore, data_data_data_to_error_request, backup, backupKeybag, password, error);
});
}
bool _SecKeychainSyncUpdate(CFDictionaryRef updates, CFErrorRef *error) {
return SECURITYD_XPC(sec_keychain_sync_update, dict_to_error_request, updates, error);
}
OSStatus _SecKeychainBackupSyncable(CFDataRef keybag, CFDataRef password, CFDictionaryRef backup_in, CFDictionaryRef *backup_out)
{
return SecOSStatusWith(^bool (CFErrorRef *error) {
*backup_out = SECURITYD_XPC(sec_keychain_backup_syncable, data_data_dict_to_dict_error_request, backup_in, keybag, password, error);
return *backup_out != NULL;
});
}
OSStatus _SecKeychainRestoreSyncable(CFDataRef keybag, CFDataRef password, CFDictionaryRef backup_in)
{
return SecOSStatusWith(^bool (CFErrorRef *error) {
return SECURITYD_XPC(sec_keychain_restore_syncable, dict_data_data_to_error_request, backup_in, keybag, password, error);
});
}
#ifndef SECITEM_SHIM_OSX
OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement);
OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement)
{
return -1;
}
#else
extern OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement);
#endif