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>
73 #include <utilities/simulatecrash_assert.h>
74 #include <libaks_acl_cf_keys.h>
75 #include <os/activity.h>
78 #include <os/feature_private.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>
99 #include <ctkclient/ctkclient.h>
101 #include "SecItemRateLimit.h"
104 * See corresponding definition in SecDbKeychainItemV7. This is the unserialized
105 * maximum, so the daemon's limit is not exactly the same.
107 #define REASONABLE_DATA_SIZE 4096
109 const CFStringRef kSecNetworkExtensionAccessGroupSuffix
= CFSTR("com.apple.networkextensionsharing");
111 /* Return an OSStatus for a sqlite3 error code. */
112 static OSStatus
osstatus_for_s3e(int s3e
)
118 return errSecSuccess
;
119 case SQLITE_READONLY
:
120 return errSecReadOnly
;
121 case SQLITE_CONSTRAINT
:
122 return errSecDuplicateItem
;
123 case SQLITE_ABORT
: // There is no errSecOperationCancelled
125 case SQLITE_MISMATCH
:
126 return errSecNoSuchAttr
;
128 return errSecAllocate
;
131 case SQLITE_INTERNAL
:
132 return errSecInternalComponent
;
133 case SQLITE_FULL
: // Happens if we run out of uniqueids or disk is full (TODO: replace with better code)
134 case SQLITE_PERM
: // No acess permission
135 case SQLITE_AUTH
: // No authorization (e.g. no class key for file)
136 case SQLITE_CANTOPEN
: // can be several reasons for this. Caller should sqlite3_system_errno()
137 case SQLITE_EMPTY
: // SQLite does not seem to use this. Was already here, so keeping
140 return errSecNotAvailable
;
144 static OSStatus
osstatus_for_kern_return(CFIndex kernResult
)
149 return errSecSuccess
;
150 case kIOReturnNotReadable
:
151 case kIOReturnNotWritable
:
152 return errSecAuthFailed
;
153 case kIOReturnNotPermitted
:
154 case kIOReturnNotPrivileged
:
155 case kIOReturnLockedRead
:
156 case kIOReturnLockedWrite
:
157 return errSecInteractionNotAllowed
;
160 case kIOReturnBadArgument
:
163 return errSecNotAvailable
; /* TODO: Replace with a real error code. */
167 static OSStatus
osstatus_for_xpc_error(CFIndex xpcError
) {
170 case kSecXPCErrorSuccess
:
171 return errSecSuccess
;
172 case kSecXPCErrorUnexpectedType
:
173 case kSecXPCErrorUnexpectedNull
:
175 case kSecXPCErrorConnectionFailed
:
176 return errSecNotAvailable
;
177 case kSecXPCErrorUnknown
:
179 return errSecInternal
;
183 static OSStatus
osstatus_for_der_error(CFIndex derError
) {
186 case kSecDERErrorUnknownEncoding
:
187 case kSecDERErrorUnsupportedDERType
:
188 case kSecDERErrorUnsupportedNumberType
:
190 case kSecDERErrorUnsupportedCFObject
:
192 case kSecDERErrorAllocationFailure
:
193 return errSecAllocate
;
195 return errSecInternal
;
199 static OSStatus
osstatus_for_localauthentication_error(CFIndex laError
) {
200 // Wrap LA error in Sec error.
202 case kLAErrorUserCancel
:
203 return errSecUserCanceled
;
204 case kLAErrorParameter
:
206 case kLAErrorNotInteractive
:
207 return errSecInteractionNotAllowed
;
209 return errSecAuthFailed
;
213 static OSStatus
osstatus_for_ctk_error(CFIndex ctkError
) {
215 case kTKErrorCodeBadParameter
:
217 case kTKErrorCodeNotImplemented
:
218 return errSecUnimplemented
;
219 case kTKErrorCodeCanceledByUser
:
220 return errSecUserCanceled
;
221 case kTKErrorCodeCorruptedData
:
223 case kTKErrorCodeTokenNotFound
:
224 case kTKErrorCodeObjectNotFound
:
225 return errSecItemNotFound
;
227 return errSecInternal
;
232 // Convert from securityd error codes to OSStatus for legacy API.
233 OSStatus
SecErrorGetOSStatus(CFErrorRef error
) {
236 status
= errSecSuccess
;
238 CFStringRef domain
= CFErrorGetDomain(error
);
239 if (domain
== NULL
) {
240 secerror("No error domain for error: %@", error
);
241 status
= errSecInternal
;
242 } else if (CFEqual(kSecErrorDomain
, domain
)) {
243 status
= (OSStatus
)CFErrorGetCode(error
);
244 } else if (CFEqual(kSecDbErrorDomain
, domain
)) {
245 status
= osstatus_for_s3e((int)CFErrorGetCode(error
));
246 } else if (CFEqual(kSecErrnoDomain
, domain
)) {
247 status
= (OSStatus
)CFErrorGetCode(error
);
248 } else if (CFEqual(kSecKernDomain
, domain
)) {
249 status
= osstatus_for_kern_return(CFErrorGetCode(error
));
250 } else if (CFEqual(sSecXPCErrorDomain
, domain
)) {
251 status
= osstatus_for_xpc_error(CFErrorGetCode(error
));
252 } else if (CFEqual(sSecDERErrorDomain
, domain
)) {
253 status
= osstatus_for_der_error(CFErrorGetCode(error
));
254 } else if (CFEqual(CFSTR(kLAErrorDomain
), domain
)) {
255 status
= osstatus_for_localauthentication_error(CFErrorGetCode(error
));
256 } else if (CFEqual(CFSTR(kTKErrorDomain
), domain
)) {
257 status
= osstatus_for_ctk_error(CFErrorGetCode(error
));
258 } else if (CFEqual(kSOSErrorDomain
, domain
)) {
259 status
= errSecInternal
;
261 secnotice("securityd", "unknown error domain: %@ for error: %@", domain
, error
);
262 status
= errSecInternal
;
269 lastErrorReleaseError(void *value
)
276 getLastErrorKey(pthread_key_t
*kv
)
278 static pthread_key_t key
;
279 static bool haveKey
= false;
280 static dispatch_once_t onceToken
;
281 dispatch_once(&onceToken
, ^{
282 if (pthread_key_create(&key
, lastErrorReleaseError
) == 0)
290 SetLastError(CFErrorRef newError
)
293 if (!getLastErrorKey(&key
))
295 CFErrorRef oldError
= pthread_getspecific(key
);
300 pthread_setspecific(key
, newError
);
304 SecCopyLastError(OSStatus status
)
309 if (!getLastErrorKey(&key
))
312 error
= pthread_getspecific(key
);
314 if (status
&& status
!= SecErrorGetOSStatus(error
)) {
323 // Wrapper to provide a CFErrorRef for legacy API.
324 OSStatus
SecOSStatusWith(bool (^perform
)(CFErrorRef
*error
)) {
325 CFErrorRef error
= NULL
;
327 if (perform(&error
)) {
328 assert(error
== NULL
);
330 status
= errSecSuccess
;
334 status
= SecErrorGetOSStatus(error
);
335 if (status
!= errSecItemNotFound
) // Occurs in normal operation, so exclude
336 secinfo("OSStatus", "error:[%" PRIdOSStatus
"] %@", status
, error
);
337 CFReleaseNull(error
);
343 logUnreasonableDataLength(CFDictionaryRef attributes
)
348 if (isDictionary(attributes
)) {
349 data
= CFDictionaryGetValue(attributes
, kSecValueData
);
351 length
= CFDataGetLength(data
);
352 if (length
> REASONABLE_DATA_SIZE
) {
353 // This log message is vague, as we may not know anything else about the item.
354 // securityd logging (correlate by activity ID) will have more information.
355 secwarning("keychain item data exceeds reasonable size (%lu bytes)", (unsigned long)length
);
361 /* Drop assorted kSecAttrCanXxxx attributes from the query, because these attributes are generated
362 by SecKey implementation and may differ between OS versions, see <rdar://problem/27095761>.
365 static CFDictionaryRef
366 AttributeCreateFilteredOutSecAttrs(CFDictionaryRef attributes
)
368 CFMutableDictionaryRef filtered
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, attributes
);
369 if (filtered
== NULL
)
371 CFDictionaryRemoveValue(filtered
, kSecAttrCanSign
);
372 CFDictionaryRemoveValue(filtered
, kSecAttrCanVerify
);
373 CFDictionaryRemoveValue(filtered
, kSecAttrCanEncrypt
);
374 CFDictionaryRemoveValue(filtered
, kSecAttrCanDecrypt
);
375 CFDictionaryRemoveValue(filtered
, kSecAttrCanDerive
);
376 CFDictionaryRemoveValue(filtered
, kSecAttrCanWrap
);
377 CFDictionaryRemoveValue(filtered
, kSecAttrCanUnwrap
);
378 CFDictionaryRemoveValue(filtered
, kSecAttrCanSignRecover
);
379 CFDictionaryRemoveValue(filtered
, kSecAttrCanVerifyRecover
);
380 CFDictionaryRemoveValue(filtered
, kSecAttrIsPermanent
);
386 /* IPC uses CFPropertyList to un/marshall input/output data and can handle:
387 CFData, CFString, CFArray, CFDictionary, CFDate, CFBoolean, and CFNumber
389 Currently in need of conversion below:
390 @@@ kSecValueRef allows SecKeychainItemRef and SecIdentityRef
391 @@@ kSecUseItemList allows a query against a list of itemrefs, this isn't
392 currently implemented at all, but when it is needs to short circuit to
393 local evaluation, different from the sql query abilities
396 static CFDictionaryRef
397 SecItemCopyAttributeDictionary(CFTypeRef ref
, bool forQuery
) {
398 CFDictionaryRef refDictionary
= NULL
;
399 CFTypeID typeID
= CFGetTypeID(ref
);
400 if (typeID
== SecKeyGetTypeID()) {
401 refDictionary
= SecKeyCopyAttributeDictionary((SecKeyRef
)ref
);
402 if (refDictionary
&& forQuery
) {
403 CFDictionaryRef filtered
= AttributeCreateFilteredOutSecAttrs(refDictionary
);
404 CFAssignRetained(refDictionary
, filtered
);
406 } else if (typeID
== SecCertificateGetTypeID()) {
407 refDictionary
= SecCertificateCopyAttributeDictionary((SecCertificateRef
)ref
);
408 } else if (typeID
== SecIdentityGetTypeID()) {
410 SecIdentityRef identity
= (SecIdentityRef
)ref
;
411 SecCertificateRef cert
= NULL
;
412 SecKeyRef key
= NULL
;
413 if (!SecIdentityCopyCertificate(identity
, &cert
) &&
414 !SecIdentityCopyPrivateKey(identity
, &key
))
416 CFDataRef data
= SecCertificateCopyData(cert
);
417 CFDictionaryRef key_dict
= SecKeyCopyAttributeDictionary(key
);
419 if (key_dict
&& data
) {
420 refDictionary
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, key_dict
);
421 CFDictionarySetValue((CFMutableDictionaryRef
)refDictionary
, kSecAttrIdentityCertificateData
, data
);
423 CFReleaseNull(key_dict
);
429 return refDictionary
;
432 #ifdef SECITEM_SHIM_OSX
433 extern CFTypeRef
SecItemCreateFromAttributeDictionary_osx(CFDictionaryRef refAttributes
);
437 SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes
) {
438 CFTypeRef ref
= NULL
;
439 CFStringRef
class = CFDictionaryGetValue(refAttributes
, kSecClass
);
440 if (CFEqual(class, kSecClassKey
)) {
441 ref
= SecKeyCreateFromAttributeDictionary(refAttributes
);
442 } else if (CFEqual(class, kSecClassCertificate
)) {
443 ref
= SecCertificateCreateFromAttributeDictionary(refAttributes
);
444 } else if (CFEqual(class, kSecClassIdentity
)) {
445 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecAttrIdentityCertificateData
);
446 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
);
447 SecKeyRef key
= SecKeyCreateFromAttributeDictionary(refAttributes
);
449 ref
= SecIdentityCreate(kCFAllocatorDefault
, cert
, key
);
452 secerror("SecItem: failed to create identity");
457 #ifdef SECITEM_SHIM_OSX
459 ref
= SecItemCreateFromAttributeDictionary_osx(refAttributes
);
465 typedef OSStatus (*secitem_operation
)(CFDictionaryRef attributes
, CFTypeRef
*result
);
467 static bool explode_identity(CFDictionaryRef attributes
, secitem_operation operation
,
468 OSStatus
*return_status
, CFTypeRef
*return_result
)
470 bool handled
= false;
471 CFTypeRef value
= CFDictionaryGetValue(attributes
, kSecValueRef
);
473 CFTypeID typeID
= CFGetTypeID(value
);
474 if (typeID
== SecIdentityGetTypeID()) {
476 OSStatus status
= errSecSuccess
;
477 SecIdentityRef identity
= (SecIdentityRef
)value
;
478 SecCertificateRef cert
= NULL
;
479 SecKeyRef key
= NULL
;
480 if (!SecIdentityCopyCertificate(identity
, &cert
) &&
481 !SecIdentityCopyPrivateKey(identity
, &key
))
483 CFMutableDictionaryRef partial_query
=
484 CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, attributes
);
485 CFDictionarySetValue(partial_query
, kSecValueRef
, cert
);
486 CFTypeRef result
= NULL
;
487 bool duplicate_cert
= false;
488 /* an identity is first and foremost a key, but it can have multiple
489 certs associated with it: so we identify it by the cert */
490 status
= operation(partial_query
, return_result
? &result
: NULL
);
491 if ((operation
== (secitem_operation
)SecItemAdd
) &&
492 (status
== errSecDuplicateItem
)) {
493 duplicate_cert
= true;
494 status
= errSecSuccess
;
497 if (!status
|| status
== errSecItemNotFound
) {
498 bool skip_key_operation
= false;
500 /* if the key is still in use, skip deleting it */
501 if (operation
== (secitem_operation
)SecItemDelete
) {
502 // find certs with cert.pkhh == keys.klbl
503 CFDictionaryRef key_dict
= NULL
, query_dict
= NULL
;
504 CFDataRef pkhh
= NULL
;
506 key_dict
= SecKeyCopyAttributeDictionary(key
);
508 pkhh
= (CFDataRef
)CFDictionaryGetValue(key_dict
, kSecAttrApplicationLabel
);
509 const void *keys
[] = { kSecClass
, kSecAttrPublicKeyHash
};
510 const void *vals
[] = { kSecClassCertificate
, pkhh
};
512 query_dict
= CFDictionaryCreate(NULL
, keys
,
513 vals
, (array_size(keys
)),
516 if (errSecSuccess
== SecItemCopyMatching(query_dict
, NULL
))
517 skip_key_operation
= true;
518 CFReleaseSafe(query_dict
);
519 CFReleaseSafe(key_dict
);
522 if (!skip_key_operation
) {
523 /* now perform the operation for the key */
524 CFDictionarySetValue(partial_query
, kSecValueRef
, key
);
525 CFDictionarySetValue(partial_query
, kSecReturnPersistentRef
, kCFBooleanFalse
);
527 status
= operation(partial_query
, NULL
);
528 if ((operation
== (secitem_operation
)SecItemAdd
) &&
529 (status
== errSecDuplicateItem
) &&
531 status
= errSecSuccess
;
534 /* add and copy matching for an identityref have a persistent ref result */
537 /* result is a persistent ref to a cert */
538 sqlite_int64 rowid
= -1;
539 CFDictionaryRef tokenAttrs
= NULL
;
540 if (_SecItemParsePersistentRef(result
, NULL
, &rowid
, &tokenAttrs
)) {
541 *return_result
= _SecItemCreatePersistentRef(kSecClassIdentity
, rowid
, tokenAttrs
);
543 CFReleaseNull(tokenAttrs
);
548 CFReleaseNull(partial_query
);
551 status
= errSecInvalidItemRef
;
555 *return_status
= status
;
558 value
= CFDictionaryGetValue(attributes
, kSecClass
);
559 if (value
&& CFEqual(kSecClassIdentity
, value
) &&
560 (operation
== (secitem_operation
)SecItemDelete
)) {
561 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, attributes
);
562 CFDictionaryRemoveValue(dict
, kSecClass
);
563 CFDictionarySetValue(dict
, kSecClass
, kSecClassCertificate
);
564 OSStatus status
= SecItemDelete(dict
);
566 CFDictionarySetValue(dict
, kSecClass
, kSecClassKey
);
567 status
= SecItemDelete(dict
);
570 *return_status
= status
;
578 SecErrorPropagateLastError(OSStatus status
, CFErrorRef
*error
)
581 CFErrorRef lastError
= SecCopyLastError(status
);
583 CFErrorPropagate(lastError
, error
);
585 SecError(status
, error
, CFSTR("SecError: error not captured, OSStatus was: %d"), (int)status
);
592 handleUpdateIdentity(CFDictionaryRef query
,
593 CFDictionaryRef update
,
597 CFMutableDictionaryRef updatedQuery
= NULL
;
598 SecCertificateRef cert
= NULL
;
599 SecKeyRef key
= NULL
;
600 bool handled
= false;
604 CFTypeRef value
= CFDictionaryGetValue(query
, kSecValueRef
);
606 CFTypeID typeID
= CFGetTypeID(value
);
607 if (typeID
== SecIdentityGetTypeID()) {
608 SecIdentityRef identity
= (SecIdentityRef
)value
;
613 status
= SecIdentityCopyCertificate(identity
, &cert
);
614 require_noerr_action_quiet(status
, errOut
, SecErrorPropagateLastError(status
, error
));
616 status
= SecIdentityCopyPrivateKey(identity
, &key
);
617 require_noerr_action_quiet(status
, errOut
, SecErrorPropagateLastError(status
, error
));
619 updatedQuery
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, query
);
620 require_action_quiet(updatedQuery
, errOut
, *result
= false);
622 CFDictionarySetValue(updatedQuery
, kSecValueRef
, cert
);
623 require_quiet(SecItemUpdateWithError(updatedQuery
, update
, error
), errOut
);
625 CFDictionarySetValue(updatedQuery
, kSecValueRef
, key
);
626 require_quiet(SecItemUpdateWithError(updatedQuery
, update
, error
), errOut
);
630 value
= CFDictionaryGetValue(query
, kSecClass
);
631 if (value
&& CFEqual(kSecClassIdentity
, value
)) {
634 updatedQuery
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, query
);
635 require_action_quiet(updatedQuery
, errOut
, *result
= false);
637 CFDictionarySetValue(updatedQuery
, kSecClass
, kSecClassCertificate
);
638 require_quiet(SecItemUpdateWithError(updatedQuery
, update
, error
), errOut
);
640 CFDictionarySetValue(updatedQuery
, kSecClass
, kSecClassKey
);
641 require_quiet(SecItemUpdateWithError(updatedQuery
, update
, error
), errOut
);
643 CFReleaseNull(updatedQuery
);
648 CFReleaseNull(updatedQuery
);
654 static void infer_cert_label(SecCFDictionaryCOW
*attributes
)
656 if (!CFDictionaryContainsKey(attributes
->dictionary
, kSecAttrLabel
)) {
657 CFTypeRef value_ref
= CFDictionaryGetValue(attributes
->dictionary
, kSecValueRef
);
658 if (value_ref
&& CFGetTypeID(value_ref
) == SecCertificateGetTypeID()) {
659 SecCertificateRef certificate
= (SecCertificateRef
)value_ref
;
660 CFStringRef label
= SecCertificateCopySubjectSummary(certificate
);
662 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes
), kSecAttrLabel
, label
);
663 CFReleaseNull(label
);
669 static CFDataRef
CreateTokenPersistentRefData(CFTypeRef
class, CFDictionaryRef attributes
) {
670 CFDataRef tokenPersistentRef
= NULL
;
671 CFStringRef tokenID
= NULL
;
672 CFDataRef tokenData
= NULL
;
673 CFDataRef oid
= NULL
;
674 CFDictionaryRef itemValue
= NULL
;
676 oid
= CFDictionaryGetValue(attributes
, kSecAttrTokenOID
);
678 require_quiet(tokenID
= CFCast(CFString
, CFDictionaryGetValue(attributes
, kSecAttrTokenID
)), out
);
680 // Identities are identified by their contained certificate, so we need to get tokenID and OID from certificate,
682 if (CFEqual(class, kSecClassIdentity
) && oid
== NULL
) {
683 require_quiet(tokenID
= CFCast(CFString
, CFDictionaryGetValue(attributes
, kSecAttrIdentityCertificateTokenID
)), out
);
684 require_quiet(tokenData
= CFCast(CFData
, CFDictionaryGetValue(attributes
, kSecAttrIdentityCertificateData
)), out
);
686 require_quiet(tokenID
= CFCast(CFString
, CFDictionaryGetValue(attributes
, kSecAttrTokenID
)), out
);
687 require_quiet(tokenData
= CFCast(CFData
, CFDictionaryGetValue(attributes
, kSecValueData
)), out
);
689 require_quiet(itemValue
= SecTokenItemValueCopy(tokenData
, NULL
), out
);
690 require_quiet(oid
= CFDictionaryGetValue(itemValue
, kSecTokenValueObjectIDKey
), out
);
691 require_quiet(CFCast(CFData
, oid
) != NULL
, out
);
693 CFArrayRef array
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, class, tokenID
, oid
, NULL
);
694 tokenPersistentRef
= CFPropertyListCreateDERData(kCFAllocatorDefault
, array
, NULL
);
697 CFReleaseNull(itemValue
);
698 return tokenPersistentRef
;
701 static const uint8_t tk_persistent_ref_id
[] = {'t', 'k', 'p', 'r'};
702 /* A persistent ref is just the class and the rowid of the record.
703 Persistent ref for token items is a der blob with class, tokenID and objectId. */
704 CFDataRef
_SecItemCreatePersistentRef(CFTypeRef
class, sqlite_int64 rowid
, CFDictionaryRef attributes
)
706 CFDataRef result
= NULL
;
707 CFDataRef tokenPersistentRef
= NULL
;
708 if (attributes
!= NULL
) {
709 tokenPersistentRef
= CreateTokenPersistentRefData(class, attributes
);
712 if (tokenPersistentRef
!= NULL
) {
713 CFMutableDataRef tmpData
= CFDataCreateMutable(kCFAllocatorDefault
, sizeof(tk_persistent_ref_id
) + CFDataGetLength(tokenPersistentRef
));
714 CFDataAppendBytes(tmpData
, tk_persistent_ref_id
, sizeof(tk_persistent_ref_id
));
715 CFDataAppend(tmpData
, tokenPersistentRef
);
716 CFReleaseNull(tokenPersistentRef
);
719 require(rowid
>= 0, out
);
720 uint8_t bytes
[sizeof(sqlite_int64
) + 4];
721 if (CFStringGetCString(class, (char *)bytes
, 4 + 1 /*null-term*/,
722 kCFStringEncodingUTF8
))
724 OSWriteBigInt64(bytes
+ 4, 0, rowid
);
725 result
= CFDataCreate(NULL
, bytes
, sizeof(bytes
));
732 static Boolean
isValidClass(CFStringRef
class, CFStringRef
*return_class
) {
733 const void *valid_classes
[] = { kSecClassGenericPassword
,
734 kSecClassInternetPassword
,
735 kSecClassAppleSharePassword
,
736 kSecClassCertificate
,
740 for (size_t i
= 0; i
< array_size(valid_classes
); i
++) {
741 if (CFEqual(valid_classes
[i
], class)) {
743 *return_class
= valid_classes
[i
];
751 static bool ParseTokenPersistentRefData(CFDataRef persistent_ref
, CFStringRef
*return_class
, CFDictionaryRef
*return_token_attrs
) {
752 bool valid_ref
= false;
753 CFPropertyListRef pl
= NULL
;
754 const uint8_t *der
= CFDataGetBytePtr(persistent_ref
) + sizeof(tk_persistent_ref_id
);
755 const uint8_t *der_end
= der
+ (CFDataGetLength(persistent_ref
) - sizeof(tk_persistent_ref_id
));
756 require_quiet(der
= der_decode_plist(0, &pl
, NULL
, der
, der_end
), out
);
757 require_quiet(der
== der_end
, out
);
758 require_quiet(CFGetTypeID(pl
) == CFArrayGetTypeID(), out
);
759 require_quiet(CFArrayGetCount(pl
) == 3, out
);
760 require_quiet(valid_ref
= isValidClass(CFArrayGetValueAtIndex(pl
, 0), return_class
), out
);
761 if (return_token_attrs
) {
762 *return_token_attrs
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
763 kSecAttrTokenID
, CFArrayGetValueAtIndex(pl
, 1),
764 kSecAttrTokenOID
, CFArrayGetValueAtIndex(pl
, 2), NULL
);
771 /* AUDIT[securityd](done):
772 persistent_ref (ok) is a caller provided, non NULL CFTypeRef.
774 bool _SecItemParsePersistentRef(CFDataRef persistent_ref
, CFStringRef
*return_class
, sqlite_int64
*return_rowid
, CFDictionaryRef
*return_token_attrs
)
776 bool valid_ref
= false;
777 require(CFGetTypeID(persistent_ref
) == CFDataGetTypeID(), out
);
779 if (CFDataGetLength(persistent_ref
) > (CFIndex
)sizeof(tk_persistent_ref_id
) &&
780 memcmp(tk_persistent_ref_id
, CFDataGetBytePtr(persistent_ref
), sizeof(tk_persistent_ref_id
)) == 0) {
781 valid_ref
= ParseTokenPersistentRefData(persistent_ref
, return_class
, return_token_attrs
);
782 } else if (CFDataGetLength(persistent_ref
) == (CFIndex
)(sizeof(sqlite_int64
) + 4)) {
783 const uint8_t *bytes
= CFDataGetBytePtr(persistent_ref
);
784 sqlite_int64 rowid
= OSReadBigInt64(bytes
+ 4, 0);
786 CFStringRef
class = CFStringCreateWithBytes(kCFAllocatorDefault
,
787 bytes
, CFStringGetLength(kSecClassGenericPassword
),
788 kCFStringEncodingUTF8
, true);
790 if ((valid_ref
= isValidClass(class, return_class
))) {
792 *return_rowid
= rowid
;
800 static bool cf_bool_value(CFTypeRef cf_bool
) {
801 return cf_bool
&& CFBooleanGetValue(cf_bool
);
804 CFMutableDictionaryRef
SecCFDictionaryCOWGetMutable(SecCFDictionaryCOW
*cow_dictionary
) {
805 if (cow_dictionary
->mutable_dictionary
== NULL
) {
806 cow_dictionary
->mutable_dictionary
= CFDictionaryCreateMutableForCFTypes(NULL
);
807 if (cow_dictionary
->dictionary
!= NULL
) {
808 CFDictionaryForEach(cow_dictionary
->dictionary
, ^(const void *key
, const void *value
) {
809 CFDictionarySetValue(cow_dictionary
->mutable_dictionary
, key
, value
);
812 cow_dictionary
->dictionary
= cow_dictionary
->mutable_dictionary
;
815 return cow_dictionary
->mutable_dictionary
;
818 // Creates kSecValueData field stored in the DB for token-based items. Data field consists of objectID, real
819 // access_control and optionally of the data value.
820 static CFDataRef
SecTokenItemValueCreate(CFDataRef oid
, CFDataRef access_control
, CFDataRef object_value
, CFErrorRef
*error
) {
821 CFMutableDictionaryRef value
= NULL
;
822 value
= CFDictionaryCreateMutableForCFTypesWith(NULL
,
823 kSecTokenValueObjectIDKey
, oid
,
824 kSecTokenValueAccessControlKey
, access_control
,
826 if (object_value
!= NULL
) {
827 CFDictionarySetValue(value
, kSecTokenValueDataKey
, object_value
);
830 CFDataRef value_data
= CFPropertyListCreateDERData(NULL
, value
, error
);
835 CFDictionaryRef
SecTokenItemValueCopy(CFDataRef db_value
, CFErrorRef
*error
) {
836 CFPropertyListRef plist
= NULL
, result
= NULL
;
837 require_quiet(CFCastWithError(CFData
, db_value
, error
), out
);
838 const uint8_t *der
= CFDataGetBytePtr(db_value
);
839 const uint8_t *der_end
= der
+ CFDataGetLength(db_value
);
840 require_quiet(der
= der_decode_plist(0, &plist
, error
, der
, der_end
), out
);
841 require_action_quiet(der
== der_end
, out
, SecError(errSecDecode
, error
, CFSTR("trailing garbage at end of token data field")));
842 CFTypeRef value
= CFDictionaryGetValue(plist
, kSecTokenValueObjectIDKey
);
843 require_action_quiet(CFCast(CFData
, value
) != NULL
, out
,
844 SecError(errSecInternal
, error
, CFSTR("token based item data does not have OID")));
845 value
= CFDictionaryGetValue(plist
, kSecTokenValueAccessControlKey
);
846 require_quiet(value
== NULL
|| CFCastWithError(CFData
, value
, error
), out
);
847 value
= CFDictionaryGetValue(plist
, kSecTokenValueDataKey
);
848 require_quiet(value
== NULL
|| CFCastWithError(CFData
, value
, error
), out
);
849 result
= CFRetainSafe(plist
);
851 CFReleaseNull(plist
);
855 TKTokenRef
SecTokenCreate(CFStringRef token_id
, SecCFDictionaryCOW
*auth_params
, CFErrorRef
*error
) {
856 CFMutableDictionaryRef token_attrs
= NULL
;
857 TKTokenRef token
= NULL
;
859 static CFMutableDictionaryRef sharedLAContexts
= NULL
;
860 static dispatch_once_t onceToken
;
861 static os_unfair_lock lock
= OS_UNFAIR_LOCK_INIT
;
862 if ((auth_params
->dictionary
== NULL
|| CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCredentialReference
) == NULL
) && !CFStringHasPrefix(token_id
, kSecAttrTokenIDSecureEnclave
)) {
863 dispatch_once(&onceToken
, ^{
864 sharedLAContexts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
867 os_unfair_lock_lock(&lock
);
868 CFTypeRef ctx
= CFDictionaryGetValue(sharedLAContexts
, token_id
);
870 ctx
= LACreateNewContextWithACMContext(NULL
, error
);
872 os_unfair_lock_unlock(&lock
);
873 secerror("Failed to create authentication context %@", *error
);
876 CFDictionarySetValue(sharedLAContexts
, token_id
, ctx
);
878 ctx
= CFDictionaryGetValue(sharedLAContexts
, token_id
);
881 CFDataRef credRef
= NULL
;
883 credRef
= LACopyACMContext(ctx
, NULL
);
887 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationContext
, ctx
);
888 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, credRef
);
891 os_unfair_lock_unlock(&lock
);
894 token_attrs
= (auth_params
->dictionary
!= NULL
) ?
895 CFDictionaryCreateMutableCopy(NULL
, 0, auth_params
->dictionary
) :
896 CFDictionaryCreateMutableForCFTypes(NULL
);
897 CFDictionarySetValue(token_attrs
, kSecAttrTokenID
, token_id
);
899 CFDictionaryRemoveValue(token_attrs
, kSecUseAuthenticationContext
);
900 token
= TKTokenCreate(token_attrs
, error
);
902 CFReleaseSafe(token_attrs
);
906 static bool SecTokenItemCreateFromAttributes(CFDictionaryRef attributes
, CFDictionaryRef auth_params_dict
,
907 TKTokenRef token
, CFDataRef object_id
, CFTypeRef
*ref
, CFErrorRef
*error
) {
909 SecCFDictionaryCOW auth_params
= { auth_params_dict
};
910 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutableCopy(NULL
, 0, attributes
);
911 CFTypeRef token_id
= CFDictionaryGetValue(attributes
, kSecAttrTokenID
);
912 if (token_id
!= NULL
&& object_id
!= NULL
) {
913 require_quiet(CFCastWithError(CFString
, token_id
, error
), out
);
914 if (CFRetainSafe(token
) == NULL
) {
915 require_quiet(token
= SecTokenCreate(token_id
, &auth_params
, error
), out
);
918 if (auth_params
.dictionary
!= NULL
) {
919 CFDictionaryForEach(auth_params
.dictionary
, ^(const void *key
, const void *value
) {
920 CFDictionaryAddValue(attrs
, key
, value
);
923 CFDictionarySetValue(attrs
, kSecUseToken
, token
);
924 CFDictionarySetValue(attrs
, kSecAttrTokenOID
, object_id
);
927 *ref
= SecItemCreateFromAttributeDictionary(attrs
);
931 CFReleaseSafe(attrs
);
932 CFReleaseSafe(auth_params
.mutable_dictionary
);
937 /* Turn the returned single value or dictionary that contains all the attributes to create a
938 ref into the exact result the client asked for */
939 static bool SecItemResultCopyPrepared(CFTypeRef raw_result
, TKTokenRef token
,
940 CFDictionaryRef query
, CFDictionaryRef auth_params_dict
,
941 CFTypeRef
*result
, CFErrorRef
*error
) {
943 CFDataRef ac_data
= NULL
;
944 CFDataRef value
= NULL
;
945 CFTypeRef persistent_ref
= NULL
;
946 CFStringRef token_id
= NULL
;
947 CFStringRef cert_token_id
= NULL
;
948 CFDataRef object_id
= NULL
;
949 CFMutableDictionaryRef attrs
= NULL
;
950 CFDataRef cert_data
= NULL
;
951 CFDataRef cert_object_id
= NULL
;
952 TKTokenRef cert_token
= NULL
;
953 SecCFDictionaryCOW auth_params
= { auth_params_dict
};
955 bool wants_ref
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnRef
));
956 bool wants_data
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnData
));
957 bool wants_attributes
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnAttributes
));
958 bool wants_persistent_ref
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnPersistentRef
));
960 // Get token value if not provided by the caller.
961 bool token_item
= false;
962 bool cert_token_item
= false;
964 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID()) {
965 token_id
= CFDictionaryGetValue(raw_result
, kSecAttrTokenID
);
966 require_quiet(token_id
== NULL
|| CFCastWithError(CFString
, token_id
, error
) != NULL
, out
);
967 token_item
= (token_id
!= NULL
);
969 cert_token_id
= CFDictionaryGetValue(raw_result
, kSecAttrIdentityCertificateTokenID
);
970 require_quiet(cert_token_id
== NULL
|| CFCastWithError(CFString
, cert_token_id
, error
) != NULL
, out
);
971 cert_token_item
= (cert_token_id
!= NULL
);
975 cert_token_item
= true;
977 CFRetainAssign(cert_token
, token
);
980 if ((token_item
|| cert_token_item
) && cf_bool_value(CFDictionaryGetValue(query
, kSecUseTokenRawItems
))) {
982 cert_token_item
= false;
985 // Decode and prepare data value, if it is requested at the output, or if we want attributes from token.
986 if (wants_data
|| wants_ref
|| (token_item
&& wants_attributes
)) {
987 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID())
988 value
= CFRetainSafe(CFDictionaryGetValue(raw_result
, kSecValueData
));
990 value
= CFRetainSafe(raw_result
);
991 if (token_item
&& value
!= NULL
) {
992 // Parse token-based item's data field.
993 CFDataRef object_value
= NULL
;
994 CFDictionaryRef parsed_value
= NULL
;
995 require_quiet(parsed_value
= SecTokenItemValueCopy(value
, error
), out
);
996 object_id
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueObjectIDKey
));
997 require_quiet(object_id
== NULL
|| CFCastWithError(CFData
, object_id
, error
) != NULL
, out
);
998 ac_data
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueAccessControlKey
));
999 require_quiet(ac_data
== NULL
|| CFCastWithError(CFData
, ac_data
, error
) != NULL
, out
);
1000 object_value
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueDataKey
));
1001 require_quiet(object_value
== NULL
|| CFCastWithError(CFData
, object_value
, error
) != NULL
, out
);
1002 CFRelease(parsed_value
);
1003 if ((wants_data
|| wants_ref
) && object_value
== NULL
) {
1004 // Retrieve value directly from the token.
1005 if (token
== NULL
) {
1006 require_quiet(token
= SecTokenCreate(token_id
, &auth_params
, error
), out
);
1008 require_quiet(object_value
= TKTokenCopyObjectData(token
, object_id
, error
), out
);
1009 if (CFEqual(object_value
, kCFNull
))
1010 CFReleaseNull(object_value
);
1012 CFAssignRetained(value
, object_value
);
1015 // If only thing requested is data, return them directly.
1016 if (!(wants_attributes
|| wants_persistent_ref
|| wants_ref
)) {
1017 *result
= CFRetainSafe(value
);
1023 // Extract persistent_ref, if caller wants it.
1024 if (wants_persistent_ref
) {
1025 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID())
1026 persistent_ref
= CFRetainSafe(CFDictionaryGetValue(raw_result
, kSecValuePersistentRef
));
1028 persistent_ref
= CFRetainSafe(raw_result
);
1030 // If only thing requested is persistentref, extract it from dictionary if needed and return it.
1031 if (!(wants_attributes
|| wants_data
|| wants_ref
)) {
1032 *result
= CFRetainSafe(persistent_ref
);
1038 if (!wants_ref
&& !wants_attributes
&& (!wants_data
|| !wants_persistent_ref
)) {
1044 // For other cases we need an output dictionary.
1045 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID())
1046 *result
= CFDictionaryCreateMutableCopy(NULL
, 0, raw_result
);
1048 *result
= CFDictionaryCreateForCFTypes(NULL
, NULL
);
1049 CFMutableDictionaryRef output
= (CFMutableDictionaryRef
)*result
;
1051 if ((wants_data
|| wants_ref
) && value
!= NULL
)
1052 CFDictionarySetValue(output
, kSecValueData
, value
);
1054 CFDictionaryRemoveValue(output
, kSecValueData
);
1056 if (wants_persistent_ref
&& persistent_ref
!= NULL
)
1057 CFDictionarySetValue(output
, kSecValuePersistentRef
, persistent_ref
);
1059 CFDictionaryRemoveValue(output
, kSecValuePersistentRef
);
1061 if ((wants_ref
|| wants_attributes
) && cert_token_item
&&
1062 CFEqualSafe(CFDictionaryGetValue(output
, kSecClass
), kSecClassIdentity
)) {
1063 // Decode also certdata field of the identity.
1064 CFDataRef data
= CFDictionaryGetValue(output
, kSecAttrIdentityCertificateData
);
1066 CFDictionaryRef parsed_value
;
1067 require_quiet(parsed_value
= SecTokenItemValueCopy(data
, error
), out
);
1068 cert_data
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueDataKey
));
1069 require_quiet(cert_data
== NULL
|| CFCastWithError(CFData
, cert_data
, error
) != NULL
, out
);
1070 cert_object_id
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueObjectIDKey
));
1071 require_quiet(cert_object_id
== NULL
|| CFCastWithError(CFData
, cert_object_id
, error
) != NULL
, out
);
1072 CFRelease(parsed_value
);
1073 if (cert_data
== NULL
) {
1074 // Retrieve value directly from the token.
1075 if (cert_token
== NULL
) {
1076 require_quiet(cert_token
= SecTokenCreate(cert_token_id
, &auth_params
, error
), out
);
1078 require_quiet(cert_data
= TKTokenCopyObjectData(cert_token
, cert_object_id
, error
), out
);
1079 if (CFEqual(cert_data
, kCFNull
))
1080 CFReleaseNull(cert_data
);
1082 if (cert_data
!= NULL
) {
1083 CFDictionarySetValue(output
, kSecAttrIdentityCertificateData
, cert_data
);
1085 CFDictionaryRemoveValue(output
, kSecAttrIdentityCertificateData
);
1090 if (wants_ref
|| wants_attributes
) {
1091 // Convert serialized form of access control to object form.
1093 CFRetainAssign(ac_data
, CFDictionaryGetValue(output
, kSecAttrAccessControl
));
1096 if (ac_data
!= NULL
) {
1097 SecAccessControlRef ac
;
1098 require_quiet(ac
= SecAccessControlCreateFromData(kCFAllocatorDefault
, ac_data
, error
), out
);
1099 CFDictionarySetValue(output
, kSecAttrAccessControl
, ac
);
1106 require_quiet(SecTokenItemCreateFromAttributes(output
, auth_params
.dictionary
, token
, object_id
, &ref
, error
), out
);
1107 if (!(wants_attributes
|| wants_data
|| wants_persistent_ref
)) {
1108 CFAssignRetained(*result
, ref
);
1109 } else if (ref
!= NULL
) {
1110 CFDictionarySetValue(output
, kSecValueRef
, ref
);
1113 // We could have stored data value previously to make ref creation succeed.
1114 // They are not needed any more and in case that caller did not want the data, avoid returning them.
1115 CFDictionaryRemoveValue(output
, kSecValueData
);
1123 CFReleaseSafe(cert_object_id
);
1124 CFReleaseSafe(cert_data
);
1125 CFReleaseSafe(ac_data
);
1126 CFReleaseSafe(value
);
1127 CFReleaseSafe(persistent_ref
);
1128 CFReleaseSafe(object_id
);
1129 CFReleaseSafe(attrs
);
1130 CFReleaseSafe(token
);
1131 CFReleaseSafe(cert_token
);
1132 CFReleaseSafe(auth_params
.mutable_dictionary
);
1136 bool SecItemResultProcess(CFDictionaryRef query
, CFDictionaryRef auth_params
, TKTokenRef token
,
1137 CFTypeRef raw_result
, CFTypeRef
*result
, CFErrorRef
*error
) {
1139 require_action_quiet(raw_result
!= NULL
, out
, ok
= true);
1140 require_action_quiet(result
!= NULL
, out
, ok
= true);
1142 if (CFGetTypeID(raw_result
) == CFArrayGetTypeID()) {
1143 CFIndex i
, count
= CFArrayGetCount(raw_result
);
1144 *result
= CFArrayCreateMutableForCFTypes(NULL
);
1145 for (i
= 0; i
< count
; i
++) {
1147 CFErrorRef localError
= NULL
;
1148 bool prepared
= SecItemResultCopyPrepared(CFArrayGetValueAtIndex(raw_result
, i
),
1149 token
, query
, auth_params
, &ref
, &localError
);
1151 // TokenNotFound or TokenObjectNotFound will just not insert failing item into resulting array, other errors abort processing.
1152 require_action_quiet(localError
!= NULL
&& CFEqual(CFErrorGetDomain(localError
), CFSTR(kTKErrorDomain
)) &&
1153 (CFErrorGetCode(localError
) == kTKErrorCodeTokenNotFound
|| CFErrorGetCode(localError
) == kTKErrorCodeObjectNotFound
), out
,
1154 CFErrorPropagate(localError
, error
));
1155 CFReleaseNull(localError
);
1156 } else if (ref
!= NULL
) {
1157 CFArrayAppendValue((CFMutableArrayRef
)*result
, ref
);
1162 require_quiet(SecItemResultCopyPrepared(raw_result
, token
, query
, auth_params
, result
, error
), out
);
1171 static bool SecItemAttributesPrepare(SecCFDictionaryCOW
*attrs
, bool forQuery
, CFErrorRef
*error
) {
1173 CFDataRef ac_data
= NULL
, acm_context
= NULL
;
1175 // If a ref was specified we get its attribute dictionary and parse it.
1176 CFTypeRef value
= CFDictionaryGetValue(attrs
->dictionary
, kSecValueRef
);
1178 CFDictionaryRef ref_attributes
;
1179 require_action_quiet(ref_attributes
= SecItemCopyAttributeDictionary(value
, forQuery
), out
,
1180 SecError(errSecValueRefUnsupported
, error
, CFSTR("unsupported kSecValueRef in query")));
1182 // Replace any attributes we already got from the ref with the ones from the attributes dictionary the caller passed us.
1183 // This allows a caller to add an item using attributes from the ref and still override some of them in the dictionary directly.
1184 CFDictionaryForEach(ref_attributes
, ^(const void *key
, const void *value
) {
1185 // Attributes already present in 'attrs' have precedence over the generic ones retrieved from the ref,
1186 // so add only those attributes from 'ref' which are missing in attrs.
1187 CFDictionaryAddValue(SecCFDictionaryCOWGetMutable(attrs
), key
, value
);
1189 CFRelease(ref_attributes
);
1192 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs
), kSecAttrTokenOID
);
1195 // Remove original expanded valueRef. Do not remove it in case when adding token item, because it is needed later to avoid
1196 // another roundtrip to token driver.
1197 if (forQuery
|| !CFDictionaryContainsKey(attrs
->dictionary
, kSecAttrTokenID
)) {
1198 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs
), kSecValueRef
);
1202 SecAccessControlRef access_control
= (SecAccessControlRef
)CFDictionaryGetValue(attrs
->dictionary
, kSecAttrAccessControl
);
1203 if (access_control
!= NULL
) {
1204 require_action_quiet(CFGetTypeID(access_control
) == SecAccessControlGetTypeID(), out
,
1205 SecError(errSecParam
, error
, CFSTR("Unexpected type of kSecAttrAccessControl attribute")));
1206 require_action_quiet(ac_data
= SecAccessControlCopyData(access_control
), out
,
1207 SecError(errSecParam
, error
, CFSTR("unsupported kSecAttrAccessControl in query")));
1208 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecAttrAccessControl
, ac_data
);
1211 const CFTypeRef la_context
= CFDictionaryGetValue(attrs
->dictionary
, kSecUseAuthenticationContext
);
1213 require_action_quiet(!CFDictionaryContainsKey(attrs
->dictionary
, kSecUseCredentialReference
), out
,
1214 SecError(errSecParam
, error
, CFSTR("kSecUseAuthenticationContext cannot be used together with kSecUseCredentialReference")));
1215 require_quiet(acm_context
= LACopyACMContext(la_context
, error
), out
);
1216 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs
), kSecUseAuthenticationContext
);
1217 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecUseCredentialReference
, acm_context
);
1220 CFTypeRef policy
= CFDictionaryGetValue(attrs
->dictionary
, kSecMatchPolicy
);
1222 require_action_quiet(CFGetTypeID(policy
) == SecPolicyGetTypeID(), out
,
1223 SecError(errSecParam
, error
, CFSTR("unsupported kSecMatchPolicy in query")));
1225 CFTypeRef values
[] = { policy
};
1226 CFArrayRef policiesArray
= CFArrayCreate(kCFAllocatorDefault
, values
, 1, &kCFTypeArrayCallBacks
);
1227 xpc_object_t policiesArrayXPC
= SecPolicyArrayCopyXPCArray(policiesArray
, error
);
1228 CFReleaseSafe(policiesArray
);
1229 require_action_quiet(policiesArrayXPC
, out
,
1230 SecError(errSecInternal
, error
, CFSTR("Failed to copy XPC policy")));
1232 CFTypeRef objectReadyForXPC
= _CFXPCCreateCFObjectFromXPCObject(policiesArrayXPC
);
1233 xpc_release(policiesArrayXPC
);
1234 require_action_quiet(objectReadyForXPC
, out
,
1235 SecError(errSecInternal
, error
, CFSTR("Failed to create CFObject from XPC policy")));
1237 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecMatchPolicy
, objectReadyForXPC
);
1238 CFRelease(objectReadyForXPC
);
1240 value
= CFDictionaryGetValue(attrs
->dictionary
, kSecAttrIssuer
);
1242 /* convert DN to canonical issuer, if value is DN (top level sequence) */
1244 require_quiet(issuer
= CFCastWithError(CFData
, value
, error
), out
);
1245 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
) };
1246 DERDecodedInfo content
;
1247 if (DERDecodeItem(&name
, &content
) == DR_Success
&& content
.tag
== ASN1_CONSTR_SEQUENCE
) {
1248 CFDataRef canonical_issuer
= createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1249 if (canonical_issuer
) {
1250 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecAttrIssuer
, canonical_issuer
);
1251 CFRelease(canonical_issuer
);
1256 if (CFDictionaryContainsKey(attrs
->dictionary
, kSecUseTokenRawItems
)) {
1257 // This use flag is client-only, securityd does not understand it.
1258 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs
), kSecUseTokenRawItems
);
1264 CFReleaseSafe(ac_data
);
1265 CFReleaseSafe(acm_context
);
1269 static bool SecItemAuthMaxAttemptsReached(CFArrayRef ac_pairs
, CFErrorRef
*error
)
1271 CFMutableStringRef log_string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1273 CFArrayForEachC(ac_pairs
, ac_pair
) {
1274 CFStringRef acl_hex_string
= CFDataCopyHexString(CFArrayGetValueAtIndex(ac_pair
, 0));
1275 CFStringRef str
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("operation: %@ acl:%@\n"), CFArrayGetValueAtIndex(ac_pair
, 1), acl_hex_string
);
1276 CFStringAppend(log_string
, str
);
1277 CFRelease(acl_hex_string
);
1281 CFStringRef reason
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("Reached maximum count of authentication attempts\n %@"), log_string
);
1282 SecError(errSecAuthFailed
, error
, CFSTR("%@"), reason
);
1283 __security_simulatecrash(reason
, __sec_exception_code_AuthLoop
);
1285 CFRelease(log_string
);
1289 bool SecItemAuthDo(SecCFDictionaryCOW
*auth_params
, CFErrorRef
*error
, SecItemAuthResult (^perform
)(CFArrayRef
*ac_pairs
, CFErrorRef
*error
),
1290 void (^newCredentialRefAdded
)(void)) {
1292 CFArrayRef ac_pairs
= NULL
;
1293 SecCFDictionaryCOW auth_options
= { NULL
};
1295 for (uint32_t i
= 0;; ++i
) {
1296 // If the operation succeeded or failed with other than auth-needed error, just leave.
1297 SecItemAuthResult auth_result
= perform(&ac_pairs
, error
);
1298 require_quiet(auth_result
!= kSecItemAuthResultError
, out
);
1299 require_action_quiet(auth_result
== kSecItemAuthResultNeedAuth
, out
, ok
= true);
1301 // If auth_params were not created up to now, do create them because we will definitely need them.
1302 SecCFDictionaryCOWGetMutable(auth_params
);
1304 // Retrieve or create authentication handle and/or ACM context.
1305 CFTypeRef auth_handle
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseAuthenticationContext
);
1306 if (auth_handle
== NULL
) {
1307 CFDataRef acm_context
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCredentialReference
);
1308 require_quiet(auth_handle
= LACreateNewContextWithACMContext(acm_context
, error
), out
);
1309 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationContext
, auth_handle
);
1310 CFRelease(auth_handle
);
1311 if (acm_context
== NULL
) {
1312 require_quiet(acm_context
= LACopyACMContext(auth_handle
, error
), out
);
1313 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, acm_context
);
1314 CFRelease(acm_context
);
1315 if (newCredentialRefAdded
) {
1316 newCredentialRefAdded();
1321 // Throttle max authentication attempts. This is mainly protection against exceptional states, not ordinary
1322 // user retry limit.
1323 require_action(i
< 20, out
, SecItemAuthMaxAttemptsReached(ac_pairs
, error
));
1325 // Prepare auth options dictionary.
1326 if (auth_options
.dictionary
== NULL
) {
1327 CFStringRef operation_prompt
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseOperationPrompt
);
1328 if (operation_prompt
!= NULL
) {
1329 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionAuthenticationReason
);
1330 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, operation_prompt
);
1334 CFStringRef caller_name
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCallerName
);
1335 if (caller_name
!= NULL
) {
1336 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionCallerName
);
1337 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, caller_name
);
1341 CFTypeRef auth_ui
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseAuthenticationUI
);
1342 if (CFEqualSafe(auth_ui
, kSecUseAuthenticationUIFail
)) {
1343 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionNotInteractive
);
1344 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, kCFBooleanTrue
);
1349 // Go through all access_control-operation pairs and evaluate them.
1351 CFArrayForEachC(ac_pairs
, ac_pair
) {
1352 CFDataRef updated_acl
= NULL
;
1353 require_quiet(LAEvaluateAndUpdateACL(auth_handle
,
1354 CFArrayGetValueAtIndex(ac_pair
, 0), CFArrayGetValueAtIndex(ac_pair
, 1),
1355 auth_options
.dictionary
, &updated_acl
, error
), out
);
1357 if (updated_acl
|| CFEqual(CFArrayGetValueAtIndex(ac_pair
, 1), CFSTR(""))) {
1358 // we assume that only one ACL can be modified during ItemAdd or ItemUpdate
1359 SecAccessControlRef ac
= NULL
;
1360 require(ac
= SecAccessControlCreateFromData(kCFAllocatorDefault
,
1361 updated_acl
? updated_acl
: CFArrayGetValueAtIndex(ac_pair
, 0), error
), out
);
1362 SecAccessControlSetBound(ac
, true);
1363 CFAssignRetained(updated_acl
, SecAccessControlCopyData(ac
));
1364 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecAttrAccessControl
, updated_acl
);
1365 CFRelease(updated_acl
);
1374 CFReleaseSafe(auth_options
.mutable_dictionary
);
1375 CFReleaseSafe(ac_pairs
);
1379 void SecItemAuthCopyParams(SecCFDictionaryCOW
*auth_params
, SecCFDictionaryCOW
*query
) {
1380 // Store operation prompt.
1381 CFStringRef operation_prompt
= CFDictionaryGetValue(query
->dictionary
, kSecUseOperationPrompt
);
1382 if (operation_prompt
!= NULL
) {
1383 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseOperationPrompt
, operation_prompt
);
1384 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseOperationPrompt
);
1387 // Store caller name.
1388 CFStringRef caller_name
= CFDictionaryGetValue(query
->dictionary
, kSecUseCallerName
);
1389 if (caller_name
!= NULL
) {
1390 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCallerName
, caller_name
);
1391 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseCallerName
);
1394 // Find out whether we are allowed to pop up a UI.
1395 CFTypeRef auth_ui
= CFDictionaryGetValue(query
->dictionary
, kSecUseAuthenticationUI
) ?:
1396 (CFEqualSafe(CFDictionaryGetValue(query
->dictionary
, kSecUseNoAuthenticationUI
), kCFBooleanTrue
) ?
1397 kSecUseAuthenticationUIFail
: kSecUseAuthenticationUIAllow
);
1398 if (!CFEqual(auth_ui
, kSecUseAuthenticationUISkip
) || CFDictionaryGetValue(query
->dictionary
, kSecUseNoAuthenticationUI
)) {
1399 if (CFDictionaryContainsKey(query
->dictionary
, kSecUseNoAuthenticationUI
))
1400 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseNoAuthenticationUI
);
1401 if (!CFEqualSafe(auth_ui
, kSecUseAuthenticationUISkip
) && CFDictionaryContainsKey(query
->dictionary
, kSecUseAuthenticationUI
))
1402 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseAuthenticationUI
);
1405 if (!CFEqual(auth_ui
, kSecUseAuthenticationUIAllow
)) {
1406 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationUI
, auth_ui
);
1409 CFDataRef acm_context
= CFDictionaryGetValue(query
->dictionary
, kSecUseCredentialReference
);
1410 if (acm_context
!= NULL
) {
1411 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, acm_context
);
1415 static SecItemAuthResult
SecItemCreatePairsFromError(CFErrorRef
*error
, CFArrayRef
*ac_pairs
)
1417 if (error
&& *error
&& CFErrorGetCode(*error
) == errSecAuthNeeded
&& CFEqualSafe(CFErrorGetDomain(*error
), kSecErrorDomain
)) {
1418 // Extract ACLs to be verified from the error.
1419 CFDictionaryRef user_info
= CFErrorCopyUserInfo(*error
);
1420 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, errSecAuthNeeded
);
1421 CFRetainAssign(*ac_pairs
, CFDictionaryGetValue(user_info
, key
));
1422 if (*ac_pairs
== NULL
)
1423 CFAssignRetained(*ac_pairs
, CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
));
1426 CFRelease(user_info
);
1427 CFReleaseNull(*error
);
1428 return kSecItemAuthResultNeedAuth
;
1430 return kSecItemAuthResultError
;
1433 // Wrapper to handle automatic authentication and token/secd case switching.
1434 bool SecItemAuthDoQuery(SecCFDictionaryCOW
*query
, SecCFDictionaryCOW
*attributes
, const void *secItemOperation
, CFErrorRef
*error
,
1435 bool (^perform
)(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
)) {
1437 __block SecCFDictionaryCOW auth_params
= { NULL
};
1438 __block TKTokenRef token
= NULL
;
1440 CFDictionaryRef dict
= attributes
? attributes
->dictionary
: query
->dictionary
;
1441 SecAccessControlRef access_control
= (SecAccessControlRef
)CFDictionaryGetValue(dict
, kSecAttrAccessControl
);
1442 require_action_quiet(access_control
== NULL
|| CFGetTypeID(access_control
) == SecAccessControlGetTypeID(), out
,
1443 SecError(errSecParam
, error
, CFSTR("Unexpected type of kSecAttrAccessControl attribute")));
1445 if (secItemOperation
== SecItemAdd
|| secItemOperation
== SecItemUpdate
) {
1446 if (access_control
&& SecAccessControlGetConstraints(access_control
) &&
1447 CFEqualSafe(CFDictionaryGetValue(dict
, kSecAttrSynchronizable
), kCFBooleanTrue
))
1448 require_quiet(SecError(errSecParam
, error
, CFSTR("item with kSecAttrAccessControl is not synchronizable")), out
);
1451 // Perform initial surgery on query/attributes (resolve LAContext to serialized ACM handle, resolve
1452 // SecAccessControlRef to serialized forms, expand kSecValueRef etc.)
1454 secItemOperation
== SecItemCopyMatching
||
1455 secItemOperation
== SecItemUpdate
||
1456 secItemOperation
== SecItemDelete
;
1458 require_quiet(SecItemAttributesPrepare(query
, forQuery
, error
), out
);
1459 if (attributes
!= NULL
)
1460 require_quiet(SecItemAttributesPrepare(attributes
, false, error
), out
);
1462 // Populate auth_params dictionary according to initial query contents.
1463 SecItemAuthCopyParams(&auth_params
, query
);
1465 if (secItemOperation
!= SecItemCopyMatching
) {
1466 // UISkip is allowed only for CopyMatching.
1467 require_action_quiet(!CFEqualSafe(CFDictionaryGetValue(query
->dictionary
, kSecUseAuthenticationUI
), kSecUseAuthenticationUISkip
), out
,
1468 SecError(errSecParam
, error
,
1469 CFSTR("kSecUseAuthenticationUISkip is allowed only for SecItemCopyMatching")));
1472 ok
= SecItemAuthDo(&auth_params
, error
, ^SecItemAuthResult(CFArrayRef
*ac_pairs
, CFErrorRef
*error
) {
1473 SecItemAuthResult result
= kSecItemAuthResultError
;
1475 // Propagate actual credential reference to the query.
1476 if (auth_params
.dictionary
!= NULL
) {
1477 CFDataRef acm_context
= CFDictionaryGetValue(auth_params
.dictionary
, kSecUseCredentialReference
);
1478 if (acm_context
!= NULL
) {
1479 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query
), kSecUseCredentialReference
, acm_context
);
1482 CFDataRef acl_data_ref
= CFDictionaryGetValue(auth_params
.dictionary
, kSecAttrAccessControl
);
1483 if (acl_data_ref
!= NULL
) {
1484 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes
?: query
), kSecAttrAccessControl
, acl_data_ref
);
1488 // Prepare connection to target token if it is present.
1489 CFStringRef token_id
= CFDictionaryGetValue(query
->dictionary
, kSecAttrTokenID
);
1490 require_quiet(token_id
== NULL
|| CFCastWithError(CFString
, token_id
, error
) != NULL
, out
);
1491 if (secItemOperation
!= SecItemCopyMatching
&& token_id
!= NULL
&& !cf_bool_value(CFDictionaryGetValue(query
->dictionary
, kSecUseTokenRawItems
))) {
1492 CFErrorRef localError
= NULL
;
1493 CFAssignRetained(token
, SecTokenCreate(token_id
, &auth_params
, &localError
));
1494 if (token
== NULL
) {
1495 require_action_quiet(secItemOperation
== SecItemDelete
&&
1496 CFEqual(CFErrorGetDomain(localError
), CFSTR(kTKErrorDomain
)) &&
1497 CFErrorGetCode(localError
) == kTKErrorCodeTokenNotFound
,
1498 out
, CFErrorPropagate(localError
, error
));
1500 // In case that token cannot be found and deletion is required, just continue and delete item from keychain only.
1501 CFReleaseNull(localError
);
1505 CFDictionaryRef attrs
= (attributes
!= NULL
) ? attributes
->dictionary
: NULL
;
1506 if(perform(token
, query
->dictionary
, attrs
, auth_params
.dictionary
, error
)) {
1507 result
= kSecItemAuthResultOK
;
1508 if(error
&& *error
) {
1509 // <rdar://problem/60642633> SecItemAuthDoQuery perform() sometimes returns success and fills in error parameter
1510 secdebug("SecItemAuthDoQuery", "perform() succeded but returned an error: %@", *error
);
1511 CFReleaseNull(*error
);
1514 result
= SecItemCreatePairsFromError(error
, ac_pairs
);
1520 require_quiet(ok
, out
);
1525 CFReleaseSafe(token
);
1526 CFReleaseSafe(auth_params
.mutable_dictionary
);
1530 static bool cftype_to_bool_cftype_error_request(enum SecXPCOperation op
, CFTypeRef attributes
, CFTypeRef
*result
, CFErrorRef
*error
)
1532 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1533 return SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, attributes
, error
);
1534 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1536 return SecXPCDictionaryCopyPListOptional(response
, kSecXPCKeyResult
, result
, error
);
1542 static CFArrayRef
dict_to_array_error_request(enum SecXPCOperation op
, CFDictionaryRef attributes
, CFErrorRef
*error
)
1544 CFArrayRef result
= NULL
;
1545 bool success
= cftype_to_bool_cftype_error_request(op
, attributes
, (CFTypeRef
*)&result
, error
);
1546 if(success
&& !isArray(result
)){
1547 SecError(errSecUnimplemented
, error
, CFSTR("Unexpected nonarray returned: %@"), result
);
1548 CFReleaseNull(result
);
1553 bool cftype_client_to_bool_cftype_error_request(enum SecXPCOperation op
, CFTypeRef attributes
, __unused SecurityClient
*client
, CFTypeRef
*result
, CFErrorRef
*error
) {
1554 return cftype_to_bool_cftype_error_request(op
, attributes
, result
, error
);
1557 static bool dict_to_error_request(enum SecXPCOperation op
, CFDictionaryRef query
, CFErrorRef
*error
)
1559 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1560 return SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, query
, error
);
1564 static bool cfstring_array_array_to_error_request(enum SecXPCOperation op
, CFStringRef string
, CFArrayRef groups
, CFArrayRef items
, __unused SecurityClient
*client
, CFErrorRef
*error
)
1566 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1568 if (!SecXPCDictionarySetString(message
, kSecXPCKeyString
, string
, error
))
1573 if (!SecXPCDictionarySetPList(message
, kSecXPCKeyArray
, groups
, error
))
1578 if (!SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, items
, error
))
1586 static bool dict_client_to_error_request(enum SecXPCOperation op
, CFDictionaryRef query
, __unused SecurityClient
*client
, CFErrorRef
*error
)
1588 return dict_to_error_request(op
, query
, error
);
1591 static bool SecTokenCreateAccessControlError(CFStringRef operation
, CFDataRef access_control
, CFErrorRef
*error
) {
1592 CFArrayRef ac_pair
= CFArrayCreateForCFTypes(NULL
, access_control
, operation
, NULL
);
1593 const void *ac_pairs
[] = { CFArrayCreateForCFTypes(NULL
, ac_pair
, NULL
) };
1594 const void *keys
[] = { CFNumberCreateWithCFIndex(NULL
, errSecAuthNeeded
) };
1595 CFAssignRetained(*error
, CFErrorCreateWithUserInfoKeysAndValues(NULL
, kSecErrorDomain
, errSecAuthNeeded
,
1596 keys
, ac_pairs
, 1));
1598 CFRelease(ac_pairs
[0]);
1603 static bool SecTokenProcessError(CFStringRef operation
, TKTokenRef token
, CFTypeRef object_or_attrs
, CFErrorRef
*error
) {
1604 if (CFEqualSafe(CFErrorGetDomain(*error
), CFSTR(kTKErrorDomain
)) &&
1605 CFErrorGetCode(*error
) == kTKErrorCodeAuthenticationNeeded
) {
1606 // Replace error with the one which is augmented with access control and operation which failed,
1607 // which will cause SecItemDoWithAuth to throw UI.
1608 // Create array containing tuple (array) with error and requested operation.
1609 CFDataRef access_control
= TKTokenCopyObjectAccessControl(token
, object_or_attrs
, error
);
1610 if (access_control
!= NULL
) {
1611 SecTokenCreateAccessControlError(operation
, access_control
, error
);
1612 CFRelease(access_control
);
1618 static CFTypeRef
SecTokenCopyUpdatedObjectID(TKTokenRef token
, CFDataRef object_id
, CFMutableDictionaryRef attributes
, CFErrorRef
*error
) {
1619 CFDataRef access_control
= NULL
, db_value
= NULL
, new_object_id
= NULL
, result
= NULL
;
1620 SecAccessControlRef ac
= NULL
;
1621 CFDictionaryRef old_attrs
= NULL
;
1623 // Make sure that ACL is bound - if not, generate an error which will trigger binding.
1624 CFDataRef ac_data
= CFDictionaryGetValue(attributes
, kSecAttrAccessControl
);
1625 if (ac_data
!= NULL
) {
1626 require_quiet(ac
= SecAccessControlCreateFromData(NULL
, ac_data
, error
), out
);
1627 require_action_quiet(SecAccessControlIsBound(ac
), out
,
1628 SecTokenCreateAccessControlError(CFSTR(""), ac_data
, error
));
1631 // Create or update the object on the token.
1632 old_attrs
= CFDictionaryCreateCopy(kCFAllocatorDefault
, attributes
);
1633 require_action_quiet(new_object_id
= TKTokenCreateOrUpdateObject(token
, object_id
, attributes
, error
), out
,
1634 SecTokenProcessError(kAKSKeyOpEncrypt
, token
, object_id
?: (CFTypeRef
)attributes
, error
));
1635 CFDictionaryForEach(old_attrs
, ^(const void *key
, const void *value
) {
1636 if (!CFEqual(key
, kSecValueData
)) {
1637 CFDictionaryAddValue(attributes
, key
, value
);
1641 // Prepare kSecValueData field for the item to be stored into the keychain DB.
1642 require_quiet(access_control
= TKTokenCopyObjectAccessControl(token
, new_object_id
, error
), out
);
1643 require_quiet(db_value
= SecTokenItemValueCreate(new_object_id
, access_control
,
1644 CFDictionaryGetValue(attributes
, kSecValueData
), error
), out
);
1645 CFDictionarySetValue(attributes
, kSecValueData
, db_value
);
1647 // kSecAttrAccessControl is handled directly by the token and stored inside data field.
1648 CFDictionaryRemoveValue(attributes
, kSecAttrAccessControl
);
1649 CFRetainAssign(result
, new_object_id
);
1653 CFReleaseSafe(access_control
);
1654 CFReleaseSafe(db_value
);
1655 CFReleaseSafe(old_attrs
);
1656 CFReleaseSafe(new_object_id
);
1660 static bool SecTokenItemAdd(TKTokenRef token
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
,
1661 CFTypeRef
*result
, CFErrorRef
*error
) {
1663 CFTypeRef object_id
= NULL
, ref
= NULL
;
1664 CFDictionaryRef ref_attrs
= NULL
;
1665 CFTypeRef db_result
= NULL
;
1666 CFDataRef db_value
= NULL
;
1667 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutableCopy(NULL
, 0, attributes
);
1669 CFDictionarySetValue(attrs
, kSecAttrAccessible
, kSecAttrAccessibleAlwaysPrivate
); //token items should be accesible always because have own ACL encoded in OID
1670 object_id
= CFRetainSafe(CFDictionaryGetValue(attrs
, kSecAttrTokenOID
));
1671 CFDictionaryRemoveValue(attrs
, kSecAttrTokenOID
);
1672 require_quiet(CFAssignRetained(object_id
, SecTokenCopyUpdatedObjectID(token
, object_id
, attrs
, error
)), out
);
1673 CFDictionaryRemoveValue(attrs
, kSecAttrTokenOID
);
1674 if (CFDictionaryContainsKey(attrs
, kSecValueRef
)) {
1675 // All attributes already had been extracted from valueRef, so do not go through that step again, just remove
1676 // the ref from the dictionary since it is of no use any more.
1677 CFDictionaryRemoveValue(attrs
, kSecValueRef
);
1679 // Augment attributes from default attributes of the related ref (SecKeyRef, SecCertificateRef). This is best done
1680 // by creating ref and getting back its attributes.
1681 require_quiet(SecTokenItemCreateFromAttributes(attrs
, auth_params
, token
, object_id
, &ref
, error
), out
);
1683 if ((ref_attrs
= SecItemCopyAttributeDictionary(ref
, false)) != NULL
) {
1684 CFDictionaryForEach(ref_attrs
, ^(const void *key
, const void *value
) {
1685 if (!CFEqual(key
, kSecValueData
)) {
1686 CFDictionaryAddValue(attrs
, key
, value
);
1693 // Make sure that both attributes and data are returned.
1694 CFDictionarySetValue(attrs
, kSecReturnAttributes
, kCFBooleanTrue
);
1695 CFDictionarySetValue(attrs
, kSecReturnData
, kCFBooleanTrue
);
1697 if (!CFEqualSafe(CFDictionaryGetValue(attrs
, kSecAttrIsPermanent
), kCFBooleanFalse
)) {
1698 // IsPermanent is not present or is true, so add item to the db.
1699 require_quiet(SECURITYD_XPC(sec_item_add
, cftype_client_to_bool_cftype_error_request
, attrs
,
1700 SecSecurityClientGet(), &db_result
, error
), out
);
1702 // Process directly result of token call.
1703 db_result
= CFRetain(attrs
);
1705 require_quiet(SecItemResultProcess(attributes
, auth_params
, token
, db_result
, result
, error
), out
);
1709 CFReleaseSafe(db_result
);
1710 CFReleaseSafe(db_value
);
1711 CFReleaseSafe(attrs
);
1712 CFReleaseSafe(ref_attrs
);
1713 CFReleaseSafe(object_id
);
1718 static void countReadOnlyAPICall() {
1719 if (!isReadOnlyAPIRateWithinLimits()) {
1723 static void countModifyingAPICall() {
1724 if (!isModifyingAPIRateWithinLimits()) {
1728 OSStatus
SecItemAdd(CFDictionaryRef attributes
, CFTypeRef
*result
) {
1729 __block SecCFDictionaryCOW attrs
= { attributes
};
1732 os_activity_t activity
= os_activity_create("SecItemAdd_ios", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
1733 os_activity_scope(activity
);
1734 os_release(activity
);
1736 require_quiet(!explode_identity(attrs
.dictionary
, (secitem_operation
)SecItemAdd
, &status
, result
), errOut
);
1737 infer_cert_label(&attrs
);
1739 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1740 return SecItemAuthDoQuery(&attrs
, NULL
, SecItemAdd
, error
, ^bool(TKTokenRef token
, CFDictionaryRef attributes
, CFDictionaryRef unused
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1741 if (token
== NULL
) {
1742 CFTypeRef raw_result
= NULL
;
1743 logUnreasonableDataLength(attributes
);
1744 countModifyingAPICall();
1745 if (!SECURITYD_XPC(sec_item_add
, cftype_client_to_bool_cftype_error_request
, attributes
, SecSecurityClientGet(), &raw_result
, error
)) {
1749 bool ok
= SecItemResultProcess(attributes
, auth_params
, token
, raw_result
, result
, error
);
1750 CFReleaseSafe(raw_result
);
1753 // Send request to an appropriate token instead of secd.
1754 return SecTokenItemAdd(token
, attributes
, auth_params
, result
, error
);
1760 CFReleaseSafe(attrs
.mutable_dictionary
);
1761 secdebug("secitem", "SecItemAdd returned: %d", (int)status
);
1767 OSStatus
SecItemCopyMatching(CFDictionaryRef inQuery
, CFTypeRef
*result
) {
1769 __block SecCFDictionaryCOW query
= { inQuery
};
1771 os_activity_t activity
= os_activity_create("SecItemCopyMatching_ios", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
1772 os_activity_scope(activity
);
1773 os_release(activity
);
1775 require_quiet(!explode_identity(query
.dictionary
, (secitem_operation
)SecItemCopyMatching
, &status
, result
), errOut
);
1777 bool wants_data
= cf_bool_value(CFDictionaryGetValue(query
.dictionary
, kSecReturnData
));
1778 bool wants_attributes
= cf_bool_value(CFDictionaryGetValue(query
.dictionary
, kSecReturnAttributes
));
1779 if ((wants_data
&& !wants_attributes
)) {
1780 // When either attributes or data are requested, we need to query both, because for token based items,
1781 // both are needed in order to generate proper data and/or attributes results.
1782 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query
), kSecReturnAttributes
, kCFBooleanTrue
);
1785 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1786 return SecItemAuthDoQuery(&query
, NULL
, SecItemCopyMatching
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1787 CFTypeRef raw_result
= NULL
;
1788 countReadOnlyAPICall();
1789 if (!SECURITYD_XPC(sec_item_copy_matching
, cftype_client_to_bool_cftype_error_request
, query
, SecSecurityClientGet(), &raw_result
, error
)) {
1793 // We intentionally pass NULL as token argument, because we want to be able to decide about token on which the item lives
1794 // on per-record basis, not wholesale. Logic inside SecItemResultCopyPrepared will open proper token according
1795 // to currently processed item.
1796 bool ok
= SecItemResultProcess(inQuery
, auth_params
, NULL
, raw_result
, result
, error
);
1797 CFReleaseSafe(raw_result
);
1803 secdebug("secitem", "SecItemCopyMatching_ios returned: %d", (int)status
);
1804 CFReleaseSafe(query
.mutable_dictionary
);
1808 // Invokes token-object handler for each item matching specified query.
1809 static bool SecTokenItemForEachMatching(CFDictionaryRef query
, CFErrorRef
*error
,
1810 bool (^perform
)(CFDictionaryRef item_value
, CFDictionaryRef item_query
,
1811 CFErrorRef
*error
)) {
1813 CFMutableDictionaryRef list_query
= NULL
;
1814 CFTypeRef items
= NULL
;
1815 CFArrayRef ref_array
= NULL
;
1816 CFDictionaryRef item_query
= NULL
, item_data
= NULL
;
1818 // Query all items with data and persistent_ref, so that we can extract objectIDs and also identify originating
1819 // items in the keychain.
1820 list_query
= CFDictionaryCreateMutableCopy(NULL
, 0, query
);
1821 if (CFDictionaryGetValue(list_query
, kSecMatchLimit
) == NULL
) {
1822 CFDictionarySetValue(list_query
, kSecMatchLimit
, kSecMatchLimitAll
);
1824 CFDictionarySetValue(list_query
, kSecReturnData
, kCFBooleanTrue
);
1825 CFDictionarySetValue(list_query
, kSecReturnPersistentRef
, kCFBooleanTrue
);
1826 require_quiet(SECURITYD_XPC(sec_item_copy_matching
, cftype_client_to_bool_cftype_error_request
, list_query
,
1827 SecSecurityClientGet(), &items
, error
), out
);
1828 if (CFGetTypeID(items
) != CFArrayGetTypeID()) {
1829 // Wrap single returned item into the array.
1830 CFArrayRef item_array
= CFArrayCreateForCFTypes(NULL
, items
, NULL
);
1831 CFAssignRetained(items
, item_array
);
1835 CFArrayForEachC(items
, item
) {
1836 CFDataRef data
= CFDictionaryGetValue(item
, kSecValueData
);
1837 require_action_quiet(data
!= NULL
, out
, SecError(errSecInternal
, error
, CFSTR("value not present for token item")));
1839 CFAssignRetained(item_data
, SecTokenItemValueCopy(data
, error
));
1840 require_quiet(item_data
, out
);
1842 CFAssignRetained(item_query
,
1843 CFDictionaryCreateForCFTypes(NULL
,
1844 kSecValuePersistentRef
, CFDictionaryGetValue(item
, kSecValuePersistentRef
),
1846 require_quiet(perform(item_data
, item_query
, error
), out
);
1852 CFReleaseSafe(list_query
);
1853 CFReleaseSafe(items
);
1854 CFReleaseSafe(item_data
);
1855 CFReleaseSafe(ref_array
);
1856 CFReleaseSafe(item_query
);
1860 static bool SecItemRawUpdate(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
, CFErrorRef
*error
) {
1863 // Ensure the dictionary passed to securityd has proper kCFTypeDictionaryKeyCallBacks.
1864 CFMutableDictionaryRef tmp
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
1865 CFDictionaryForEach(attributesToUpdate
, ^(const void *key
, const void *value
) { CFDictionaryAddValue(tmp
, key
, value
); });
1866 ok
= gSecurityd
->sec_item_update(query
, tmp
, SecSecurityClientGet(), error
);
1869 xpc_object_t message
= securityd_create_message(sec_item_update_id
, error
);
1871 if (SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, query
, error
) &&
1872 SecXPCDictionarySetPList(message
, kSecXPCKeyAttributesToUpdate
, attributesToUpdate
, error
)) {
1873 logUnreasonableDataLength(attributesToUpdate
);
1874 xpc_object_t reply
= securityd_message_with_reply_sync(message
, error
);
1876 ok
= securityd_message_no_error(reply
, error
);
1880 xpc_release(message
);
1886 static bool SecTokenItemUpdate(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
, CFErrorRef
*error
) {
1887 return SecTokenItemForEachMatching(query
, error
, ^bool(CFDictionaryRef object_data
, CFDictionaryRef item_query
, CFErrorRef
*error
) {
1889 CFDataRef object_id
= NULL
;
1890 CFMutableDictionaryRef db_value
= NULL
;
1892 // Update attributes on the token.
1893 CFMutableDictionaryRef attributes
= CFDictionaryCreateMutableCopy(NULL
, 0, attributesToUpdate
);
1894 require_quiet(object_id
= SecTokenCopyUpdatedObjectID(token
, CFDictionaryGetValue(object_data
, kSecTokenValueObjectIDKey
),
1895 attributes
, error
), out
);
1897 // Update attributes in the database.
1898 require_quiet(SecItemRawUpdate(item_query
, attributes
, error
), out
);
1903 CFReleaseSafe(object_id
);
1904 CFReleaseSafe(attributes
);
1905 CFReleaseSafe(db_value
);
1910 OSStatus
SecItemUpdate(CFDictionaryRef inQuery
, CFDictionaryRef inAttributesToUpdate
) {
1911 os_activity_t activity
= os_activity_create("SecItemUpdate_ios", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
1912 os_activity_scope(activity
);
1913 os_release(activity
);
1915 return SecOSStatusWith(^bool(CFErrorRef
*error
) {
1916 return SecItemUpdateWithError(inQuery
, inAttributesToUpdate
, error
);
1921 SecItemUpdateWithError(CFDictionaryRef inQuery
,
1922 CFDictionaryRef inAttributesToUpdate
,
1925 __block SecCFDictionaryCOW query
= { inQuery
};
1926 __block SecCFDictionaryCOW attributesToUpdate
= { inAttributesToUpdate
};
1927 bool result
= false;
1929 if (handleUpdateIdentity(inQuery
, inAttributesToUpdate
, &result
, error
))
1932 result
= SecItemAuthDoQuery(&query
, &attributesToUpdate
, SecItemUpdate
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1933 countModifyingAPICall();
1934 if (token
== NULL
) {
1935 return SecItemRawUpdate(query
, attributes
, error
);
1937 return SecTokenItemUpdate(token
, query
, attributes
, error
);
1942 CFReleaseSafe(query
.mutable_dictionary
);
1943 CFReleaseSafe(attributesToUpdate
.mutable_dictionary
);
1944 secdebug("secitem", "SecItemUpdateWithError returned: %d", (int)result
);
1948 static OSStatus
explode_persistent_identity_ref(SecCFDictionaryCOW
*query
)
1950 OSStatus status
= errSecSuccess
;
1951 CFTypeRef persist
= CFDictionaryGetValue(query
->dictionary
, kSecValuePersistentRef
);
1953 if (persist
&& _SecItemParsePersistentRef(persist
, &class, NULL
, NULL
)
1954 && CFEqual(class, kSecClassIdentity
)) {
1955 const void *keys
[] = { kSecReturnRef
, kSecValuePersistentRef
};
1956 const void *vals
[] = { kCFBooleanTrue
, persist
};
1957 CFDictionaryRef persistent_query
= CFDictionaryCreate(NULL
, keys
,
1958 vals
, (array_size(keys
)), NULL
, NULL
);
1959 CFTypeRef item_query
= NULL
;
1960 status
= SecItemCopyMatching(persistent_query
, &item_query
);
1961 CFReleaseNull(persistent_query
);
1964 if (item_query
== NULL
)
1965 return errSecItemNotFound
;
1967 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecValuePersistentRef
);
1968 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query
), kSecValueRef
, item_query
);
1969 CFRelease(item_query
);
1975 OSStatus
SecItemDelete(CFDictionaryRef inQuery
) {
1977 __block SecCFDictionaryCOW query
= { inQuery
};
1979 os_activity_t activity
= os_activity_create("SecItemDelete_ios", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
1980 os_activity_scope(activity
);
1981 os_release(activity
);
1983 require_noerr_quiet(status
= explode_persistent_identity_ref(&query
), errOut
);
1984 require_quiet(!explode_identity(query
.dictionary
, (secitem_operation
)SecItemDelete
, &status
, NULL
), errOut
);
1986 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1987 return SecItemAuthDoQuery(&query
, NULL
, SecItemDelete
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1988 countModifyingAPICall();
1989 if (token
== NULL
) {
1990 return SECURITYD_XPC(sec_item_delete
, dict_client_to_error_request
, query
, SecSecurityClientGet(), error
);
1992 return SecTokenItemForEachMatching(query
, error
, ^bool(CFDictionaryRef object_data
, CFDictionaryRef item_query
, CFErrorRef
*error
) {
1995 // Delete item from the token.
1996 CFDataRef object_id
= CFDictionaryGetValue(object_data
, kSecTokenValueObjectIDKey
);
1997 CFErrorRef localError
= NULL
;
1998 if (!TKTokenDeleteObject(token
, object_id
, &localError
)) {
1999 // Check whether object was not found; in this case, ignore the error.
2000 require_action_quiet(CFEqual(CFErrorGetDomain(localError
), CFSTR(kTKErrorDomain
)) &&
2001 CFErrorGetCode(localError
) == kTKErrorCodeObjectNotFound
, out
,
2002 (CFErrorPropagate(localError
, error
), SecTokenProcessError(kAKSKeyOpDelete
, token
, object_id
, error
)));
2003 CFReleaseNull(localError
);
2006 // Delete the item from the keychain.
2007 require_quiet(SECURITYD_XPC(sec_item_delete
, dict_client_to_error_request
, item_query
,
2008 SecSecurityClientGet(), error
), out
);
2019 CFReleaseSafe(query
.mutable_dictionary
);
2020 secdebug("secitem", "SecItemDelete returned: %d", (int)status
);
2026 SecItemDeleteAll(void)
2028 return SecOSStatusWith(^bool (CFErrorRef
*error
) {
2031 if (!gSecurityd
->sec_item_delete_all(error
))
2032 ok
&= SecError(errSecInternal
, error
, CFSTR("sec_item_delete_all is NULL"));
2034 ok
&= securityd_send_sync_and_do(sec_delete_all_id
, error
, NULL
, NULL
);
2040 bool SecItemDeleteAllWithAccessGroups(CFArrayRef accessGroups
, CFErrorRef
*error
) {
2045 SecItemUpdateTokenItemsForAccessGroups(CFTypeRef tokenID
, CFArrayRef accessGroups
, CFArrayRef tokenItemsAttributes
)
2049 os_activity_t activity
= os_activity_create("SecItemUpdateTokenItemsForAccessGroups", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
2050 os_activity_scope(activity
);
2051 os_release(activity
);
2053 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
2055 CFMutableArrayRef tokenItemsForServer
= NULL
;
2056 if (tokenItemsAttributes
) {
2057 tokenItemsForServer
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
2058 for (CFIndex i
= 0; i
< CFArrayGetCount(tokenItemsAttributes
); ++i
) {
2059 CFDictionaryRef itemAttributes
= CFArrayGetValueAtIndex(tokenItemsAttributes
, i
);
2060 CFTypeRef accessControl
= CFDictionaryGetValue(itemAttributes
, kSecAttrAccessControl
);
2061 CFTypeRef tokenOID
= CFDictionaryGetValue(itemAttributes
, kSecAttrTokenOID
);
2062 CFTypeRef valueData
= CFDictionaryGetValue(itemAttributes
, kSecValueData
);
2063 if (tokenOID
!= NULL
&& accessControl
!= NULL
&& CFDataGetTypeID() == CFGetTypeID(accessControl
)) {
2065 require_quiet(data
= SecTokenItemValueCreate(tokenOID
, accessControl
, valueData
, error
), out
);
2066 CFMutableDictionaryRef attributes
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, itemAttributes
);
2067 CFDictionarySetValue(attributes
, kSecValueData
, data
);
2068 CFDictionarySetValue(attributes
, kSecAttrTokenID
, tokenID
);
2069 CFDictionaryRemoveValue(attributes
, kSecAttrAccessControl
);
2070 CFDictionaryRemoveValue(attributes
, kSecAttrTokenOID
);
2071 CFArrayAppendValue(tokenItemsForServer
, attributes
);
2072 CFReleaseNull(attributes
);
2073 CFReleaseNull(data
);
2075 CFArrayAppendValue(tokenItemsForServer
, itemAttributes
);
2080 ok
= SECURITYD_XPC(sec_item_update_token_items_for_access_groups
, cfstring_array_array_to_error_request
, tokenID
, accessGroups
, tokenItemsForServer
, SecSecurityClientGet(), error
);
2082 CFReleaseNull(tokenItemsForServer
);
2089 CFArrayRef
_SecKeychainSyncUpdateMessage(CFDictionaryRef updates
, CFErrorRef
*error
) {
2090 __block CFArrayRef result
;
2091 os_activity_initiate("_SecKeychainSyncUpdateMessage", OS_ACTIVITY_FLAG_DEFAULT
, ^{
2092 result
= SECURITYD_XPC(sec_keychain_sync_update_message
, dict_to_array_error_request
, updates
, error
);
2097 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
2099 bool _SecKeychainRollKeys(bool force
, CFErrorRef
*error
)
2101 do_if_registered(sec_roll_keys
, force
, error
);
2103 __block
bool result
= false;
2105 secdebug("secitem","enter - %s", __FUNCTION__
);
2106 securityd_send_sync_and_do(kSecXPCOpRollKeys
, error
,
2107 ^bool(xpc_object_t message
, CFErrorRef
*error
) {
2108 xpc_dictionary_set_bool(message
, "force", force
);
2111 ^bool(xpc_object_t response
, __unused CFErrorRef
*error
) {
2112 result
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);
2118 static CFArrayRef
data_array_to_array_error_request(enum SecXPCOperation op
, CFDataRef normalizedIssuer
, CFArrayRef accessGroups
, CFErrorRef
*error
) {
2119 __block CFArrayRef results
= NULL
;
2120 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
2121 SecXPCDictionarySetData(message
, kSecXPCKeyNormalizedIssuer
, normalizedIssuer
, error
);
2122 SecXPCDictionarySetPList(message
, kSecXPCKeyAccessGroups
, accessGroups
, error
);
2124 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
2125 return SecXPCDictionaryCopyArrayOptional(response
, kSecXPCKeyResult
, &results
, error
);
2130 static bool data_data_array_to_bool_error_request(enum SecXPCOperation op
, CFDataRef normalizedIssuer
, CFDataRef serialNumber
, CFArrayRef accessGroups
, CFErrorRef
*error
) {
2131 __block
bool result
= false;
2132 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
2133 SecXPCDictionarySetData(message
, kSecXPCKeyNormalizedIssuer
, normalizedIssuer
, error
);
2134 SecXPCDictionarySetData(message
, kSecXPCKeySerialNumber
, serialNumber
, error
);
2135 SecXPCDictionarySetPList(message
, kSecXPCKeyAccessGroups
, accessGroups
, error
);
2137 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
2138 result
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);
2144 CFArrayRef
SecItemCopyParentCertificates_ios(CFDataRef normalizedIssuer
, CFArrayRef accessGroups
, CFErrorRef
*error
) {
2145 CFArrayRef results
= NULL
;
2147 os_activity_t activity
= os_activity_create("SecItemCopyParentCertificates_ios", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
2148 os_activity_scope(activity
);
2149 os_release(activity
);
2151 results
= SECURITYD_XPC(sec_item_copy_parent_certificates
, data_array_to_array_error_request
, normalizedIssuer
, accessGroups
, error
);
2156 bool SecItemCertificateExists(CFDataRef normalizedIssuer
, CFDataRef serialNumber
, CFArrayRef accessGroups
, CFErrorRef
*error
) {
2157 bool results
= false;
2159 os_activity_t activity
= os_activity_create("SecItemCertificateExists", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
2160 os_activity_scope(activity
);
2161 os_release(activity
);
2163 results
= SECURITYD_XPC(sec_item_certificate_exists
, data_data_array_to_bool_error_request
, normalizedIssuer
, serialNumber
, accessGroups
, error
);