2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecItem.c - CoreFoundation-based constants and functions for
26 access to Security items (certificates, keys, identities, and
30 #include <Security/SecBasePriv.h>
31 #include <Security/SecItem.h>
32 #include <Security/SecItemPriv.h>
33 #include <Security/SecItemInternal.h>
34 #include <Security/SecItemShim.h>
35 #include <Security/SecAccessControl.h>
36 #include <Security/SecAccessControlPriv.h>
37 #include <Security/SecKey.h>
38 #include <Security/SecKeyPriv.h>
39 #include <Security/SecCertificateInternal.h>
40 #include <Security/SecIdentity.h>
41 #include <Security/SecIdentityPriv.h>
42 #include <Security/SecRandom.h>
43 #include <Security/SecBasePriv.h>
44 #include <Security/SecCTKKeyPriv.h>
45 #include <Security/SecTask.h>
46 #include <Security/SecPolicyInternal.h>
53 #include <sys/param.h>
55 #include <Security/SecBase.h>
56 #include <CoreFoundation/CFData.h>
57 #include <CoreFoundation/CFDate.h>
58 #include <CoreFoundation/CFDictionary.h>
59 #include <CoreFoundation/CFNumber.h>
60 #include <CoreFoundation/CFString.h>
61 #include <CoreFoundation/CFURL.h>
62 #include <CommonCrypto/CommonDigest.h>
63 #include <libkern/OSByteOrder.h>
64 #include <corecrypto/ccder.h>
65 #include <utilities/array_size.h>
66 #include <utilities/debugging.h>
67 #include <utilities/SecCFError.h>
68 #include <utilities/SecCFWrappers.h>
69 #include <utilities/SecIOFormat.h>
70 #include <utilities/SecXPCError.h>
71 #include <utilities/der_plist.h>
72 #include <utilities/der_plist_internal.h>
75 #include <libaks_acl_cf_keys.h>
76 #include <os/activity.h>
80 #include <Security/SecInternal.h>
81 #include "keychain/SecureObjectSync/SOSInternal.h"
82 #include <TargetConditionals.h>
83 #include <ipc/securityd_client.h>
84 #include <Security/SecuritydXPC.h>
85 #include <AssertMacros.h>
87 #include <sys/types.h>
91 #include <libDER/asn1Types.h>
93 #include <utilities/SecDb.h>
94 #include <IOKit/IOReturn.h>
96 #include <coreauthd_spi.h>
97 #include <LocalAuthentication/LAPrivateDefines.h>
98 #include <LocalAuthentication/LACFSupport.h>
100 #include <ctkclient/ctkclient.h>
103 * See corresponding definition in SecDbKeychainItemV7. This is the unserialized
104 * maximum, so the daemon's limit is not exactly the same.
106 #define REASONABLE_DATA_SIZE 4096
108 const CFStringRef kSecNetworkExtensionAccessGroupSuffix
= CFSTR("com.apple.networkextensionsharing");
110 /* Return an OSStatus for a sqlite3 error code. */
111 static OSStatus
osstatus_for_s3e(int s3e
)
117 return errSecSuccess
;
118 case SQLITE_READONLY
:
119 return errSecReadOnly
;
120 case SQLITE_CONSTRAINT
:
121 return errSecDuplicateItem
;
122 case SQLITE_ABORT
: // There is no errSecOperationCancelled
124 case SQLITE_MISMATCH
:
125 return errSecNoSuchAttr
;
127 return errSecAllocate
;
130 case SQLITE_INTERNAL
:
131 return errSecInternalComponent
;
132 case SQLITE_FULL
: // Happens if we run out of uniqueids or disk is full (TODO: replace with better code)
133 case SQLITE_PERM
: // No acess permission
134 case SQLITE_AUTH
: // No authorization (e.g. no class key for file)
135 case SQLITE_CANTOPEN
: // can be several reasons for this. Caller should sqlite3_system_errno()
136 case SQLITE_EMPTY
: // SQLite does not seem to use this. Was already here, so keeping
139 return errSecNotAvailable
;
143 static OSStatus
osstatus_for_kern_return(CFIndex kernResult
)
148 return errSecSuccess
;
149 case kIOReturnNotReadable
:
150 case kIOReturnNotWritable
:
151 return errSecAuthFailed
;
152 case kIOReturnNotPermitted
:
153 case kIOReturnNotPrivileged
:
154 case kIOReturnLockedRead
:
155 case kIOReturnLockedWrite
:
156 return errSecInteractionNotAllowed
;
159 case kIOReturnBadArgument
:
162 return errSecNotAvailable
; /* TODO: Replace with a real error code. */
166 static OSStatus
osstatus_for_xpc_error(CFIndex xpcError
) {
169 case kSecXPCErrorSuccess
:
170 return errSecSuccess
;
171 case kSecXPCErrorUnexpectedType
:
172 case kSecXPCErrorUnexpectedNull
:
174 case kSecXPCErrorConnectionFailed
:
175 return errSecNotAvailable
;
176 case kSecXPCErrorUnknown
:
178 return errSecInternal
;
182 static OSStatus
osstatus_for_der_error(CFIndex derError
) {
185 case kSecDERErrorUnknownEncoding
:
186 case kSecDERErrorUnsupportedDERType
:
187 case kSecDERErrorUnsupportedNumberType
:
189 case kSecDERErrorUnsupportedCFObject
:
191 case kSecDERErrorAllocationFailure
:
192 return errSecAllocate
;
194 return errSecInternal
;
198 static OSStatus
osstatus_for_localauthentication_error(CFIndex laError
) {
199 // Wrap LA error in Sec error.
201 case kLAErrorUserCancel
:
202 return errSecUserCanceled
;
203 case kLAErrorParameter
:
205 case kLAErrorNotInteractive
:
206 return errSecInteractionNotAllowed
;
208 return errSecAuthFailed
;
212 static OSStatus
osstatus_for_ctk_error(CFIndex ctkError
) {
214 case kTKErrorCodeBadParameter
:
216 case kTKErrorCodeNotImplemented
:
217 return errSecUnimplemented
;
218 case kTKErrorCodeCanceledByUser
:
219 return errSecUserCanceled
;
220 case kTKErrorCodeCorruptedData
:
223 return errSecInternal
;
228 // Convert from securityd error codes to OSStatus for legacy API.
229 OSStatus
SecErrorGetOSStatus(CFErrorRef error
) {
232 status
= errSecSuccess
;
234 CFStringRef domain
= CFErrorGetDomain(error
);
235 if (domain
== NULL
) {
236 secerror("No error domain for error: %@", error
);
237 status
= errSecInternal
;
238 } else if (CFEqual(kSecErrorDomain
, domain
)) {
239 status
= (OSStatus
)CFErrorGetCode(error
);
240 } else if (CFEqual(kSecDbErrorDomain
, domain
)) {
241 status
= osstatus_for_s3e((int)CFErrorGetCode(error
));
242 } else if (CFEqual(kSecErrnoDomain
, domain
)) {
243 status
= (OSStatus
)CFErrorGetCode(error
);
244 } else if (CFEqual(kSecKernDomain
, domain
)) {
245 status
= osstatus_for_kern_return(CFErrorGetCode(error
));
246 } else if (CFEqual(sSecXPCErrorDomain
, domain
)) {
247 status
= osstatus_for_xpc_error(CFErrorGetCode(error
));
248 } else if (CFEqual(sSecDERErrorDomain
, domain
)) {
249 status
= osstatus_for_der_error(CFErrorGetCode(error
));
250 } else if (CFEqual(CFSTR(kLAErrorDomain
), domain
)) {
251 status
= osstatus_for_localauthentication_error(CFErrorGetCode(error
));
252 } else if (CFEqual(CFSTR(kTKErrorDomain
), domain
)) {
253 status
= osstatus_for_ctk_error(CFErrorGetCode(error
));
254 } else if (CFEqual(kSOSErrorDomain
, domain
)) {
255 status
= errSecInternal
;
257 secnotice("securityd", "unknown error domain: %@ for error: %@", domain
, error
);
258 status
= errSecInternal
;
265 lastErrorReleaseError(void *value
)
272 getLastErrorKey(pthread_key_t
*kv
)
274 static pthread_key_t key
;
275 static bool haveKey
= false;
276 static dispatch_once_t onceToken
;
277 dispatch_once(&onceToken
, ^{
278 if (pthread_key_create(&key
, lastErrorReleaseError
) == 0)
286 SetLastError(CFErrorRef newError
)
289 if (!getLastErrorKey(&key
))
291 CFErrorRef oldError
= pthread_getspecific(key
);
296 pthread_setspecific(key
, newError
);
300 SecCopyLastError(OSStatus status
)
305 if (!getLastErrorKey(&key
))
308 error
= pthread_getspecific(key
);
310 if (status
&& status
!= SecErrorGetOSStatus(error
)) {
319 // Wrapper to provide a CFErrorRef for legacy API.
320 OSStatus
SecOSStatusWith(bool (^perform
)(CFErrorRef
*error
)) {
321 CFErrorRef error
= NULL
;
323 if (perform(&error
)) {
324 assert(error
== NULL
);
326 status
= errSecSuccess
;
330 status
= SecErrorGetOSStatus(error
);
331 if (status
!= errSecItemNotFound
) // Occurs in normal operation, so exclude
332 secinfo("OSStatus", "error:[%" PRIdOSStatus
"] %@", status
, error
);
333 CFReleaseNull(error
);
339 logUnreasonableDataLength(CFDictionaryRef attributes
)
344 if (isDictionary(attributes
)) {
345 data
= CFDictionaryGetValue(attributes
, kSecValueData
);
347 length
= CFDataGetLength(data
);
348 if (length
> REASONABLE_DATA_SIZE
) {
349 // This log message is vague, as we may not know anything else about the item.
350 // securityd logging (correlate by activity ID) will have more information.
351 secwarning("keychain item data exceeds reasonable size (%lu bytes)", (unsigned long)length
);
357 /* Drop assorted kSecAttrCanXxxx attributes from the query, because these attributes are generated
358 by SecKey implementation and may differ between OS versions, see <rdar://problem/27095761>.
361 static CFDictionaryRef
362 AttributeCreateFilteredOutSecAttrs(CFDictionaryRef attributes
)
364 CFMutableDictionaryRef filtered
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, attributes
);
365 if (filtered
== NULL
)
367 CFDictionaryRemoveValue(filtered
, kSecAttrCanSign
);
368 CFDictionaryRemoveValue(filtered
, kSecAttrCanVerify
);
369 CFDictionaryRemoveValue(filtered
, kSecAttrCanEncrypt
);
370 CFDictionaryRemoveValue(filtered
, kSecAttrCanDecrypt
);
371 CFDictionaryRemoveValue(filtered
, kSecAttrCanDerive
);
372 CFDictionaryRemoveValue(filtered
, kSecAttrCanWrap
);
373 CFDictionaryRemoveValue(filtered
, kSecAttrCanUnwrap
);
374 CFDictionaryRemoveValue(filtered
, kSecAttrCanSignRecover
);
375 CFDictionaryRemoveValue(filtered
, kSecAttrCanVerifyRecover
);
376 CFDictionaryRemoveValue(filtered
, kSecAttrIsPermanent
);
382 /* IPC uses CFPropertyList to un/marshall input/output data and can handle:
383 CFData, CFString, CFArray, CFDictionary, CFDate, CFBoolean, and CFNumber
385 Currently in need of conversion below:
386 @@@ kSecValueRef allows SecKeychainItemRef and SecIdentityRef
387 @@@ kSecUseItemList allows a query against a list of itemrefs, this isn't
388 currently implemented at all, but when it is needs to short circuit to
389 local evaluation, different from the sql query abilities
392 static CFDictionaryRef
393 SecItemCopyAttributeDictionary(CFTypeRef ref
, bool forQuery
) {
394 CFDictionaryRef refDictionary
= NULL
;
395 CFTypeID typeID
= CFGetTypeID(ref
);
396 if (typeID
== SecKeyGetTypeID()) {
397 refDictionary
= SecKeyCopyAttributeDictionary((SecKeyRef
)ref
);
398 if (refDictionary
&& forQuery
) {
399 CFDictionaryRef filtered
= AttributeCreateFilteredOutSecAttrs(refDictionary
);
400 CFAssignRetained(refDictionary
, filtered
);
402 } else if (typeID
== SecCertificateGetTypeID()) {
403 refDictionary
= SecCertificateCopyAttributeDictionary((SecCertificateRef
)ref
);
404 } else if (typeID
== SecIdentityGetTypeID()) {
406 SecIdentityRef identity
= (SecIdentityRef
)ref
;
407 SecCertificateRef cert
= NULL
;
408 SecKeyRef key
= NULL
;
409 if (!SecIdentityCopyCertificate(identity
, &cert
) &&
410 !SecIdentityCopyPrivateKey(identity
, &key
))
412 CFDataRef data
= SecCertificateCopyData(cert
);
413 CFDictionaryRef key_dict
= SecKeyCopyAttributeDictionary(key
);
415 if (key_dict
&& data
) {
416 refDictionary
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, key_dict
);
417 CFDictionarySetValue((CFMutableDictionaryRef
)refDictionary
, kSecAttrIdentityCertificateData
, data
);
419 CFReleaseNull(key_dict
);
425 return refDictionary
;
428 #ifdef SECITEM_SHIM_OSX
429 extern CFTypeRef
SecItemCreateFromAttributeDictionary_osx(CFDictionaryRef refAttributes
);
433 SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes
) {
434 CFTypeRef ref
= NULL
;
435 CFStringRef
class = CFDictionaryGetValue(refAttributes
, kSecClass
);
436 if (CFEqual(class, kSecClassKey
)) {
437 ref
= SecKeyCreateFromAttributeDictionary(refAttributes
);
438 } else if (CFEqual(class, kSecClassCertificate
)) {
439 ref
= SecCertificateCreateFromAttributeDictionary(refAttributes
);
440 } else if (CFEqual(class, kSecClassIdentity
)) {
441 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecAttrIdentityCertificateData
);
442 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
);
443 SecKeyRef key
= SecKeyCreateFromAttributeDictionary(refAttributes
);
445 ref
= SecIdentityCreate(kCFAllocatorDefault
, cert
, key
);
448 secerror("SecItem: failed to create identity");
453 #ifdef SECITEM_SHIM_OSX
455 ref
= SecItemCreateFromAttributeDictionary_osx(refAttributes
);
463 SecItemCopyDisplayNames(CFArrayRef items
, CFArrayRef
*displayNames
)
466 return -1 /* errSecUnimplemented */;
468 #endif // TARGET_OS_OSX
470 typedef OSStatus (*secitem_operation
)(CFDictionaryRef attributes
, CFTypeRef
*result
);
472 static bool explode_identity(CFDictionaryRef attributes
, secitem_operation operation
,
473 OSStatus
*return_status
, CFTypeRef
*return_result
)
475 bool handled
= false;
476 CFTypeRef value
= CFDictionaryGetValue(attributes
, kSecValueRef
);
478 CFTypeID typeID
= CFGetTypeID(value
);
479 if (typeID
== SecIdentityGetTypeID()) {
481 OSStatus status
= errSecSuccess
;
482 SecIdentityRef identity
= (SecIdentityRef
)value
;
483 SecCertificateRef cert
= NULL
;
484 SecKeyRef key
= NULL
;
485 if (!SecIdentityCopyCertificate(identity
, &cert
) &&
486 !SecIdentityCopyPrivateKey(identity
, &key
))
488 CFMutableDictionaryRef partial_query
=
489 CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, attributes
);
490 CFDictionarySetValue(partial_query
, kSecValueRef
, cert
);
491 CFTypeRef result
= NULL
;
492 bool duplicate_cert
= false;
493 /* an identity is first and foremost a key, but it can have multiple
494 certs associated with it: so we identify it by the cert */
495 status
= operation(partial_query
, return_result
? &result
: NULL
);
496 if ((operation
== (secitem_operation
)SecItemAdd
) &&
497 (status
== errSecDuplicateItem
)) {
498 duplicate_cert
= true;
499 status
= errSecSuccess
;
502 if (!status
|| status
== errSecItemNotFound
) {
503 bool skip_key_operation
= false;
505 /* if the key is still in use, skip deleting it */
506 if (operation
== (secitem_operation
)SecItemDelete
) {
507 // find certs with cert.pkhh == keys.klbl
508 CFDictionaryRef key_dict
= NULL
, query_dict
= NULL
;
509 CFDataRef pkhh
= NULL
;
511 key_dict
= SecKeyCopyAttributeDictionary(key
);
513 pkhh
= (CFDataRef
)CFDictionaryGetValue(key_dict
, kSecAttrApplicationLabel
);
514 const void *keys
[] = { kSecClass
, kSecAttrPublicKeyHash
};
515 const void *vals
[] = { kSecClassCertificate
, pkhh
};
517 query_dict
= CFDictionaryCreate(NULL
, keys
,
518 vals
, (array_size(keys
)),
521 if (errSecSuccess
== SecItemCopyMatching(query_dict
, NULL
))
522 skip_key_operation
= true;
523 CFReleaseSafe(query_dict
);
524 CFReleaseSafe(key_dict
);
527 if (!skip_key_operation
) {
528 /* now perform the operation for the key */
529 CFDictionarySetValue(partial_query
, kSecValueRef
, key
);
530 CFDictionarySetValue(partial_query
, kSecReturnPersistentRef
, kCFBooleanFalse
);
532 status
= operation(partial_query
, NULL
);
533 if ((operation
== (secitem_operation
)SecItemAdd
) &&
534 (status
== errSecDuplicateItem
) &&
536 status
= errSecSuccess
;
539 /* add and copy matching for an identityref have a persistent ref result */
542 /* result is a persistent ref to a cert */
544 CFDictionaryRef tokenAttrs
= NULL
;
545 if (_SecItemParsePersistentRef(result
, NULL
, &rowid
, &tokenAttrs
)) {
546 *return_result
= _SecItemCreatePersistentRef(kSecClassIdentity
, rowid
, tokenAttrs
);
548 CFReleaseNull(tokenAttrs
);
553 CFReleaseNull(partial_query
);
556 status
= errSecInvalidItemRef
;
560 *return_status
= status
;
563 value
= CFDictionaryGetValue(attributes
, kSecClass
);
564 if (value
&& CFEqual(kSecClassIdentity
, value
) &&
565 (operation
== (secitem_operation
)SecItemDelete
)) {
566 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, attributes
);
567 CFDictionaryRemoveValue(dict
, kSecClass
);
568 CFDictionarySetValue(dict
, kSecClass
, kSecClassCertificate
);
569 OSStatus status
= SecItemDelete(dict
);
571 CFDictionarySetValue(dict
, kSecClass
, kSecClassKey
);
572 status
= SecItemDelete(dict
);
575 *return_status
= status
;
583 SecErrorPropagateLastError(OSStatus status
, CFErrorRef
*error
)
586 CFErrorRef lastError
= SecCopyLastError(status
);
588 CFErrorPropagate(lastError
, error
);
590 SecError(status
, error
, CFSTR("SecError: error not captured, OSStatus was: %d"), (int)status
);
597 handleUpdateIdentity(CFDictionaryRef query
,
598 CFDictionaryRef update
,
602 CFMutableDictionaryRef updatedQuery
= NULL
;
603 SecCertificateRef cert
= NULL
;
604 SecKeyRef key
= NULL
;
605 bool handled
= false;
609 CFTypeRef value
= CFDictionaryGetValue(query
, kSecValueRef
);
611 CFTypeID typeID
= CFGetTypeID(value
);
612 if (typeID
== SecIdentityGetTypeID()) {
613 SecIdentityRef identity
= (SecIdentityRef
)value
;
618 status
= SecIdentityCopyCertificate(identity
, &cert
);
619 require_noerr_action_quiet(status
, errOut
, SecErrorPropagateLastError(status
, error
));
621 status
= SecIdentityCopyPrivateKey(identity
, &key
);
622 require_noerr_action_quiet(status
, errOut
, SecErrorPropagateLastError(status
, error
));
624 updatedQuery
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, query
);
625 require_action_quiet(updatedQuery
, errOut
, *result
= false);
627 CFDictionarySetValue(updatedQuery
, kSecValueRef
, cert
);
628 require_quiet(SecItemUpdateWithError(updatedQuery
, update
, error
), errOut
);
630 CFDictionarySetValue(updatedQuery
, kSecValueRef
, key
);
631 require_quiet(SecItemUpdateWithError(updatedQuery
, update
, error
), errOut
);
635 value
= CFDictionaryGetValue(query
, kSecClass
);
636 if (value
&& CFEqual(kSecClassIdentity
, value
)) {
639 updatedQuery
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, query
);
640 require_action_quiet(updatedQuery
, errOut
, *result
= false);
642 CFDictionarySetValue(updatedQuery
, kSecClass
, kSecClassCertificate
);
643 require_quiet(SecItemUpdateWithError(updatedQuery
, update
, error
), errOut
);
645 CFDictionarySetValue(updatedQuery
, kSecClass
, kSecClassKey
);
646 require_quiet(SecItemUpdateWithError(updatedQuery
, update
, error
), errOut
);
648 CFReleaseNull(updatedQuery
);
653 CFReleaseNull(updatedQuery
);
659 static void infer_cert_label(SecCFDictionaryCOW
*attributes
)
661 if (!CFDictionaryContainsKey(attributes
->dictionary
, kSecAttrLabel
)) {
662 CFTypeRef value_ref
= CFDictionaryGetValue(attributes
->dictionary
, kSecValueRef
);
663 if (value_ref
&& CFGetTypeID(value_ref
) == SecCertificateGetTypeID()) {
664 SecCertificateRef certificate
= (SecCertificateRef
)value_ref
;
665 CFStringRef label
= SecCertificateCopySubjectSummary(certificate
);
667 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes
), kSecAttrLabel
, label
);
668 CFReleaseNull(label
);
674 static CFDataRef
CreateTokenPersistentRefData(CFTypeRef
class, CFDictionaryRef attributes
)
676 CFDataRef tokenPersistentRef
= NULL
;
678 CFDictionaryRef itemValue
= NULL
;
679 require_quiet(tokenId
= CFCast(CFString
, CFDictionaryGetValue(attributes
, kSecAttrTokenID
)), out
);
680 if (CFEqual(class, kSecClassIdentity
)) {
681 itemValue
= SecTokenItemValueCopy(CFDictionaryGetValue(attributes
, kSecAttrIdentityCertificateData
), NULL
);
683 itemValue
= SecTokenItemValueCopy(CFDictionaryGetValue(attributes
, kSecValueData
), NULL
);
685 require_quiet(itemValue
, out
);
686 CFDataRef oid
= CFDictionaryGetValue(itemValue
, kSecTokenValueObjectIDKey
);
687 require_quiet(oid
, out
);
688 CFArrayRef array
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, class, tokenId
, oid
, NULL
);
689 tokenPersistentRef
= CFPropertyListCreateDERData(kCFAllocatorDefault
, array
, NULL
);
692 CFReleaseNull(itemValue
);
693 return tokenPersistentRef
;
696 static const uint8_t tk_persistent_ref_id
[] = {'t', 'k', 'p', 'r'};
697 /* A persistent ref is just the class and the rowid of the record.
698 Persistent ref for token items is a der blob with class, tokenID and objectId. */
699 CFDataRef
_SecItemCreatePersistentRef(CFTypeRef
class, sqlite_int64 rowid
, CFDictionaryRef attributes
)
701 CFDataRef result
= NULL
;
702 if (attributes
&& CFDictionaryContainsKey(attributes
, CFEqual(class, kSecClassIdentity
) ? kSecAttrIdentityCertificateTokenID
: kSecAttrTokenID
)) {
703 CFDataRef tokenPersistentRef
= CreateTokenPersistentRefData(class, attributes
);
704 require(tokenPersistentRef
, out
);
705 CFMutableDataRef tmpData
= CFDataCreateMutable(kCFAllocatorDefault
, sizeof(tk_persistent_ref_id
) + CFDataGetLength(tokenPersistentRef
));
706 CFDataAppendBytes(tmpData
, tk_persistent_ref_id
, sizeof(tk_persistent_ref_id
));
707 CFDataAppend(tmpData
, tokenPersistentRef
);
708 CFReleaseNull(tokenPersistentRef
);
711 require(rowid
>= 0, out
);
712 uint8_t bytes
[sizeof(sqlite_int64
) + 4];
713 if (CFStringGetCString(class, (char *)bytes
, 4 + 1 /*null-term*/,
714 kCFStringEncodingUTF8
))
716 OSWriteBigInt64(bytes
+ 4, 0, rowid
);
717 result
= CFDataCreate(NULL
, bytes
, sizeof(bytes
));
724 static Boolean
isValidClass(CFStringRef
class, CFStringRef
*return_class
) {
725 const void *valid_classes
[] = { kSecClassGenericPassword
,
726 kSecClassInternetPassword
,
727 kSecClassAppleSharePassword
,
728 kSecClassCertificate
,
732 for (size_t i
= 0; i
< array_size(valid_classes
); i
++) {
733 if (CFEqual(valid_classes
[i
], class)) {
735 *return_class
= valid_classes
[i
];
743 static bool ParseTokenPersistentRefData(CFDataRef persistent_ref
, CFStringRef
*return_class
, CFDictionaryRef
*return_token_attrs
) {
744 bool valid_ref
= false;
745 CFPropertyListRef pl
= NULL
;
746 const uint8_t *der
= CFDataGetBytePtr(persistent_ref
) + sizeof(tk_persistent_ref_id
);
747 const uint8_t *der_end
= der
+ (CFDataGetLength(persistent_ref
) - sizeof(tk_persistent_ref_id
));
748 require_quiet(der
= der_decode_plist(0, kCFPropertyListImmutable
, &pl
, NULL
, der
, der_end
), out
);
749 require_quiet(der
== der_end
, out
);
750 require_quiet(CFGetTypeID(pl
) == CFArrayGetTypeID(), out
);
751 require_quiet(CFArrayGetCount(pl
) == 3, out
);
752 require_quiet(valid_ref
= isValidClass(CFArrayGetValueAtIndex(pl
, 0), return_class
), out
);
753 if (return_token_attrs
) {
754 *return_token_attrs
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
755 kSecAttrTokenID
, CFArrayGetValueAtIndex(pl
, 1),
756 kSecAttrTokenOID
, CFArrayGetValueAtIndex(pl
, 2), NULL
);
763 /* AUDIT[securityd](done):
764 persistent_ref (ok) is a caller provided, non NULL CFTypeRef.
766 bool _SecItemParsePersistentRef(CFDataRef persistent_ref
, CFStringRef
*return_class
, sqlite_int64
*return_rowid
, CFDictionaryRef
*return_token_attrs
)
768 bool valid_ref
= false;
769 require(CFGetTypeID(persistent_ref
) == CFDataGetTypeID(), out
);
771 if (CFDataGetLength(persistent_ref
) > (CFIndex
)sizeof(tk_persistent_ref_id
) &&
772 memcmp(tk_persistent_ref_id
, CFDataGetBytePtr(persistent_ref
), sizeof(tk_persistent_ref_id
)) == 0) {
773 valid_ref
= ParseTokenPersistentRefData(persistent_ref
, return_class
, return_token_attrs
);
774 } else if (CFDataGetLength(persistent_ref
) == (CFIndex
)(sizeof(sqlite_int64
) + 4)) {
775 const uint8_t *bytes
= CFDataGetBytePtr(persistent_ref
);
776 sqlite_int64 rowid
= OSReadBigInt64(bytes
+ 4, 0);
778 CFStringRef
class = CFStringCreateWithBytes(kCFAllocatorDefault
,
779 bytes
, CFStringGetLength(kSecClassGenericPassword
),
780 kCFStringEncodingUTF8
, true);
782 if ((valid_ref
= isValidClass(class, return_class
))) {
784 *return_rowid
= rowid
;
792 static bool cf_bool_value(CFTypeRef cf_bool
) {
793 return cf_bool
&& CFBooleanGetValue(cf_bool
);
796 CFMutableDictionaryRef
SecCFDictionaryCOWGetMutable(SecCFDictionaryCOW
*cow_dictionary
) {
797 if (cow_dictionary
->mutable_dictionary
== NULL
) {
798 cow_dictionary
->mutable_dictionary
= CFDictionaryCreateMutableForCFTypes(NULL
);
799 if (cow_dictionary
->dictionary
!= NULL
) {
800 CFDictionaryForEach(cow_dictionary
->dictionary
, ^(const void *key
, const void *value
) {
801 CFDictionarySetValue(cow_dictionary
->mutable_dictionary
, key
, value
);
804 cow_dictionary
->dictionary
= cow_dictionary
->mutable_dictionary
;
807 return cow_dictionary
->mutable_dictionary
;
810 // Creates kSecValueData field stored in the DB for token-based items. Data field consists of objectID, real
811 // access_control and optionally of the data value.
812 static CFDataRef
SecTokenItemValueCreate(CFDataRef oid
, CFDataRef access_control
, CFDataRef object_value
, CFErrorRef
*error
) {
813 CFMutableDictionaryRef value
= NULL
;
814 value
= CFDictionaryCreateMutableForCFTypesWith(NULL
,
815 kSecTokenValueObjectIDKey
, oid
,
816 kSecTokenValueAccessControlKey
, access_control
,
818 if (object_value
!= NULL
) {
819 CFDictionarySetValue(value
, kSecTokenValueDataKey
, object_value
);
822 CFDataRef value_data
= CFPropertyListCreateDERData(NULL
, value
, error
);
827 CFDictionaryRef
SecTokenItemValueCopy(CFDataRef db_value
, CFErrorRef
*error
) {
828 CFPropertyListRef plist
= NULL
;
829 require_quiet(CFCastWithError(CFData
, db_value
, error
), out
);
830 const uint8_t *der
= CFDataGetBytePtr(db_value
);
831 const uint8_t *der_end
= der
+ CFDataGetLength(db_value
);
832 require_quiet(der
= der_decode_plist(0, kCFPropertyListImmutable
, &plist
, error
, der
, der_end
), out
);
833 require_action_quiet(der
== der_end
, out
, SecError(errSecDecode
, error
, CFSTR("trailing garbage at end of token data field")));
834 CFTypeRef value
= CFDictionaryGetValue(plist
, kSecTokenValueObjectIDKey
);
835 require_action_quiet(CFCast(CFData
, value
) != NULL
, out
,
836 SecError(errSecInternal
, error
, CFSTR("token based item data does not have OID")));
837 value
= CFDictionaryGetValue(plist
, kSecTokenValueAccessControlKey
);
838 require_quiet(value
== NULL
|| CFCastWithError(CFData
, value
, error
), out
);
839 value
= CFDictionaryGetValue(plist
, kSecTokenValueDataKey
);
840 require_quiet(value
== NULL
|| CFCastWithError(CFData
, value
, error
), out
);
846 TKTokenRef
SecTokenCreate(CFStringRef token_id
, SecCFDictionaryCOW
*auth_params
, CFErrorRef
*error
) {
847 CFMutableDictionaryRef token_attrs
= NULL
;
848 TKTokenRef token
= NULL
;
850 static CFMutableDictionaryRef sharedLAContexts
= NULL
;
851 static dispatch_once_t onceToken
;
852 static os_unfair_lock lock
= OS_UNFAIR_LOCK_INIT
;
853 if ((auth_params
->dictionary
== NULL
|| CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCredentialReference
) == NULL
) && !CFStringHasPrefix(token_id
, kSecAttrTokenIDSecureEnclave
)) {
854 dispatch_once(&onceToken
, ^{
855 sharedLAContexts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
858 os_unfair_lock_lock(&lock
);
859 CFTypeRef ctx
= CFDictionaryGetValue(sharedLAContexts
, token_id
);
861 ctx
= LACreateNewContextWithACMContext(NULL
, error
);
863 os_unfair_lock_unlock(&lock
);
864 secerror("Failed to create authentication context %@", *error
);
867 CFDictionarySetValue(sharedLAContexts
, token_id
, ctx
);
869 ctx
= CFDictionaryGetValue(sharedLAContexts
, token_id
);
872 CFDataRef credRef
= NULL
;
874 credRef
= LACopyACMContext(ctx
, NULL
);
878 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationContext
, ctx
);
879 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, credRef
);
882 os_unfair_lock_unlock(&lock
);
885 token_attrs
= (auth_params
->dictionary
!= NULL
) ?
886 CFDictionaryCreateMutableCopy(NULL
, 0, auth_params
->dictionary
) :
887 CFDictionaryCreateMutableForCFTypes(NULL
);
888 CFDictionarySetValue(token_attrs
, kSecAttrTokenID
, token_id
);
890 CFDictionaryRemoveValue(token_attrs
, kSecUseAuthenticationContext
);
891 token
= TKTokenCreate(token_attrs
, error
);
893 CFReleaseSafe(token_attrs
);
897 static bool SecTokenItemCreateFromAttributes(CFDictionaryRef attributes
, CFDictionaryRef auth_params_dict
,
898 TKTokenRef token
, CFDataRef object_id
, CFTypeRef
*ref
, CFErrorRef
*error
) {
900 SecCFDictionaryCOW auth_params
= { auth_params_dict
};
901 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutableCopy(NULL
, 0, attributes
);
902 CFTypeRef token_id
= CFDictionaryGetValue(attributes
, kSecAttrTokenID
);
903 if (token_id
!= NULL
&& object_id
!= NULL
) {
904 require_quiet(CFCastWithError(CFString
, token_id
, error
), out
);
905 if (CFRetainSafe(token
) == NULL
) {
906 require_quiet(token
= SecTokenCreate(token_id
, &auth_params
, error
), out
);
909 if (auth_params
.dictionary
!= NULL
) {
910 CFDictionaryForEach(auth_params
.dictionary
, ^(const void *key
, const void *value
) {
911 CFDictionaryAddValue(attrs
, key
, value
);
914 CFDictionarySetValue(attrs
, kSecUseToken
, token
);
915 CFDictionarySetValue(attrs
, kSecAttrTokenOID
, object_id
);
918 *ref
= SecItemCreateFromAttributeDictionary(attrs
);
922 CFReleaseSafe(attrs
);
923 CFReleaseSafe(auth_params
.mutable_dictionary
);
928 /* Turn the returned single value or dictionary that contains all the attributes to create a
929 ref into the exact result the client asked for */
930 static bool SecItemResultCopyPrepared(CFTypeRef raw_result
, TKTokenRef token
,
931 CFDictionaryRef query
, CFDictionaryRef auth_params_dict
,
932 CFTypeRef
*result
, CFErrorRef
*error
) {
934 CFDataRef ac_data
= NULL
;
935 CFDataRef value
= NULL
;
936 CFTypeRef persistent_ref
= NULL
;
937 CFStringRef token_id
= NULL
;
938 CFStringRef cert_token_id
= NULL
;
939 CFDataRef object_id
= NULL
;
940 CFMutableDictionaryRef attrs
= NULL
;
941 CFDataRef cert_data
= NULL
;
942 CFDataRef cert_object_id
= NULL
;
943 TKTokenRef cert_token
= NULL
;
944 SecCFDictionaryCOW auth_params
= { auth_params_dict
};
946 bool wants_ref
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnRef
));
947 bool wants_data
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnData
));
948 bool wants_attributes
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnAttributes
));
949 bool wants_persistent_ref
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnPersistentRef
));
951 // Get token value if not provided by the caller.
952 bool token_item
= false;
953 bool cert_token_item
= false;
955 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID()) {
956 token_id
= CFDictionaryGetValue(raw_result
, kSecAttrTokenID
);
957 require_quiet(token_id
== NULL
|| CFCastWithError(CFString
, token_id
, error
) != NULL
, out
);
958 token_item
= (token_id
!= NULL
);
960 cert_token_id
= CFDictionaryGetValue(raw_result
, kSecAttrIdentityCertificateTokenID
);
961 require_quiet(cert_token_id
== NULL
|| CFCastWithError(CFString
, cert_token_id
, error
) != NULL
, out
);
962 cert_token_item
= (cert_token_id
!= NULL
);
966 cert_token_item
= true;
968 CFRetainAssign(cert_token
, token
);
971 if ((token_item
|| cert_token_item
) && cf_bool_value(CFDictionaryGetValue(query
, kSecUseTokenRawItems
))) {
973 cert_token_item
= false;
976 // Decode and prepare data value, if it is requested at the output, or if we want attributes from token.
977 if (wants_data
|| wants_ref
|| (token_item
&& wants_attributes
)) {
978 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID())
979 value
= CFRetainSafe(CFDictionaryGetValue(raw_result
, kSecValueData
));
981 value
= CFRetainSafe(raw_result
);
982 if (token_item
&& value
!= NULL
) {
983 // Parse token-based item's data field.
984 CFDataRef object_value
= NULL
;
985 CFDictionaryRef parsed_value
= NULL
;
986 require_quiet(parsed_value
= SecTokenItemValueCopy(value
, error
), out
);
987 object_id
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueObjectIDKey
));
988 ac_data
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueAccessControlKey
));
989 object_value
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueDataKey
));
990 CFRelease(parsed_value
);
991 if ((wants_data
|| wants_ref
) && object_value
== NULL
) {
992 // Retrieve value directly from the token.
994 require_quiet(token
= SecTokenCreate(token_id
, &auth_params
, error
), out
);
996 require_quiet(object_value
= TKTokenCopyObjectData(token
, object_id
, error
), out
);
997 if (CFEqual(object_value
, kCFNull
))
998 CFReleaseNull(object_value
);
1000 CFAssignRetained(value
, object_value
);
1003 // If only thing requested is data, return them directly.
1004 if (!(wants_attributes
|| wants_persistent_ref
|| wants_ref
)) {
1005 *result
= CFRetainSafe(value
);
1011 // Extract persistent_ref, if caller wants it.
1012 if (wants_persistent_ref
) {
1013 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID())
1014 persistent_ref
= CFRetainSafe(CFDictionaryGetValue(raw_result
, kSecValuePersistentRef
));
1016 persistent_ref
= CFRetainSafe(raw_result
);
1018 // If only thing requested is persistentref, extract it from dictionary if needed and return it.
1019 if (!(wants_attributes
|| wants_data
|| wants_ref
)) {
1020 *result
= CFRetainSafe(persistent_ref
);
1026 if (!wants_ref
&& !wants_attributes
&& (!wants_data
|| !wants_persistent_ref
)) {
1032 // For other cases we need an output dictionary.
1033 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID())
1034 *result
= CFDictionaryCreateMutableCopy(NULL
, 0, raw_result
);
1036 *result
= CFDictionaryCreateForCFTypes(NULL
, NULL
);
1037 CFMutableDictionaryRef output
= (CFMutableDictionaryRef
)*result
;
1039 if ((wants_data
|| wants_ref
) && value
!= NULL
)
1040 CFDictionarySetValue(output
, kSecValueData
, value
);
1042 CFDictionaryRemoveValue(output
, kSecValueData
);
1044 if (wants_persistent_ref
&& persistent_ref
!= NULL
)
1045 CFDictionarySetValue(output
, kSecValuePersistentRef
, persistent_ref
);
1047 CFDictionaryRemoveValue(output
, kSecValuePersistentRef
);
1049 if ((wants_ref
|| wants_attributes
) && cert_token_item
&&
1050 CFEqualSafe(CFDictionaryGetValue(output
, kSecClass
), kSecClassIdentity
)) {
1051 // Decode also certdata field of the identity.
1052 CFDataRef data
= CFDictionaryGetValue(output
, kSecAttrIdentityCertificateData
);
1054 CFDictionaryRef parsed_value
;
1055 require_quiet(parsed_value
= SecTokenItemValueCopy(data
, error
), out
);
1056 cert_data
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueDataKey
));
1057 cert_object_id
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueObjectIDKey
));
1058 CFRelease(parsed_value
);
1059 if (cert_data
== NULL
) {
1060 // Retrieve value directly from the token.
1061 if (cert_token
== NULL
) {
1062 require_quiet(cert_token
= SecTokenCreate(cert_token_id
, &auth_params
, error
), out
);
1064 require_quiet(cert_data
= TKTokenCopyObjectData(cert_token
, cert_object_id
, error
), out
);
1065 if (CFEqual(cert_data
, kCFNull
))
1066 CFReleaseNull(cert_data
);
1068 if (cert_data
!= NULL
) {
1069 CFDictionarySetValue(output
, kSecAttrIdentityCertificateData
, cert_data
);
1071 CFDictionaryRemoveValue(output
, kSecAttrIdentityCertificateData
);
1076 if (wants_ref
|| wants_attributes
) {
1077 // Convert serialized form of access control to object form.
1079 CFRetainAssign(ac_data
, CFDictionaryGetValue(output
, kSecAttrAccessControl
));
1082 if (ac_data
!= NULL
) {
1083 SecAccessControlRef ac
;
1084 require_quiet(ac
= SecAccessControlCreateFromData(kCFAllocatorDefault
, ac_data
, error
), out
);
1085 CFDictionarySetValue(output
, kSecAttrAccessControl
, ac
);
1092 require_quiet(SecTokenItemCreateFromAttributes(output
, auth_params
.dictionary
, token
, object_id
, &ref
, error
), out
);
1093 if (!(wants_attributes
|| wants_data
|| wants_persistent_ref
)) {
1094 CFAssignRetained(*result
, ref
);
1095 } else if (ref
!= NULL
) {
1096 CFDictionarySetValue(output
, kSecValueRef
, ref
);
1099 // We could have stored data value previously to make ref creation succeed.
1100 // They are not needed any more and in case that caller did not want the data, avoid returning them.
1101 CFDictionaryRemoveValue(output
, kSecValueData
);
1109 CFReleaseSafe(cert_object_id
);
1110 CFReleaseSafe(cert_data
);
1111 CFReleaseSafe(ac_data
);
1112 CFReleaseSafe(value
);
1113 CFReleaseSafe(persistent_ref
);
1114 CFReleaseSafe(object_id
);
1115 CFReleaseSafe(attrs
);
1116 CFReleaseSafe(token
);
1117 CFReleaseSafe(cert_token
);
1118 CFReleaseSafe(auth_params
.mutable_dictionary
);
1122 bool SecItemResultProcess(CFDictionaryRef query
, CFDictionaryRef auth_params
, TKTokenRef token
,
1123 CFTypeRef raw_result
, CFTypeRef
*result
, CFErrorRef
*error
) {
1125 require_action_quiet(raw_result
!= NULL
, out
, ok
= true);
1126 require_action_quiet(result
!= NULL
, out
, ok
= true);
1128 if (CFGetTypeID(raw_result
) == CFArrayGetTypeID()) {
1129 CFIndex i
, count
= CFArrayGetCount(raw_result
);
1130 *result
= CFArrayCreateMutableForCFTypes(NULL
);
1131 for (i
= 0; i
< count
; i
++) {
1133 require_quiet(SecItemResultCopyPrepared(CFArrayGetValueAtIndex(raw_result
, i
),
1134 token
, query
, auth_params
, &ref
, error
), out
);
1136 CFArrayAppendValue((CFMutableArrayRef
)*result
, ref
);
1141 require_quiet(SecItemResultCopyPrepared(raw_result
, token
, query
, auth_params
, result
, error
), out
);
1150 static bool SecItemAttributesPrepare(SecCFDictionaryCOW
*attrs
, bool forQuery
, CFErrorRef
*error
) {
1152 CFDataRef ac_data
= NULL
, acm_context
= NULL
;
1153 void *la_lib
= NULL
;
1155 // If a ref was specified we get its attribute dictionary and parse it.
1156 CFTypeRef value
= CFDictionaryGetValue(attrs
->dictionary
, kSecValueRef
);
1158 CFDictionaryRef ref_attributes
;
1159 require_action_quiet(ref_attributes
= SecItemCopyAttributeDictionary(value
, forQuery
), out
,
1160 SecError(errSecValueRefUnsupported
, error
, CFSTR("unsupported kSecValueRef in query")));
1162 // Replace any attributes we already got from the ref with the ones from the attributes dictionary the caller passed us.
1163 // This allows a caller to add an item using attributes from the ref and still override some of them in the dictionary directly.
1164 CFDictionaryForEach(ref_attributes
, ^(const void *key
, const void *value
) {
1165 // Attributes already present in 'attrs' have precedence over the generic ones retrieved from the ref,
1166 // so add only those attributes from 'ref' which are missing in attrs.
1167 CFDictionaryAddValue(SecCFDictionaryCOWGetMutable(attrs
), key
, value
);
1169 CFRelease(ref_attributes
);
1172 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs
), kSecAttrTokenOID
);
1175 // Remove original expanded valueRef. Do not remove it in case when adding token item, because it is needed later to avoid
1176 // another roundtrip to token driver.
1177 if (forQuery
|| !CFDictionaryContainsKey(attrs
->dictionary
, kSecAttrTokenID
)) {
1178 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs
), kSecValueRef
);
1182 SecAccessControlRef access_control
= (SecAccessControlRef
)CFDictionaryGetValue(attrs
->dictionary
, kSecAttrAccessControl
);
1183 if (access_control
!= NULL
) {
1184 require_action_quiet(CFGetTypeID(access_control
) == SecAccessControlGetTypeID(), out
,
1185 SecError(errSecParam
, error
, CFSTR("Unexpected type of kSecAttrAccessControl attribute")));
1186 require_action_quiet(ac_data
= SecAccessControlCopyData(access_control
), out
,
1187 SecError(errSecParam
, error
, CFSTR("unsupported kSecAttrAccessControl in query")));
1188 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecAttrAccessControl
, ac_data
);
1191 const CFTypeRef la_context
= CFDictionaryGetValue(attrs
->dictionary
, kSecUseAuthenticationContext
);
1193 require_action_quiet(!CFDictionaryContainsKey(attrs
->dictionary
, kSecUseCredentialReference
), out
,
1194 SecError(errSecParam
, error
, CFSTR("kSecUseAuthenticationContext cannot be used together with kSecUseCredentialReference")));
1195 require_quiet(acm_context
= LACopyACMContext(la_context
, error
), out
);
1196 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs
), kSecUseAuthenticationContext
);
1197 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecUseCredentialReference
, acm_context
);
1200 CFTypeRef policy
= CFDictionaryGetValue(attrs
->dictionary
, kSecMatchPolicy
);
1202 require_action_quiet(CFGetTypeID(policy
) == SecPolicyGetTypeID(), out
,
1203 SecError(errSecParam
, error
, CFSTR("unsupported kSecMatchPolicy in query")));
1205 CFTypeRef values
[] = { policy
};
1206 CFArrayRef policiesArray
= CFArrayCreate(kCFAllocatorDefault
, values
, 1, &kCFTypeArrayCallBacks
);
1207 xpc_object_t policiesArrayXPC
= SecPolicyArrayCopyXPCArray(policiesArray
, error
);
1208 CFReleaseSafe(policiesArray
);
1209 require_action_quiet(policiesArrayXPC
, out
,
1210 SecError(errSecInternal
, error
, CFSTR("Failed to copy XPC policy")));
1212 CFTypeRef objectReadyForXPC
= _CFXPCCreateCFObjectFromXPCObject(policiesArrayXPC
);
1213 xpc_release(policiesArrayXPC
);
1214 require_action_quiet(objectReadyForXPC
, out
,
1215 SecError(errSecInternal
, error
, CFSTR("Failed to create CFObject from XPC policy")));
1217 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecMatchPolicy
, objectReadyForXPC
);
1218 CFRelease(objectReadyForXPC
);
1220 value
= CFDictionaryGetValue(attrs
->dictionary
, kSecAttrIssuer
);
1222 /* convert DN to canonical issuer, if value is DN (top level sequence) */
1224 require_quiet(issuer
= CFCastWithError(CFData
, value
, error
), out
);
1225 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
) };
1226 DERDecodedInfo content
;
1227 if (DERDecodeItem(&name
, &content
) == DR_Success
&& content
.tag
== ASN1_CONSTR_SEQUENCE
) {
1228 CFDataRef canonical_issuer
= createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1229 if (canonical_issuer
) {
1230 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecAttrIssuer
, canonical_issuer
);
1231 CFRelease(canonical_issuer
);
1236 if (CFDictionaryContainsKey(attrs
->dictionary
, kSecUseTokenRawItems
)) {
1237 // This use flag is client-only, securityd does not understand it.
1238 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs
), kSecUseTokenRawItems
);
1244 if (la_lib
!= NULL
) {
1247 CFReleaseSafe(ac_data
);
1248 CFReleaseSafe(acm_context
);
1252 static bool SecItemAuthMaxAttemptsReached(CFArrayRef ac_pairs
, CFErrorRef
*error
)
1254 CFMutableStringRef log_string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1256 CFArrayForEachC(ac_pairs
, ac_pair
) {
1257 CFStringRef acl_hex_string
= CFDataCopyHexString(CFArrayGetValueAtIndex(ac_pair
, 0));
1258 CFStringRef str
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("operation: %@ acl:%@\n"), CFArrayGetValueAtIndex(ac_pair
, 1), acl_hex_string
);
1259 CFStringAppend(log_string
, str
);
1260 CFRelease(acl_hex_string
);
1264 CFStringRef reason
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("Reached maximum count of authentication attempts\n %@"), log_string
);
1265 SecError(errSecAuthFailed
, error
, CFSTR("%@"), reason
);
1266 __security_simulatecrash(reason
, __sec_exception_code_AuthLoop
);
1268 CFRelease(log_string
);
1272 bool SecItemAuthDo(SecCFDictionaryCOW
*auth_params
, CFErrorRef
*error
, SecItemAuthResult (^perform
)(CFArrayRef
*ac_pairs
, CFErrorRef
*error
),
1273 void (^newCredentialRefAdded
)(void)) {
1275 CFArrayRef ac_pairs
= NULL
;
1276 SecCFDictionaryCOW auth_options
= { NULL
};
1278 for (uint32_t i
= 0;; ++i
) {
1279 // If the operation succeeded or failed with other than auth-needed error, just leave.
1280 SecItemAuthResult auth_result
= perform(&ac_pairs
, error
);
1281 require_quiet(auth_result
!= kSecItemAuthResultError
, out
);
1282 require_action_quiet(auth_result
== kSecItemAuthResultNeedAuth
, out
, ok
= true);
1284 // If auth_params were not created up to now, do create them because we will definitely need them.
1285 SecCFDictionaryCOWGetMutable(auth_params
);
1287 // Retrieve or create authentication handle and/or ACM context.
1288 CFTypeRef auth_handle
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseAuthenticationContext
);
1289 if (auth_handle
== NULL
) {
1290 CFDataRef acm_context
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCredentialReference
);
1291 require_quiet(auth_handle
= LACreateNewContextWithACMContext(acm_context
, error
), out
);
1292 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationContext
, auth_handle
);
1293 CFRelease(auth_handle
);
1294 if (acm_context
== NULL
) {
1295 require_quiet(acm_context
= LACopyACMContext(auth_handle
, error
), out
);
1296 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, acm_context
);
1297 CFRelease(acm_context
);
1298 if (newCredentialRefAdded
) {
1299 newCredentialRefAdded();
1304 // Throttle max authentication attempts. This is mainly protection against exceptional states, not ordinary
1305 // user retry limit.
1306 require_action(i
< 20, out
, SecItemAuthMaxAttemptsReached(ac_pairs
, error
));
1308 // Prepare auth options dictionary.
1309 if (auth_options
.dictionary
== NULL
) {
1310 CFStringRef operation_prompt
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseOperationPrompt
);
1311 if (operation_prompt
!= NULL
) {
1312 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionAuthenticationReason
);
1313 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, operation_prompt
);
1317 CFStringRef caller_name
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCallerName
);
1318 if (caller_name
!= NULL
) {
1319 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionCallerName
);
1320 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, caller_name
);
1324 CFTypeRef auth_ui
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseAuthenticationUI
);
1325 if (CFEqualSafe(auth_ui
, kSecUseAuthenticationUIFail
)) {
1326 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionNotInteractive
);
1327 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, kCFBooleanTrue
);
1332 // Go through all access_control-operation pairs and evaluate them.
1334 CFArrayForEachC(ac_pairs
, ac_pair
) {
1335 CFDataRef updated_acl
= NULL
;
1336 require_quiet(LAEvaluateAndUpdateACL(auth_handle
,
1337 CFArrayGetValueAtIndex(ac_pair
, 0), CFArrayGetValueAtIndex(ac_pair
, 1),
1338 auth_options
.dictionary
, &updated_acl
, error
), out
);
1340 if (updated_acl
|| CFEqual(CFArrayGetValueAtIndex(ac_pair
, 1), CFSTR(""))) {
1341 // we assume that only one ACL can be modified during ItemAdd or ItemUpdate
1342 SecAccessControlRef ac
= NULL
;
1343 require(ac
= SecAccessControlCreateFromData(kCFAllocatorDefault
,
1344 updated_acl
? updated_acl
: CFArrayGetValueAtIndex(ac_pair
, 0), error
), out
);
1345 SecAccessControlSetBound(ac
, true);
1346 CFAssignRetained(updated_acl
, SecAccessControlCopyData(ac
));
1347 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecAttrAccessControl
, updated_acl
);
1348 CFRelease(updated_acl
);
1357 CFReleaseSafe(auth_options
.mutable_dictionary
);
1358 CFReleaseSafe(ac_pairs
);
1362 void SecItemAuthCopyParams(SecCFDictionaryCOW
*auth_params
, SecCFDictionaryCOW
*query
) {
1363 // Store operation prompt.
1364 CFStringRef operation_prompt
= CFDictionaryGetValue(query
->dictionary
, kSecUseOperationPrompt
);
1365 if (operation_prompt
!= NULL
) {
1366 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseOperationPrompt
, operation_prompt
);
1367 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseOperationPrompt
);
1370 // Store caller name.
1371 CFStringRef caller_name
= CFDictionaryGetValue(query
->dictionary
, kSecUseCallerName
);
1372 if (caller_name
!= NULL
) {
1373 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCallerName
, caller_name
);
1374 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseCallerName
);
1377 // Find out whether we are allowed to pop up a UI.
1378 CFTypeRef auth_ui
= CFDictionaryGetValue(query
->dictionary
, kSecUseAuthenticationUI
) ?:
1379 (CFEqualSafe(CFDictionaryGetValue(query
->dictionary
, kSecUseNoAuthenticationUI
), kCFBooleanTrue
) ?
1380 kSecUseAuthenticationUIFail
: kSecUseAuthenticationUIAllow
);
1381 if (!CFEqual(auth_ui
, kSecUseAuthenticationUISkip
) || CFDictionaryGetValue(query
->dictionary
, kSecUseNoAuthenticationUI
)) {
1382 if (CFDictionaryContainsKey(query
->dictionary
, kSecUseNoAuthenticationUI
))
1383 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseNoAuthenticationUI
);
1384 if (!CFEqualSafe(auth_ui
, kSecUseAuthenticationUISkip
) && CFDictionaryContainsKey(query
->dictionary
, kSecUseAuthenticationUI
))
1385 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseAuthenticationUI
);
1388 if (!CFEqual(auth_ui
, kSecUseAuthenticationUIAllow
)) {
1389 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationUI
, auth_ui
);
1392 CFDataRef acm_context
= CFDictionaryGetValue(query
->dictionary
, kSecUseCredentialReference
);
1393 if (acm_context
!= NULL
) {
1394 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, acm_context
);
1398 static SecItemAuthResult
SecItemCreatePairsFromError(CFErrorRef
*error
, CFArrayRef
*ac_pairs
)
1400 if (error
&& *error
&& CFErrorGetCode(*error
) == errSecAuthNeeded
&& CFEqualSafe(CFErrorGetDomain(*error
), kSecErrorDomain
)) {
1401 // Extract ACLs to be verified from the error.
1402 CFDictionaryRef user_info
= CFErrorCopyUserInfo(*error
);
1403 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, errSecAuthNeeded
);
1404 CFRetainAssign(*ac_pairs
, CFDictionaryGetValue(user_info
, key
));
1405 if (*ac_pairs
== NULL
)
1406 CFAssignRetained(*ac_pairs
, CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
));
1409 CFRelease(user_info
);
1410 CFReleaseNull(*error
);
1411 return kSecItemAuthResultNeedAuth
;
1413 return kSecItemAuthResultError
;
1416 // Wrapper to handle automatic authentication and token/secd case switching.
1417 bool SecItemAuthDoQuery(SecCFDictionaryCOW
*query
, SecCFDictionaryCOW
*attributes
, const void *secItemOperation
, CFErrorRef
*error
,
1418 bool (^perform
)(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
)) {
1420 __block SecCFDictionaryCOW auth_params
= { NULL
};
1421 __block TKTokenRef token
= NULL
;
1423 CFDictionaryRef dict
= attributes
? attributes
->dictionary
: query
->dictionary
;
1424 SecAccessControlRef access_control
= (SecAccessControlRef
)CFDictionaryGetValue(dict
, kSecAttrAccessControl
);
1425 require_action_quiet(access_control
== NULL
|| CFGetTypeID(access_control
) == SecAccessControlGetTypeID(), out
,
1426 SecError(errSecParam
, error
, CFSTR("Unexpected type of kSecAttrAccessControl attribute")));
1428 if (secItemOperation
== SecItemAdd
|| secItemOperation
== SecItemUpdate
) {
1429 if (access_control
&& SecAccessControlGetConstraints(access_control
) &&
1430 CFEqualSafe(CFDictionaryGetValue(dict
, kSecAttrSynchronizable
), kCFBooleanTrue
))
1431 require_quiet(SecError(errSecParam
, error
, CFSTR("item with kSecAttrAccessControl is not synchronizable")), out
);
1434 // Perform initial surgery on query/attributes (resolve LAContext to serialized ACM handle, resolve
1435 // SecAccessControlRef to serialized forms, expand kSecValueRef etc.)
1437 secItemOperation
== SecItemCopyMatching
||
1438 secItemOperation
== SecItemUpdate
||
1439 secItemOperation
== SecItemDelete
;
1441 require_quiet(SecItemAttributesPrepare(query
, forQuery
, error
), out
);
1442 if (attributes
!= NULL
)
1443 require_quiet(SecItemAttributesPrepare(attributes
, false, error
), out
);
1445 // Populate auth_params dictionary according to initial query contents.
1446 SecItemAuthCopyParams(&auth_params
, query
);
1448 if (secItemOperation
!= SecItemCopyMatching
) {
1449 // UISkip is allowed only for CopyMatching.
1450 require_action_quiet(!CFEqualSafe(CFDictionaryGetValue(query
->dictionary
, kSecUseAuthenticationUI
), kSecUseAuthenticationUISkip
), out
,
1451 SecError(errSecParam
, error
,
1452 CFSTR("kSecUseAuthenticationUISkip is allowed only for SecItemCopyMatching")));
1455 ok
= SecItemAuthDo(&auth_params
, error
, ^SecItemAuthResult(CFArrayRef
*ac_pairs
, CFErrorRef
*error
) {
1456 SecItemAuthResult result
= kSecItemAuthResultError
;
1458 // Propagate actual credential reference to the query.
1459 if (auth_params
.dictionary
!= NULL
) {
1460 CFDataRef acm_context
= CFDictionaryGetValue(auth_params
.dictionary
, kSecUseCredentialReference
);
1461 if (acm_context
!= NULL
) {
1462 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query
), kSecUseCredentialReference
, acm_context
);
1465 CFDataRef acl_data_ref
= CFDictionaryGetValue(auth_params
.dictionary
, kSecAttrAccessControl
);
1466 if (acl_data_ref
!= NULL
) {
1467 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes
?: query
), kSecAttrAccessControl
, acl_data_ref
);
1471 // Prepare connection to target token if it is present.
1472 CFStringRef token_id
= CFDictionaryGetValue(query
->dictionary
, kSecAttrTokenID
);
1473 require_quiet(token_id
== NULL
|| CFCastWithError(CFString
, token_id
, error
) != NULL
, out
);
1474 if (secItemOperation
!= SecItemCopyMatching
&& token_id
!= NULL
) {
1475 require_quiet(CFAssignRetained(token
, SecTokenCreate(token_id
, &auth_params
, error
)), out
);
1478 CFDictionaryRef attrs
= (attributes
!= NULL
) ? attributes
->dictionary
: NULL
;
1479 if(!perform(token
, query
->dictionary
, attrs
, auth_params
.dictionary
, error
)) {
1480 require_quiet((result
= SecItemCreatePairsFromError(error
, ac_pairs
)) == kSecItemAuthResultOK
, out
);
1483 result
= kSecItemAuthResultOK
;
1488 require_quiet(ok
, out
);
1493 CFReleaseSafe(token
);
1494 CFReleaseSafe(auth_params
.mutable_dictionary
);
1498 static bool cftype_to_bool_cftype_error_request(enum SecXPCOperation op
, CFTypeRef attributes
, CFTypeRef
*result
, CFErrorRef
*error
)
1500 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1501 return SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, attributes
, error
);
1502 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1504 return SecXPCDictionaryCopyPListOptional(response
, kSecXPCKeyResult
, result
, error
);
1510 static CFArrayRef
dict_to_array_error_request(enum SecXPCOperation op
, CFDictionaryRef attributes
, CFErrorRef
*error
)
1512 CFArrayRef result
= NULL
;
1513 bool success
= cftype_to_bool_cftype_error_request(op
, attributes
, (CFTypeRef
*)&result
, error
);
1514 if(success
&& !isArray(result
)){
1515 SecError(errSecUnimplemented
, error
, CFSTR("Unexpected nonarray returned: %@"), result
);
1516 CFReleaseNull(result
);
1521 bool cftype_client_to_bool_cftype_error_request(enum SecXPCOperation op
, CFTypeRef attributes
, __unused SecurityClient
*client
, CFTypeRef
*result
, CFErrorRef
*error
) {
1522 return cftype_to_bool_cftype_error_request(op
, attributes
, result
, error
);
1525 static bool dict_to_error_request(enum SecXPCOperation op
, CFDictionaryRef query
, CFErrorRef
*error
)
1527 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1528 return SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, query
, error
);
1532 static bool cfstring_array_to_error_request(enum SecXPCOperation op
, CFStringRef string
, CFArrayRef attributes
, __unused SecurityClient
*client
, CFErrorRef
*error
)
1534 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1536 if (!SecXPCDictionarySetString(message
, kSecXPCKeyString
, string
, error
))
1541 if (!SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, attributes
, error
))
1549 static bool dict_client_to_error_request(enum SecXPCOperation op
, CFDictionaryRef query
, __unused SecurityClient
*client
, CFErrorRef
*error
)
1551 return dict_to_error_request(op
, query
, error
);
1554 static bool SecTokenCreateAccessControlError(CFStringRef operation
, CFDataRef access_control
, CFErrorRef
*error
) {
1555 CFArrayRef ac_pair
= CFArrayCreateForCFTypes(NULL
, access_control
, operation
, NULL
);
1556 const void *ac_pairs
[] = { CFArrayCreateForCFTypes(NULL
, ac_pair
, NULL
) };
1557 const void *keys
[] = { CFNumberCreateWithCFIndex(NULL
, errSecAuthNeeded
) };
1558 CFAssignRetained(*error
, CFErrorCreateWithUserInfoKeysAndValues(NULL
, kSecErrorDomain
, errSecAuthNeeded
,
1559 keys
, ac_pairs
, 1));
1561 CFRelease(ac_pairs
[0]);
1566 static bool SecTokenProcessError(CFStringRef operation
, TKTokenRef token
, CFTypeRef object_or_attrs
, CFErrorRef
*error
) {
1567 if (CFEqualSafe(CFErrorGetDomain(*error
), CFSTR(kTKErrorDomain
)) &&
1568 CFErrorGetCode(*error
) == kTKErrorCodeAuthenticationNeeded
) {
1569 // Replace error with the one which is augmented with access control and operation which failed,
1570 // which will cause SecItemDoWithAuth to throw UI.
1571 // Create array containing tuple (array) with error and requested operation.
1572 CFDataRef access_control
= TKTokenCopyObjectAccessControl(token
, object_or_attrs
, error
);
1573 if (access_control
!= NULL
) {
1574 SecTokenCreateAccessControlError(operation
, access_control
, error
);
1575 CFRelease(access_control
);
1581 static CFTypeRef
SecTokenCopyUpdatedObjectID(TKTokenRef token
, CFDataRef object_id
, CFMutableDictionaryRef attributes
, CFErrorRef
*error
) {
1582 CFDataRef access_control
= NULL
, db_value
= NULL
, new_object_id
= NULL
, result
= NULL
;
1583 SecAccessControlRef ac
= NULL
;
1584 CFDictionaryRef old_attrs
= NULL
;
1586 // Make sure that ACL is bound - if not, generate an error which will trigger binding.
1587 CFDataRef ac_data
= CFDictionaryGetValue(attributes
, kSecAttrAccessControl
);
1588 if (ac_data
!= NULL
) {
1589 require_quiet(ac
= SecAccessControlCreateFromData(NULL
, ac_data
, error
), out
);
1590 require_action_quiet(SecAccessControlIsBound(ac
), out
,
1591 SecTokenCreateAccessControlError(CFSTR(""), ac_data
, error
));
1594 // Create or update the object on the token.
1595 old_attrs
= CFDictionaryCreateCopy(kCFAllocatorDefault
, attributes
);
1596 require_action_quiet(new_object_id
= TKTokenCreateOrUpdateObject(token
, object_id
, attributes
, error
), out
,
1597 SecTokenProcessError(kAKSKeyOpEncrypt
, token
, object_id
?: (CFTypeRef
)attributes
, error
));
1598 CFDictionaryForEach(old_attrs
, ^(const void *key
, const void *value
) {
1599 if (!CFEqual(key
, kSecValueData
)) {
1600 CFDictionaryAddValue(attributes
, key
, value
);
1604 // Prepare kSecValueData field for the item to be stored into the keychain DB.
1605 require_quiet(access_control
= TKTokenCopyObjectAccessControl(token
, new_object_id
, error
), out
);
1606 require_quiet(db_value
= SecTokenItemValueCreate(new_object_id
, access_control
,
1607 CFDictionaryGetValue(attributes
, kSecValueData
), error
), out
);
1608 CFDictionarySetValue(attributes
, kSecValueData
, db_value
);
1610 // kSecAttrAccessControl is handled directly by the token and stored inside data field.
1611 CFDictionaryRemoveValue(attributes
, kSecAttrAccessControl
);
1612 CFRetainAssign(result
, new_object_id
);
1616 CFReleaseSafe(access_control
);
1617 CFReleaseSafe(db_value
);
1618 CFReleaseSafe(old_attrs
);
1619 CFReleaseSafe(new_object_id
);
1623 static bool SecTokenItemAdd(TKTokenRef token
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
,
1624 CFTypeRef
*result
, CFErrorRef
*error
) {
1626 CFTypeRef object_id
= NULL
, ref
= NULL
;
1627 CFDictionaryRef ref_attrs
= NULL
;
1628 CFTypeRef db_result
= NULL
;
1629 CFDataRef db_value
= NULL
;
1630 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutableCopy(NULL
, 0, attributes
);
1632 CFDictionarySetValue(attrs
, kSecAttrAccessible
, kSecAttrAccessibleAlwaysPrivate
); //token items should be accesible always because have own ACL encoded in OID
1633 object_id
= CFRetainSafe(CFDictionaryGetValue(attrs
, kSecAttrTokenOID
));
1634 CFDictionaryRemoveValue(attrs
, kSecAttrTokenOID
);
1635 require_quiet(CFAssignRetained(object_id
, SecTokenCopyUpdatedObjectID(token
, object_id
, attrs
, error
)), out
);
1636 CFDictionaryRemoveValue(attrs
, kSecAttrTokenOID
);
1637 if (CFDictionaryContainsKey(attrs
, kSecValueRef
)) {
1638 // All attributes already had been extracted from valueRef, so do not go through that step again, just remove
1639 // the ref from the dictionary since it is of no use any more.
1640 CFDictionaryRemoveValue(attrs
, kSecValueRef
);
1642 // Augment attributes from default attributes of the related ref (SecKeyRef, SecCertificateRef). This is best done
1643 // by creating ref and getting back its attributes.
1644 require_quiet(SecTokenItemCreateFromAttributes(attrs
, auth_params
, token
, object_id
, &ref
, error
), out
);
1646 if ((ref_attrs
= SecItemCopyAttributeDictionary(ref
, false)) != NULL
) {
1647 CFDictionaryForEach(ref_attrs
, ^(const void *key
, const void *value
) {
1648 if (!CFEqual(key
, kSecValueData
)) {
1649 CFDictionaryAddValue(attrs
, key
, value
);
1656 // Make sure that both attributes and data are returned.
1657 CFDictionarySetValue(attrs
, kSecReturnAttributes
, kCFBooleanTrue
);
1658 CFDictionarySetValue(attrs
, kSecReturnData
, kCFBooleanTrue
);
1660 if (!CFEqualSafe(CFDictionaryGetValue(attrs
, kSecAttrIsPermanent
), kCFBooleanFalse
)) {
1661 // IsPermanent is not present or is true, so add item to the db.
1662 require_quiet(SECURITYD_XPC(sec_item_add
, cftype_client_to_bool_cftype_error_request
, attrs
,
1663 SecSecurityClientGet(), &db_result
, error
), out
);
1665 // Process directly result of token call.
1666 db_result
= CFRetain(attrs
);
1668 require_quiet(SecItemResultProcess(attributes
, auth_params
, token
, db_result
, result
, error
), out
);
1672 CFReleaseSafe(db_result
);
1673 CFReleaseSafe(db_value
);
1674 CFReleaseSafe(attrs
);
1675 CFReleaseSafe(ref_attrs
);
1676 CFReleaseSafe(object_id
);
1681 OSStatus
SecItemAdd(CFDictionaryRef attributes
, CFTypeRef
*result
) {
1682 __block SecCFDictionaryCOW attrs
= { attributes
};
1685 os_activity_t activity
= os_activity_create("SecItemAdd_ios", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
1686 os_activity_scope(activity
);
1687 os_release(activity
);
1689 require_quiet(!explode_identity(attrs
.dictionary
, (secitem_operation
)SecItemAdd
, &status
, result
), errOut
);
1690 infer_cert_label(&attrs
);
1692 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1693 return SecItemAuthDoQuery(&attrs
, NULL
, SecItemAdd
, error
, ^bool(TKTokenRef token
, CFDictionaryRef attributes
, CFDictionaryRef unused
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1694 if (token
== NULL
) {
1695 CFTypeRef raw_result
= NULL
;
1696 logUnreasonableDataLength(attributes
);
1697 if (!SECURITYD_XPC(sec_item_add
, cftype_client_to_bool_cftype_error_request
, attributes
, SecSecurityClientGet(), &raw_result
, error
))
1700 bool ok
= SecItemResultProcess(attributes
, auth_params
, token
, raw_result
, result
, error
);
1701 CFReleaseSafe(raw_result
);
1704 // Send request to an appropriate token instead of secd.
1705 return SecTokenItemAdd(token
, attributes
, auth_params
, result
, error
);
1711 CFReleaseSafe(attrs
.mutable_dictionary
);
1712 secdebug("secitem", "SecItemAdd returned: %d", (int)status
);
1718 OSStatus
SecItemCopyMatching(CFDictionaryRef inQuery
, CFTypeRef
*result
) {
1720 __block SecCFDictionaryCOW query
= { inQuery
};
1722 os_activity_t activity
= os_activity_create("SecItemCopyMatching_ios", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
1723 os_activity_scope(activity
);
1724 os_release(activity
);
1726 require_quiet(!explode_identity(query
.dictionary
, (secitem_operation
)SecItemCopyMatching
, &status
, result
), errOut
);
1728 bool wants_data
= cf_bool_value(CFDictionaryGetValue(query
.dictionary
, kSecReturnData
));
1729 bool wants_attributes
= cf_bool_value(CFDictionaryGetValue(query
.dictionary
, kSecReturnAttributes
));
1730 if ((wants_data
&& !wants_attributes
)) {
1731 // When either attributes or data are requested, we need to query both, because for token based items,
1732 // both are needed in order to generate proper data and/or attributes results.
1733 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query
), kSecReturnAttributes
, kCFBooleanTrue
);
1736 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1737 return SecItemAuthDoQuery(&query
, NULL
, SecItemCopyMatching
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1738 CFTypeRef raw_result
= NULL
;
1739 if (!SECURITYD_XPC(sec_item_copy_matching
, cftype_client_to_bool_cftype_error_request
, query
, SecSecurityClientGet(), &raw_result
, error
))
1742 // We intentionally pass NULL as token argument, because we want to be able to decide about token on which the item lives
1743 // on per-record basis, not wholesale. Logic inside SecItemResultCopyPrepared will open proper token according
1744 // to currently processed item.
1745 bool ok
= SecItemResultProcess(inQuery
, auth_params
, NULL
, raw_result
, result
, error
);
1746 CFReleaseSafe(raw_result
);
1752 secdebug("secitem", "SecItemCopyMatching_ios returned: %d", (int)status
);
1753 CFReleaseSafe(query
.mutable_dictionary
);
1757 // Invokes token-object handler for each item matching specified query.
1758 static bool SecTokenItemForEachMatching(CFDictionaryRef query
, CFErrorRef
*error
,
1759 bool (^perform
)(CFDictionaryRef item_value
, CFDictionaryRef item_query
,
1760 CFErrorRef
*error
)) {
1762 CFMutableDictionaryRef list_query
= NULL
;
1763 CFTypeRef items
= NULL
;
1764 CFArrayRef ref_array
= NULL
;
1765 CFDictionaryRef item_query
= NULL
, item_data
= NULL
;
1767 // Query all items with data and persistent_ref, so that we can extract objectIDs and also identify originating
1768 // items in the keychain.
1769 list_query
= CFDictionaryCreateMutableCopy(NULL
, 0, query
);
1770 if (CFDictionaryGetValue(list_query
, kSecMatchLimit
) == NULL
) {
1771 CFDictionarySetValue(list_query
, kSecMatchLimit
, kSecMatchLimitAll
);
1773 CFDictionarySetValue(list_query
, kSecReturnData
, kCFBooleanTrue
);
1774 CFDictionarySetValue(list_query
, kSecReturnPersistentRef
, kCFBooleanTrue
);
1775 require_quiet(SECURITYD_XPC(sec_item_copy_matching
, cftype_client_to_bool_cftype_error_request
, list_query
,
1776 SecSecurityClientGet(), &items
, error
), out
);
1777 if (CFGetTypeID(items
) != CFArrayGetTypeID()) {
1778 // Wrap single returned item into the array.
1779 CFArrayRef item_array
= CFArrayCreateForCFTypes(NULL
, items
, NULL
);
1780 CFAssignRetained(items
, item_array
);
1784 CFArrayForEachC(items
, item
) {
1785 CFDataRef data
= CFDictionaryGetValue(item
, kSecValueData
);
1786 require_action_quiet(data
!= NULL
, out
, SecError(errSecInternal
, error
, CFSTR("value not present for token item")));
1788 CFAssignRetained(item_data
, SecTokenItemValueCopy(data
, error
));
1789 require_quiet(item_data
, out
);
1791 CFAssignRetained(item_query
,
1792 CFDictionaryCreateForCFTypes(NULL
,
1793 kSecValuePersistentRef
, CFDictionaryGetValue(item
, kSecValuePersistentRef
),
1795 require_quiet(perform(item_data
, item_query
, error
), out
);
1801 CFReleaseSafe(list_query
);
1802 CFReleaseSafe(items
);
1803 CFReleaseSafe(item_data
);
1804 CFReleaseSafe(ref_array
);
1805 CFReleaseSafe(item_query
);
1809 static bool SecItemRawUpdate(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
, CFErrorRef
*error
) {
1812 // Ensure the dictionary passed to securityd has proper kCFTypeDictionaryKeyCallBacks.
1813 CFMutableDictionaryRef tmp
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
1814 CFDictionaryForEach(attributesToUpdate
, ^(const void *key
, const void *value
) { CFDictionaryAddValue(tmp
, key
, value
); });
1815 ok
= gSecurityd
->sec_item_update(query
, tmp
, SecSecurityClientGet(), error
);
1818 xpc_object_t message
= securityd_create_message(sec_item_update_id
, error
);
1820 if (SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, query
, error
) &&
1821 SecXPCDictionarySetPList(message
, kSecXPCKeyAttributesToUpdate
, attributesToUpdate
, error
)) {
1822 logUnreasonableDataLength(attributesToUpdate
);
1823 xpc_object_t reply
= securityd_message_with_reply_sync(message
, error
);
1825 ok
= securityd_message_no_error(reply
, error
);
1829 xpc_release(message
);
1835 static bool SecTokenItemUpdate(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
, CFErrorRef
*error
) {
1836 return SecTokenItemForEachMatching(query
, error
, ^bool(CFDictionaryRef object_data
, CFDictionaryRef item_query
, CFErrorRef
*error
) {
1838 CFDataRef object_id
= NULL
;
1839 CFMutableDictionaryRef db_value
= NULL
;
1841 // Update attributes on the token.
1842 CFMutableDictionaryRef attributes
= CFDictionaryCreateMutableCopy(NULL
, 0, attributesToUpdate
);
1843 require_quiet(object_id
= SecTokenCopyUpdatedObjectID(token
, CFDictionaryGetValue(object_data
, kSecTokenValueObjectIDKey
),
1844 attributes
, error
), out
);
1846 // Update attributes in the database.
1847 require_quiet(SecItemRawUpdate(item_query
, attributes
, error
), out
);
1852 CFReleaseSafe(object_id
);
1853 CFReleaseSafe(attributes
);
1854 CFReleaseSafe(db_value
);
1859 OSStatus
SecItemUpdate(CFDictionaryRef inQuery
, CFDictionaryRef inAttributesToUpdate
) {
1860 os_activity_t activity
= os_activity_create("SecItemUpdate_ios", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
1861 os_activity_scope(activity
);
1862 os_release(activity
);
1864 return SecOSStatusWith(^bool(CFErrorRef
*error
) {
1865 return SecItemUpdateWithError(inQuery
, inAttributesToUpdate
, error
);
1870 SecItemUpdateWithError(CFDictionaryRef inQuery
,
1871 CFDictionaryRef inAttributesToUpdate
,
1874 __block SecCFDictionaryCOW query
= { inQuery
};
1875 __block SecCFDictionaryCOW attributesToUpdate
= { inAttributesToUpdate
};
1876 bool result
= false;
1878 if (handleUpdateIdentity(inQuery
, inAttributesToUpdate
, &result
, error
))
1881 result
= SecItemAuthDoQuery(&query
, &attributesToUpdate
, SecItemUpdate
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1882 if (token
== NULL
) {
1883 return SecItemRawUpdate(query
, attributes
, error
);
1885 return SecTokenItemUpdate(token
, query
, attributes
, error
);
1890 CFReleaseSafe(query
.mutable_dictionary
);
1891 CFReleaseSafe(attributesToUpdate
.mutable_dictionary
);
1892 secdebug("secitem", "SecItemUpdateWithError returned: %d", (int)result
);
1896 static OSStatus
explode_persistent_identity_ref(SecCFDictionaryCOW
*query
)
1898 OSStatus status
= errSecSuccess
;
1899 CFTypeRef persist
= CFDictionaryGetValue(query
->dictionary
, kSecValuePersistentRef
);
1901 if (persist
&& _SecItemParsePersistentRef(persist
, &class, NULL
, NULL
)
1902 && CFEqual(class, kSecClassIdentity
)) {
1903 const void *keys
[] = { kSecReturnRef
, kSecValuePersistentRef
};
1904 const void *vals
[] = { kCFBooleanTrue
, persist
};
1905 CFDictionaryRef persistent_query
= CFDictionaryCreate(NULL
, keys
,
1906 vals
, (array_size(keys
)), NULL
, NULL
);
1907 CFTypeRef item_query
= NULL
;
1908 status
= SecItemCopyMatching(persistent_query
, &item_query
);
1909 CFReleaseNull(persistent_query
);
1912 if (item_query
== NULL
)
1913 return errSecItemNotFound
;
1915 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecValuePersistentRef
);
1916 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query
), kSecValueRef
, item_query
);
1917 CFRelease(item_query
);
1923 OSStatus
SecItemDelete(CFDictionaryRef inQuery
) {
1925 __block SecCFDictionaryCOW query
= { inQuery
};
1927 os_activity_t activity
= os_activity_create("SecItemDelete_ios", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
1928 os_activity_scope(activity
);
1929 os_release(activity
);
1931 require_noerr_quiet(status
= explode_persistent_identity_ref(&query
), errOut
);
1932 require_quiet(!explode_identity(query
.dictionary
, (secitem_operation
)SecItemDelete
, &status
, NULL
), errOut
);
1934 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1935 return SecItemAuthDoQuery(&query
, NULL
, SecItemDelete
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1936 if (token
== NULL
) {
1937 return SECURITYD_XPC(sec_item_delete
, dict_client_to_error_request
, query
, SecSecurityClientGet(), error
);
1939 return SecTokenItemForEachMatching(query
, error
, ^bool(CFDictionaryRef object_data
, CFDictionaryRef item_query
, CFErrorRef
*error
) {
1942 // Delete item from the token.
1943 CFDataRef object_id
= CFDictionaryGetValue(object_data
, kSecTokenValueObjectIDKey
);
1944 require_action_quiet(TKTokenDeleteObject(token
, object_id
, error
), out
,
1945 SecTokenProcessError(kAKSKeyOpDelete
, token
, object_id
, error
));
1947 // Delete the item from the keychain.
1948 require_quiet(SECURITYD_XPC(sec_item_delete
, dict_client_to_error_request
, item_query
,
1949 SecSecurityClientGet(), error
), out
);
1960 CFReleaseSafe(query
.mutable_dictionary
);
1961 secdebug("secitem", "SecItemDelete returned: %d", (int)status
);
1967 SecItemDeleteAll(void)
1969 return SecOSStatusWith(^bool (CFErrorRef
*error
) {
1972 if (!gSecurityd
->sec_item_delete_all(error
))
1973 ok
&= SecError(errSecInternal
, error
, CFSTR("sec_item_delete_all is NULL"));
1975 ok
&= securityd_send_sync_and_do(sec_delete_all_id
, error
, NULL
, NULL
);
1983 agrps_client_to_error_request(enum SecXPCOperation op
, CFArrayRef agrps
, __unused SecurityClient
*client
, CFErrorRef
*error
)
1985 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1986 return SecXPCDictionarySetPList(message
, kSecXPCKeyAccessGroups
, agrps
, error
);
1991 bool SecItemDeleteAllWithAccessGroups(CFArrayRef accessGroups
, CFErrorRef
*error
) {
1993 os_activity_t activity
= os_activity_create("SecItemDeleteAllWithAccessGroups", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
1994 os_activity_scope(activity
);
1995 os_release(activity
);
1997 return SECURITYD_XPC(sec_delete_items_with_access_groups
, agrps_client_to_error_request
, accessGroups
,
1998 SecSecurityClientGet(), error
);
2005 #if SECITEM_SHIM_OSX
2006 SecItemUpdateTokenItems_ios(CFTypeRef tokenID
, CFArrayRef tokenItemsAttributes
)
2008 SecItemUpdateTokenItems(CFTypeRef tokenID
, CFArrayRef tokenItemsAttributes
)
2013 os_activity_t activity
= os_activity_create("SecItemUpdateTokenItems_ios", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
2014 os_activity_scope(activity
);
2015 os_release(activity
);
2017 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
2018 CFArrayRef tmpArrayRef
= tokenItemsAttributes
;
2019 if (tokenItemsAttributes
) {
2020 CFMutableArrayRef tokenItems
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
2021 for (CFIndex i
= 0; i
< CFArrayGetCount(tokenItemsAttributes
); ++i
) {
2022 CFDictionaryRef itemAttributes
= CFArrayGetValueAtIndex(tokenItemsAttributes
, i
);
2023 CFTypeRef accessControl
= CFDictionaryGetValue(itemAttributes
, kSecAttrAccessControl
);
2024 CFTypeRef tokenOID
= CFDictionaryGetValue(itemAttributes
, kSecAttrTokenOID
);
2025 CFTypeRef valueData
= CFDictionaryGetValue(itemAttributes
, kSecValueData
);
2026 if (tokenOID
!= NULL
&& accessControl
!= NULL
&& CFDataGetTypeID() == CFGetTypeID(accessControl
)) {
2027 CFDataRef data
= SecTokenItemValueCreate(tokenOID
, accessControl
, valueData
, error
);
2029 CFRelease(tokenItems
);
2033 CFMutableDictionaryRef attributes
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, itemAttributes
);
2034 CFDictionarySetValue(attributes
, kSecValueData
, data
);
2035 CFDictionarySetValue(attributes
, kSecAttrTokenID
, tokenID
);
2036 CFDictionaryRemoveValue(attributes
, kSecAttrAccessControl
);
2037 CFDictionaryRemoveValue(attributes
, kSecAttrTokenOID
);
2038 CFArrayAppendValue(tokenItems
, attributes
);
2039 CFRelease(attributes
);
2043 CFArrayAppendValue(tokenItems
, itemAttributes
);
2046 tmpArrayRef
= tokenItems
;
2049 return SECURITYD_XPC(sec_item_update_token_items
, cfstring_array_to_error_request
, tokenID
, tmpArrayRef
, SecSecurityClientGet(), error
);
2055 CFArrayRef
_SecKeychainSyncUpdateMessage(CFDictionaryRef updates
, CFErrorRef
*error
) {
2056 __block CFArrayRef result
;
2057 os_activity_initiate("_SecKeychainSyncUpdateMessage", OS_ACTIVITY_FLAG_DEFAULT
, ^{
2058 result
= SECURITYD_XPC(sec_keychain_sync_update_message
, dict_to_array_error_request
, updates
, error
);
2063 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
2065 bool _SecKeychainRollKeys(bool force
, CFErrorRef
*error
)
2067 do_if_registered(sec_roll_keys
, force
, error
);
2069 __block
bool result
= false;
2071 secdebug("secitem","enter - %s", __FUNCTION__
);
2072 securityd_send_sync_and_do(kSecXPCOpRollKeys
, error
,
2073 ^bool(xpc_object_t message
, CFErrorRef
*error
) {
2074 xpc_dictionary_set_bool(message
, "force", force
);
2077 ^bool(xpc_object_t response
, __unused CFErrorRef
*error
) {
2078 result
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);
2084 static CFArrayRef
data_array_to_array_error_request(enum SecXPCOperation op
, CFDataRef normalizedIssuer
, CFArrayRef accessGroups
, CFErrorRef
*error
) {
2085 __block CFArrayRef results
= NULL
;
2086 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
2087 SecXPCDictionarySetData(message
, kSecXPCKeyNormalizedIssuer
, normalizedIssuer
, error
);
2088 SecXPCDictionarySetPList(message
, kSecXPCKeyAccessGroups
, accessGroups
, error
);
2090 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
2091 return SecXPCDictionaryCopyArrayOptional(response
, kSecXPCKeyResult
, &results
, error
);
2096 static bool data_data_array_to_bool_error_request(enum SecXPCOperation op
, CFDataRef normalizedIssuer
, CFDataRef serialNumber
, CFArrayRef accessGroups
, CFErrorRef
*error
) {
2097 __block
bool result
= false;
2098 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
2099 SecXPCDictionarySetData(message
, kSecXPCKeyNormalizedIssuer
, normalizedIssuer
, error
);
2100 SecXPCDictionarySetData(message
, kSecXPCKeySerialNumber
, serialNumber
, error
);
2101 SecXPCDictionarySetPList(message
, kSecXPCKeyAccessGroups
, accessGroups
, error
);
2103 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
2104 result
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);
2110 CFArrayRef
SecItemCopyParentCertificates_ios(CFDataRef normalizedIssuer
, CFArrayRef accessGroups
, CFErrorRef
*error
) {
2111 CFArrayRef results
= NULL
;
2113 os_activity_t activity
= os_activity_create("SecItemCopyParentCertificates_ios", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
2114 os_activity_scope(activity
);
2115 os_release(activity
);
2117 results
= SECURITYD_XPC(sec_item_copy_parent_certificates
, data_array_to_array_error_request
, normalizedIssuer
, accessGroups
, error
);
2122 bool SecItemCertificateExists(CFDataRef normalizedIssuer
, CFDataRef serialNumber
, CFArrayRef accessGroups
, CFErrorRef
*error
) {
2123 bool results
= false;
2125 os_activity_t activity
= os_activity_create("SecItemCertificateExists", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
2126 os_activity_scope(activity
);
2127 os_release(activity
);
2129 results
= SECURITYD_XPC(sec_item_certificate_exists
, data_data_array_to_bool_error_request
, normalizedIssuer
, serialNumber
, accessGroups
, error
);