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>
77 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
80 #include <Security/SecInternal.h>
81 #include "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.h>
102 /* Return an OSStatus for a sqlite3 error code. */
103 static OSStatus
osstatus_for_s3e(int s3e
)
105 if (s3e
> 0 && s3e
<= SQLITE_DONE
) switch (s3e
)
110 return errSecNotAvailable
; /* errSecDuplicateItem; */
111 case SQLITE_FULL
: /* Happens if we run out of uniqueids */
112 return errSecNotAvailable
; /* TODO: Replace with a better error code. */
114 case SQLITE_READONLY
:
115 return errSecNotAvailable
;
116 case SQLITE_CANTOPEN
:
117 return errSecNotAvailable
;
119 return errSecNotAvailable
;
120 case SQLITE_CONSTRAINT
:
121 return errSecDuplicateItem
;
124 case SQLITE_MISMATCH
:
125 return errSecNoSuchAttr
;
127 return errSecNotAvailable
;
129 return -2; /* TODO: Replace with a real error code. */
130 case SQLITE_INTERNAL
:
132 return errSecNotAvailable
; /* TODO: Replace with a real error code. */
137 static OSStatus
osstatus_for_kern_return(CFIndex kernResult
)
142 return errSecSuccess
;
143 case kIOReturnNotReadable
:
144 case kIOReturnNotWritable
:
145 return errSecAuthFailed
;
146 case kIOReturnNotPermitted
:
147 case kIOReturnNotPrivileged
:
148 case kIOReturnLockedRead
:
149 case kIOReturnLockedWrite
:
150 return errSecInteractionNotAllowed
;
153 case kIOReturnBadArgument
:
156 return errSecNotAvailable
; /* TODO: Replace with a real error code. */
160 static OSStatus
osstatus_for_xpc_error(CFIndex xpcError
) {
163 case kSecXPCErrorSuccess
:
164 return errSecSuccess
;
165 case kSecXPCErrorUnexpectedType
:
166 case kSecXPCErrorUnexpectedNull
:
168 case kSecXPCErrorConnectionFailed
:
169 return errSecNotAvailable
;
170 case kSecXPCErrorUnknown
:
172 return errSecInternal
;
176 static OSStatus
osstatus_for_der_error(CFIndex derError
) {
179 case kSecDERErrorUnknownEncoding
:
180 case kSecDERErrorUnsupportedDERType
:
181 case kSecDERErrorUnsupportedNumberType
:
183 case kSecDERErrorUnsupportedCFObject
:
185 case kSecDERErrorAllocationFailure
:
186 return errSecAllocate
;
188 return errSecInternal
;
192 static OSStatus
osstatus_for_ids_error(CFIndex idsError
) {
195 case kSecIDSErrorNoDeviceID
:
196 return errSecDeviceIDNeeded
;
197 case kSecIDSErrorNotRegistered
:
198 return errSecIDSNotRegistered
;
199 case kSecIDSErrorFailedToSend
:
200 return errSecFailedToSendIDSMessage
;
201 case kSecIDSErrorCouldNotFindMatchingAuthToken
:
202 return errSecDeviceIDNoMatch
;
203 case kSecIDSErrorNoPeersAvailable
:
204 return errSecPeersNotAvailable
;
206 return errSecInternal
;
210 static OSStatus
osstatus_for_localauthentication_error(CFIndex laError
) {
211 // Wrap LA error in Sec error.
213 case kLAErrorUserCancel
:
214 return errSecUserCanceled
;
215 case kLAErrorParameter
:
217 case kLAErrorNotInteractive
:
218 return errSecInteractionNotAllowed
;
220 return errSecAuthFailed
;
224 static OSStatus
osstatus_for_ctk_error(CFIndex ctkError
) {
226 case kTKErrorCodeBadParameter
:
228 case kTKErrorCodeNotImplemented
:
229 return errSecUnimplemented
;
230 case kTKErrorCodeCanceledByUser
:
231 return errSecUserCanceled
;
232 case kTKErrorCodeCorruptedData
:
235 return errSecInternal
;
240 // Convert from securityd error codes to OSStatus for legacy API.
241 OSStatus
SecErrorGetOSStatus(CFErrorRef error
) {
244 status
= errSecSuccess
;
246 CFStringRef domain
= CFErrorGetDomain(error
);
247 if (domain
== NULL
) {
248 secerror("No error domain for error: %@", error
);
249 status
= errSecInternal
;
250 } else if (CFEqual(kSecErrorDomain
, domain
)) {
251 status
= (OSStatus
)CFErrorGetCode(error
);
252 } else if (CFEqual(kSecDbErrorDomain
, domain
)) {
253 status
= osstatus_for_s3e((int)CFErrorGetCode(error
));
254 } else if (CFEqual(kSecErrnoDomain
, domain
)) {
255 status
= (OSStatus
)CFErrorGetCode(error
);
256 } else if (CFEqual(kSecKernDomain
, domain
)) {
257 status
= osstatus_for_kern_return(CFErrorGetCode(error
));
258 } else if (CFEqual(sSecXPCErrorDomain
, domain
)) {
259 status
= osstatus_for_xpc_error(CFErrorGetCode(error
));
260 } else if (CFEqual(sSecDERErrorDomain
, domain
)) {
261 status
= osstatus_for_der_error(CFErrorGetCode(error
));
262 } else if (CFEqual(kSecIDSErrorDomain
, domain
)) {
263 status
= osstatus_for_ids_error(CFErrorGetCode(error
));
264 } else if (CFEqual(CFSTR(kLAErrorDomain
), domain
)) {
265 status
= osstatus_for_localauthentication_error(CFErrorGetCode(error
));
266 } else if (CFEqual(CFSTR(kTKErrorDomain
), domain
)) {
267 status
= osstatus_for_ctk_error(CFErrorGetCode(error
));
268 } else if (CFEqual(kSOSErrorDomain
, domain
)) {
269 status
= errSecInternal
;
271 secnotice("securityd", "unknown error domain: %@ for error: %@", domain
, error
);
272 status
= errSecInternal
;
279 lastErrorReleaseError(void *value
)
286 getLastErrorKey(pthread_key_t
*kv
)
288 static pthread_key_t key
;
289 static bool haveKey
= false;
290 static dispatch_once_t onceToken
;
291 dispatch_once(&onceToken
, ^{
292 if (pthread_key_create(&key
, lastErrorReleaseError
) == 0)
300 SetLastError(CFErrorRef newError
)
303 if (!getLastErrorKey(&key
))
305 CFErrorRef oldError
= pthread_getspecific(key
);
310 pthread_setspecific(key
, newError
);
314 SecCopyLastError(OSStatus status
)
319 if (!getLastErrorKey(&key
))
322 error
= pthread_getspecific(key
);
324 if (status
&& status
!= SecErrorGetOSStatus(error
)) {
333 // Wrapper to provide a CFErrorRef for legacy API.
334 OSStatus
SecOSStatusWith(bool (^perform
)(CFErrorRef
*error
)) {
335 CFErrorRef error
= NULL
;
337 if (perform(&error
)) {
338 assert(error
== NULL
);
340 status
= errSecSuccess
;
344 status
= SecErrorGetOSStatus(error
);
345 if (status
!= errSecItemNotFound
) // Occurs in normal operation, so exclude
346 secinfo("OSStatus", "error:[%" PRIdOSStatus
"] %@", status
, error
);
347 CFReleaseNull(error
);
352 /* Drop assorted kSecAttrCanXxxx attributes from the query, because these attributes are generated
353 by SecKey implementation and may differ between OS versions, see <rdar://problem/27095761>.
356 static CFDictionaryRef
357 AttributeCreateFilteredOutSecAttrs(CFDictionaryRef attributes
)
359 CFMutableDictionaryRef filtered
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, attributes
);
360 if (filtered
== NULL
)
362 CFDictionaryRemoveValue(filtered
, kSecAttrCanSign
);
363 CFDictionaryRemoveValue(filtered
, kSecAttrCanVerify
);
364 CFDictionaryRemoveValue(filtered
, kSecAttrCanEncrypt
);
365 CFDictionaryRemoveValue(filtered
, kSecAttrCanDecrypt
);
366 CFDictionaryRemoveValue(filtered
, kSecAttrCanDerive
);
367 CFDictionaryRemoveValue(filtered
, kSecAttrCanWrap
);
368 CFDictionaryRemoveValue(filtered
, kSecAttrCanUnwrap
);
369 CFDictionaryRemoveValue(filtered
, kSecAttrCanSignRecover
);
370 CFDictionaryRemoveValue(filtered
, kSecAttrCanVerifyRecover
);
376 /* IPC uses CFPropertyList to un/marshall input/output data and can handle:
377 CFData, CFString, CFArray, CFDictionary, CFDate, CFBoolean, and CFNumber
379 Currently in need of conversion below:
380 @@@ kSecValueRef allows SecKeychainItemRef and SecIdentityRef
381 @@@ kSecUseItemList allows a query against a list of itemrefs, this isn't
382 currently implemented at all, but when it is needs to short circuit to
383 local evaluation, different from the sql query abilities
386 static CFDictionaryRef
387 SecItemCopyAttributeDictionary(CFTypeRef ref
, bool forQuery
) {
388 CFDictionaryRef refDictionary
= NULL
;
389 CFTypeID typeID
= CFGetTypeID(ref
);
390 if (typeID
== SecKeyGetTypeID()) {
391 refDictionary
= SecKeyCopyAttributeDictionary((SecKeyRef
)ref
);
392 if (refDictionary
&& forQuery
) {
393 CFDictionaryRef filtered
= AttributeCreateFilteredOutSecAttrs(refDictionary
);
394 CFAssignRetained(refDictionary
, filtered
);
396 } else if (typeID
== SecCertificateGetTypeID()) {
397 refDictionary
= SecCertificateCopyAttributeDictionary((SecCertificateRef
)ref
);
398 } else if (typeID
== SecIdentityGetTypeID()) {
400 SecIdentityRef identity
= (SecIdentityRef
)ref
;
401 SecCertificateRef cert
= NULL
;
402 SecKeyRef key
= NULL
;
403 if (!SecIdentityCopyCertificate(identity
, &cert
) &&
404 !SecIdentityCopyPrivateKey(identity
, &key
))
406 CFDataRef data
= SecCertificateCopyData(cert
);
407 CFDictionaryRef key_dict
= SecKeyCopyAttributeDictionary(key
);
409 if (key_dict
&& data
) {
410 refDictionary
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, key_dict
);
411 CFDictionarySetValue((CFMutableDictionaryRef
)refDictionary
, kSecAttrIdentityCertificateData
, data
);
413 CFReleaseNull(key_dict
);
419 return refDictionary
;
422 #ifdef SECITEM_SHIM_OSX
423 extern CFTypeRef
SecItemCreateFromAttributeDictionary_osx(CFDictionaryRef refAttributes
);
427 SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes
) {
428 CFTypeRef ref
= NULL
;
429 CFStringRef
class = CFDictionaryGetValue(refAttributes
, kSecClass
);
430 if (CFEqual(class, kSecClassKey
)) {
431 ref
= SecKeyCreateFromAttributeDictionary(refAttributes
);
432 } else if (CFEqual(class, kSecClassCertificate
)) {
433 ref
= SecCertificateCreateFromAttributeDictionary(refAttributes
);
434 } else if (CFEqual(class, kSecClassIdentity
)) {
435 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecAttrIdentityCertificateData
);
436 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
);
437 SecKeyRef key
= SecKeyCreateFromAttributeDictionary(refAttributes
);
439 ref
= SecIdentityCreate(kCFAllocatorDefault
, cert
, key
);
442 #ifdef SECITEM_SHIM_OSX
444 ref
= SecItemCreateFromAttributeDictionary_osx(refAttributes
);
451 SecItemCopyDisplayNames(CFArrayRef items
, CFArrayRef
*displayNames
)
454 return -1 /* errSecUnimplemented */;
457 typedef OSStatus (*secitem_operation
)(CFDictionaryRef attributes
, CFTypeRef
*result
);
459 static bool explode_identity(CFDictionaryRef attributes
, secitem_operation operation
,
460 OSStatus
*return_status
, CFTypeRef
*return_result
)
462 bool handled
= false;
463 CFTypeRef value
= CFDictionaryGetValue(attributes
, kSecValueRef
);
465 CFTypeID typeID
= CFGetTypeID(value
);
466 if (typeID
== SecIdentityGetTypeID()) {
468 OSStatus status
= errSecSuccess
;
469 SecIdentityRef identity
= (SecIdentityRef
)value
;
470 SecCertificateRef cert
= NULL
;
471 SecKeyRef key
= NULL
;
472 if (!SecIdentityCopyCertificate(identity
, &cert
) &&
473 !SecIdentityCopyPrivateKey(identity
, &key
))
475 CFMutableDictionaryRef partial_query
=
476 CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, attributes
);
477 CFDictionarySetValue(partial_query
, kSecValueRef
, cert
);
478 CFTypeRef result
= NULL
;
479 bool duplicate_cert
= false;
480 /* an identity is first and foremost a key, but it can have multiple
481 certs associated with it: so we identify it by the cert */
482 status
= operation(partial_query
, return_result
? &result
: NULL
);
483 if ((operation
== (secitem_operation
)SecItemAdd
) &&
484 (status
== errSecDuplicateItem
)) {
485 duplicate_cert
= true;
486 status
= errSecSuccess
;
489 if (!status
|| status
== errSecItemNotFound
) {
490 bool skip_key_operation
= false;
492 /* if the key is still in use, skip deleting it */
493 if (operation
== (secitem_operation
)SecItemDelete
) {
494 // find certs with cert.pkhh == keys.klbl
495 CFDictionaryRef key_dict
= NULL
, query_dict
= NULL
;
496 CFDataRef pkhh
= NULL
;
498 key_dict
= SecKeyCopyAttributeDictionary(key
);
500 pkhh
= (CFDataRef
)CFDictionaryGetValue(key_dict
, kSecAttrApplicationLabel
);
501 const void *keys
[] = { kSecClass
, kSecAttrPublicKeyHash
};
502 const void *vals
[] = { kSecClassCertificate
, pkhh
};
504 query_dict
= CFDictionaryCreate(NULL
, keys
,
505 vals
, (array_size(keys
)),
508 if (errSecSuccess
== SecItemCopyMatching(query_dict
, NULL
))
509 skip_key_operation
= true;
510 CFReleaseSafe(query_dict
);
511 CFReleaseSafe(key_dict
);
514 if (!skip_key_operation
) {
515 /* now perform the operation for the key */
516 CFDictionarySetValue(partial_query
, kSecValueRef
, key
);
517 CFDictionarySetValue(partial_query
, kSecReturnPersistentRef
, kCFBooleanFalse
);
519 status
= operation(partial_query
, NULL
);
520 if ((operation
== (secitem_operation
)SecItemAdd
) &&
521 (status
== errSecDuplicateItem
) &&
523 status
= errSecSuccess
;
526 /* add and copy matching for an identityref have a persistent ref result */
529 /* result is a persistent ref to a cert */
531 if (_SecItemParsePersistentRef(result
, NULL
, &rowid
)) {
532 *return_result
= _SecItemMakePersistentRef(kSecClassIdentity
, rowid
);
538 CFReleaseNull(partial_query
);
541 status
= errSecInvalidItemRef
;
545 *return_status
= status
;
548 value
= CFDictionaryGetValue(attributes
, kSecClass
);
549 if (value
&& CFEqual(kSecClassIdentity
, value
) &&
550 (operation
== (secitem_operation
)SecItemDelete
)) {
551 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, attributes
);
552 CFDictionaryRemoveValue(dict
, kSecClass
);
553 CFDictionarySetValue(dict
, kSecClass
, kSecClassCertificate
);
554 OSStatus status
= SecItemDelete(dict
);
556 CFDictionarySetValue(dict
, kSecClass
, kSecClassKey
);
557 status
= SecItemDelete(dict
);
560 *return_status
= status
;
568 SecErrorPropagateLastError(OSStatus status
, CFErrorRef
*error
)
571 CFErrorRef lastError
= SecCopyLastError(status
);
573 CFErrorPropagate(lastError
, error
);
575 SecError(status
, error
, CFSTR("SecError: error not captured, OSStatus was: %d"), (int)status
);
582 handleUpdateIdentity(CFDictionaryRef query
,
583 CFDictionaryRef update
,
587 CFMutableDictionaryRef updatedQuery
= NULL
;
588 SecCertificateRef cert
= NULL
;
589 SecKeyRef key
= NULL
;
590 bool handled
= false;
594 CFTypeRef value
= CFDictionaryGetValue(query
, kSecValueRef
);
596 CFTypeID typeID
= CFGetTypeID(value
);
597 if (typeID
== SecIdentityGetTypeID()) {
598 SecIdentityRef identity
= (SecIdentityRef
)value
;
603 status
= SecIdentityCopyCertificate(identity
, &cert
);
604 require_noerr_action_quiet(status
, errOut
, SecErrorPropagateLastError(status
, error
));
606 status
= SecIdentityCopyPrivateKey(identity
, &key
);
607 require_noerr_action_quiet(status
, errOut
, SecErrorPropagateLastError(status
, error
));
609 updatedQuery
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, query
);
610 require_action_quiet(updatedQuery
, errOut
, *result
= false);
612 CFDictionarySetValue(updatedQuery
, kSecValueRef
, cert
);
613 require_quiet(SecItemUpdateWithError(updatedQuery
, update
, error
), errOut
);
615 CFDictionarySetValue(updatedQuery
, kSecValueRef
, key
);
616 require_quiet(SecItemUpdateWithError(updatedQuery
, update
, error
), errOut
);
620 value
= CFDictionaryGetValue(query
, kSecClass
);
621 if (value
&& CFEqual(kSecClassIdentity
, value
)) {
624 updatedQuery
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, query
);
625 require_action_quiet(updatedQuery
, errOut
, *result
= false);
627 CFDictionarySetValue(updatedQuery
, kSecClass
, kSecClassCertificate
);
628 require_quiet(SecItemUpdateWithError(updatedQuery
, update
, error
), errOut
);
630 CFDictionarySetValue(updatedQuery
, kSecClass
, kSecClassKey
);
631 require_quiet(SecItemUpdateWithError(updatedQuery
, update
, error
), errOut
);
633 CFReleaseNull(updatedQuery
);
638 CFReleaseNull(updatedQuery
);
644 static void infer_cert_label(SecCFDictionaryCOW
*attributes
)
646 if (!CFDictionaryContainsKey(attributes
->dictionary
, kSecAttrLabel
)) {
647 CFTypeRef value_ref
= CFDictionaryGetValue(attributes
->dictionary
, kSecValueRef
);
648 if (value_ref
&& CFGetTypeID(value_ref
) == SecCertificateGetTypeID()) {
649 SecCertificateRef certificate
= (SecCertificateRef
)value_ref
;
650 CFStringRef label
= SecCertificateCopySubjectSummary(certificate
);
652 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes
), kSecAttrLabel
, label
);
653 CFReleaseNull(label
);
659 /* A persistent ref is just the class and the rowid of the record. */
660 CF_RETURNS_RETAINED CFDataRef
_SecItemMakePersistentRef(CFTypeRef
class, sqlite_int64 rowid
)
662 uint8_t bytes
[sizeof(sqlite_int64
) + 4];
665 if (CFStringGetCString(class, (char *)bytes
, 4 + 1 /*null-term*/,
666 kCFStringEncodingUTF8
))
668 OSWriteBigInt64(bytes
+ 4, 0, rowid
);
669 return CFDataCreate(NULL
, bytes
, sizeof(bytes
));
674 /* AUDIT[securityd](done):
675 persistent_ref (ok) is a caller provided, non NULL CFTypeRef.
677 bool _SecItemParsePersistentRef(CFDataRef persistent_ref
, CFStringRef
*return_class
, sqlite_int64
*return_rowid
)
679 bool valid_ref
= false;
680 if (CFGetTypeID(persistent_ref
) == CFDataGetTypeID() &&
681 CFDataGetLength(persistent_ref
) == (CFIndex
)(sizeof(sqlite_int64
) + 4)) {
682 const uint8_t *bytes
= CFDataGetBytePtr(persistent_ref
);
683 sqlite_int64 rowid
= OSReadBigInt64(bytes
+ 4, 0);
685 CFStringRef
class = CFStringCreateWithBytes(kCFAllocatorDefault
,
686 bytes
, CFStringGetLength(kSecClassGenericPassword
),
687 kCFStringEncodingUTF8
, true);
688 const void *valid_classes
[] = { kSecClassGenericPassword
,
689 kSecClassInternetPassword
,
690 kSecClassAppleSharePassword
,
691 kSecClassCertificate
,
696 for (i
=0; i
< array_size(valid_classes
); i
++) {
697 if (CFEqual(valid_classes
[i
], class)) {
699 *return_class
= valid_classes
[i
];
701 *return_rowid
= rowid
;
711 static bool cf_bool_value(CFTypeRef cf_bool
)
713 return (cf_bool
&& CFEqual(kCFBooleanTrue
, cf_bool
));
716 CFMutableDictionaryRef
SecCFDictionaryCOWGetMutable(SecCFDictionaryCOW
*cow_dictionary
) {
717 if (cow_dictionary
->mutable_dictionary
== NULL
) {
718 cow_dictionary
->mutable_dictionary
= CFDictionaryCreateMutableForCFTypes(NULL
);
719 if (cow_dictionary
->dictionary
!= NULL
) {
720 CFDictionaryForEach(cow_dictionary
->dictionary
, ^(const void *key
, const void *value
) {
721 CFDictionarySetValue(cow_dictionary
->mutable_dictionary
, key
, value
);
724 cow_dictionary
->dictionary
= cow_dictionary
->mutable_dictionary
;
727 return cow_dictionary
->mutable_dictionary
;
730 // Keys for dictionary of kSecvalueData of token-based items.
731 static const CFStringRef kSecTokenValueObjectIDKey
= CFSTR("oid");
732 static const CFStringRef kSecTokenValueAccessControlKey
= CFSTR("ac");
733 static const CFStringRef kSecTokenValueDataKey
= CFSTR("data");
735 // Creates kSecValueData field stored in the DB for token-based items. Data field consists of objectID, real
736 // access_control and optionally of the data value.
737 static CFDataRef
SecTokenItemValueCreate(CFDataRef oid
, CFDataRef access_control
, CFDataRef object_value
, CFErrorRef
*error
) {
738 CFMutableDictionaryRef value
= NULL
;
739 value
= CFDictionaryCreateMutableForCFTypesWith(NULL
,
740 kSecTokenValueObjectIDKey
, oid
,
741 kSecTokenValueAccessControlKey
, access_control
,
743 if (object_value
!= NULL
) {
744 CFDictionarySetValue(value
, kSecTokenValueDataKey
, object_value
);
747 CFDataRef value_data
= CFPropertyListCreateDERData(NULL
, value
, error
);
752 static CFDictionaryRef
SecTokenItemValueCopy(CFDataRef db_value
, CFErrorRef
*error
) {
753 CFPropertyListRef plist
= NULL
;
754 const uint8_t *der
= CFDataGetBytePtr(db_value
);
755 const uint8_t *der_end
= der
+ CFDataGetLength(db_value
);
756 require_quiet(der
= der_decode_plist(0, kCFPropertyListImmutable
, &plist
, error
, der
, der_end
), out
);
757 require_action_quiet(der
== der_end
, out
, SecError(errSecDecode
, error
, CFSTR("trailing garbage at end of token data field")));
758 require_action_quiet(CFDictionaryGetValue(plist
, kSecTokenValueObjectIDKey
) != NULL
, out
,
759 SecError(errSecInternal
, error
, CFSTR("token based item data does not have OID")));
765 CFDataRef
_SecTokenItemCopyValueData(CFDataRef db_value
, CFErrorRef
*error
) {
766 CFDataRef valueData
= NULL
;
767 CFDictionaryRef itemDict
= NULL
;
768 require_quiet(itemDict
= SecTokenItemValueCopy(db_value
, error
), out
);
769 CFRetainAssign(valueData
, CFDictionaryGetValue(itemDict
, kSecTokenValueDataKey
));
770 require_action_quiet(valueData
, out
, SecError(errSecInternal
, error
, CFSTR("token item does not contain value data")));
773 CFReleaseSafe(itemDict
);
777 TKTokenRef
SecTokenCreate(CFStringRef token_id
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
778 CFMutableDictionaryRef token_attrs
= NULL
;
779 TKTokenRef token
= NULL
;
780 token_attrs
= (auth_params
!= NULL
) ?
781 CFDictionaryCreateMutableCopy(NULL
, 0, auth_params
) :
782 CFDictionaryCreateMutableForCFTypes(NULL
);
783 CFDictionarySetValue(token_attrs
, kSecAttrTokenID
, token_id
);
785 CFDictionaryRemoveValue(token_attrs
, kSecUseAuthenticationContext
);
786 token
= TKTokenCreate(token_attrs
, error
);
788 CFReleaseSafe(token_attrs
);
792 static bool SecTokenItemCreateFromAttributes(CFDictionaryRef attributes
, CFDictionaryRef auth_params
,
793 TKTokenRef token
, CFDataRef object_id
, CFTypeRef
*ref
, CFErrorRef
*error
) {
795 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutableCopy(NULL
, 0, attributes
);
796 CFTypeRef token_id
= CFDictionaryGetValue(attributes
, kSecAttrTokenID
);
797 if (token_id
!= NULL
&& object_id
!= NULL
) {
798 if (CFRetainSafe(token
) == NULL
) {
799 require_quiet(token
= SecTokenCreate(token_id
, auth_params
, error
), out
);
802 if (auth_params
!= NULL
) {
803 CFDictionaryForEach(auth_params
, ^(const void *key
, const void *value
) {
804 CFDictionarySetValue(attrs
, key
, value
);
807 CFDictionarySetValue(attrs
, kSecUseToken
, token
);
808 CFDictionarySetValue(attrs
, kSecUseTokenObjectID
, object_id
);
811 *ref
= SecItemCreateFromAttributeDictionary(attrs
);
815 CFReleaseSafe(attrs
);
820 /* Turn the returned single value or dictionary that contains all the attributes to create a
821 ref into the exact result the client asked for */
822 static bool SecItemResultCopyPrepared(CFTypeRef raw_result
, TKTokenRef token
,
823 CFDictionaryRef query
, CFDictionaryRef auth_params
,
824 CFTypeRef
*result
, CFErrorRef
*error
) {
826 CFDataRef ac_data
= NULL
;
827 CFDataRef value
= NULL
;
828 CFTypeRef persistent_ref
= NULL
;
829 CFStringRef token_id
= NULL
;
830 CFStringRef cert_token_id
= NULL
;
831 CFDataRef object_id
= NULL
;
832 CFMutableDictionaryRef attrs
= NULL
;
833 CFDataRef cert_data
= NULL
;
834 CFDataRef cert_object_id
= NULL
;
835 TKTokenRef cert_token
= NULL
;
837 bool wants_ref
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnRef
));
838 bool wants_data
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnData
));
839 bool wants_attributes
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnAttributes
));
840 bool wants_persistent_ref
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnPersistentRef
));
842 // Get token value if not provided by the caller.
843 bool token_item
= false;
844 bool cert_token_item
= false;
846 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID()) {
847 token_id
= CFDictionaryGetValue(raw_result
, kSecAttrTokenID
);
848 token_item
= (token_id
!= NULL
);
850 cert_token_id
= CFDictionaryGetValue(raw_result
, kSecAttrIdentityCertificateTokenID
);
851 cert_token_item
= (cert_token_id
!= NULL
);
855 cert_token_item
= true;
857 CFRetainAssign(cert_token
, token
);
860 // Decode and prepare data value, if it is requested at the output, or if we want attributes from token.
861 if (wants_data
|| wants_ref
|| (token_item
&& wants_attributes
)) {
862 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID())
863 value
= CFRetainSafe(CFDictionaryGetValue(raw_result
, kSecValueData
));
865 value
= CFRetainSafe(raw_result
);
866 if (token_item
&& value
!= NULL
) {
867 // Parse token-based item's data field.
868 CFDataRef object_value
= NULL
;
869 CFDictionaryRef parsed_value
= NULL
;
870 require_quiet(parsed_value
= SecTokenItemValueCopy(value
, error
), out
);
871 object_id
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueObjectIDKey
));
872 ac_data
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueAccessControlKey
));
873 object_value
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueDataKey
));
874 CFRelease(parsed_value
);
875 if ((wants_data
|| wants_ref
) && object_value
== NULL
) {
876 // Retrieve value directly from the token.
878 require_quiet(token
= SecTokenCreate(token_id
, auth_params
, error
), out
);
880 require_quiet(object_value
= TKTokenCopyObjectData(token
, object_id
, error
), out
);
881 if (CFEqual(object_value
, kCFNull
))
882 CFReleaseNull(object_value
);
884 CFAssignRetained(value
, object_value
);
887 // If only thing requested is data, return them directly.
888 if (!(wants_attributes
|| wants_persistent_ref
|| wants_ref
)) {
889 *result
= CFRetainSafe(value
);
895 // Extract persistent_ref, if caller wants it.
896 if (wants_persistent_ref
) {
897 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID())
898 persistent_ref
= CFRetainSafe(CFDictionaryGetValue(raw_result
, kSecValuePersistentRef
));
900 persistent_ref
= CFRetainSafe(raw_result
);
902 // If only thing requested is persistentref, extract it from dictionary if needed and return it.
903 if (!(wants_attributes
|| wants_data
|| wants_ref
)) {
904 *result
= CFRetainSafe(persistent_ref
);
910 if (wants_ref
|| wants_attributes
|| (wants_data
&& wants_persistent_ref
)) {
911 // For these cases we need output dictionary.
912 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID())
913 *result
= CFDictionaryCreateMutableCopy(NULL
, 0, raw_result
);
915 *result
= CFDictionaryCreateForCFTypes(NULL
, NULL
);
916 CFMutableDictionaryRef output
= (CFMutableDictionaryRef
)*result
;
918 if ((wants_data
|| wants_ref
) && value
!= NULL
)
919 CFDictionarySetValue(output
, kSecValueData
, value
);
921 CFDictionaryRemoveValue(output
, kSecValueData
);
923 if (wants_persistent_ref
&& persistent_ref
!= NULL
)
924 CFDictionarySetValue(output
, kSecValuePersistentRef
, persistent_ref
);
926 CFDictionaryRemoveValue(output
, kSecValuePersistentRef
);
928 if ((wants_ref
|| wants_attributes
) && cert_token_item
&&
929 CFEqualSafe(CFDictionaryGetValue(output
, kSecClass
), kSecClassIdentity
)) {
930 // Decode also certdata field of the identity.
931 CFDataRef data
= CFDictionaryGetValue(output
, kSecAttrIdentityCertificateData
);
933 CFDictionaryRef parsed_value
;
934 require_quiet(parsed_value
= SecTokenItemValueCopy(data
, error
), out
);
935 cert_data
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueDataKey
));
936 cert_object_id
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueObjectIDKey
));
937 CFRelease(parsed_value
);
938 if (cert_data
== NULL
) {
939 // Retrieve value directly from the token.
940 if (cert_token
== NULL
) {
941 require_quiet(cert_token
= SecTokenCreate(cert_token_id
, auth_params
, error
), out
);
943 require_quiet(cert_data
= TKTokenCopyObjectData(cert_token
, cert_object_id
, error
), out
);
944 if (CFEqual(cert_data
, kCFNull
))
945 CFReleaseNull(cert_data
);
947 if (cert_data
!= NULL
) {
948 CFDictionarySetValue(output
, kSecAttrIdentityCertificateData
, cert_data
);
950 CFDictionaryRemoveValue(output
, kSecAttrIdentityCertificateData
);
957 require_quiet(SecTokenItemCreateFromAttributes(output
, auth_params
, token
, object_id
, &ref
, error
), out
);
958 if (!(wants_attributes
|| wants_data
|| wants_persistent_ref
)) {
959 CFAssignRetained(*result
, ref
);
960 } else if (ref
!= NULL
) {
961 CFDictionarySetValue(output
, kSecValueRef
, ref
);
964 // We could have stored data value previously to make ref creation succeed.
965 // They are not needed any more and in case that caller did not want the data, avoid returning them.
966 CFDictionaryRemoveValue(output
, kSecValueData
);
971 if (wants_attributes
) {
972 // Convert serialized form of access control to object form.
974 CFRetainAssign(ac_data
, CFDictionaryGetValue(output
, kSecAttrAccessControl
));
977 if (ac_data
!= NULL
) {
978 SecAccessControlRef ac
;
979 require_quiet(ac
= SecAccessControlCreateFromData(kCFAllocatorDefault
, ac_data
, error
), out
);
980 CFDictionarySetValue(output
, kSecAttrAccessControl
, ac
);
991 CFReleaseSafe(cert_object_id
);
992 CFReleaseSafe(cert_data
);
993 CFReleaseSafe(ac_data
);
994 CFReleaseSafe(value
);
995 CFReleaseSafe(persistent_ref
);
996 CFReleaseSafe(object_id
);
997 CFReleaseSafe(attrs
);
998 CFReleaseSafe(token
);
999 CFReleaseSafe(cert_token
);
1003 static bool SecItemResultProcess(CFDictionaryRef query
, CFDictionaryRef auth_params
, TKTokenRef token
,
1004 CFTypeRef raw_result
, CFTypeRef
*result
, CFErrorRef
*error
) {
1006 require_action_quiet(raw_result
!= NULL
, out
, ok
= true);
1007 require_action_quiet(result
!= NULL
, out
, ok
= true);
1009 if (CFGetTypeID(raw_result
) == CFArrayGetTypeID()) {
1010 CFIndex i
, count
= CFArrayGetCount(raw_result
);
1011 *result
= CFArrayCreateMutableForCFTypes(NULL
);
1012 for (i
= 0; i
< count
; i
++) {
1014 require_quiet(SecItemResultCopyPrepared(CFArrayGetValueAtIndex(raw_result
, i
),
1015 token
, query
, auth_params
, &ref
, error
), out
);
1017 CFArrayAppendValue((CFMutableArrayRef
)*result
, ref
);
1022 require_quiet(SecItemResultCopyPrepared(raw_result
, token
, query
, auth_params
, result
, error
), out
);
1031 CFDataRef
SecItemAttributesCopyPreparedAuthContext(CFTypeRef la_context
, CFErrorRef
*error
) {
1032 void *la_lib
= NULL
;
1033 CFDataRef acm_context
= NULL
;
1034 require_action_quiet(la_lib
= dlopen("/System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication", RTLD_LAZY
), out
,
1035 SecError(errSecInternal
, error
, CFSTR("failed to open LocalAuthentication.framework")));
1036 LAFunctionCopyExternalizedContext fnCopyExternalizedContext
= NULL
;
1037 require_action_quiet(fnCopyExternalizedContext
= dlsym(la_lib
, "LACopyExternalizedContext"), out
,
1038 SecError(errSecInternal
, error
, CFSTR("failed to obtain LACopyExternalizedContext")));
1039 require_action_quiet(acm_context
= fnCopyExternalizedContext(la_context
), out
,
1040 SecError(errSecInternal
, error
, CFSTR("failed to get ACM handle from LAContext")));
1042 if (la_lib
!= NULL
) {
1048 static bool SecItemAttributesPrepare(SecCFDictionaryCOW
*attrs
, bool forQuery
, CFErrorRef
*error
) {
1050 CFDataRef ac_data
= NULL
, acm_context
= NULL
;
1051 void *la_lib
= NULL
;
1053 SecAccessControlRef access_control
= (SecAccessControlRef
)CFDictionaryGetValue(attrs
->dictionary
, kSecAttrAccessControl
);
1054 if (access_control
!= NULL
) {
1055 require_quiet(ac_data
= SecAccessControlCopyData(access_control
), out
);
1056 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecAttrAccessControl
, ac_data
);
1059 const CFTypeRef la_context
= CFDictionaryGetValue(attrs
->dictionary
, kSecUseAuthenticationContext
);
1061 require_action_quiet(!CFDictionaryContainsKey(attrs
->dictionary
, kSecUseCredentialReference
), out
,
1062 SecError(errSecParam
, error
, CFSTR("kSecUseAuthenticationContext cannot be used together with kSecUseCredentialReference")));
1063 require_quiet(acm_context
= SecItemAttributesCopyPreparedAuthContext(la_context
, error
), out
);
1064 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs
), kSecUseAuthenticationContext
);
1065 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecUseCredentialReference
, acm_context
);
1068 // If a ref was specified we get its attribute dictionary and parse it.
1069 CFTypeRef value
= CFDictionaryGetValue(attrs
->dictionary
, kSecValueRef
);
1071 CFDictionaryRef ref_attributes
;
1072 require_action_quiet(ref_attributes
= SecItemCopyAttributeDictionary(value
, forQuery
), out
,
1073 SecError(errSecValueRefUnsupported
, error
, CFSTR("unsupported kSecValueRef in query")));
1075 /* Replace any attributes we already got from the ref with the ones
1076 from the attributes dictionary the caller passed us. This allows
1077 a caller to add an item using attributes from the ref and still
1078 override some of them in the dictionary directly. */
1079 CFMutableDictionaryRef new_query
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, ref_attributes
);
1080 CFRelease(ref_attributes
);
1081 CFDictionaryForEach(attrs
->dictionary
, ^(const void *key
, const void *value
) {
1082 if (!CFEqual(key
, kSecValueRef
))
1083 CFDictionarySetValue(new_query
, key
, value
);
1085 CFAssignRetained(attrs
->mutable_dictionary
, new_query
);
1086 attrs
->dictionary
= attrs
->mutable_dictionary
;
1089 CFTypeRef policy
= CFDictionaryGetValue(attrs
->dictionary
, kSecMatchPolicy
);
1091 require_action_quiet(CFGetTypeID(policy
) == SecPolicyGetTypeID(), out
,
1092 SecError(errSecParam
, error
, CFSTR("unsupported kSecMatchPolicy in query")));
1094 CFTypeRef values
[] = { policy
};
1095 CFArrayRef policiesArray
= CFArrayCreate(kCFAllocatorDefault
, values
, 1, &kCFTypeArrayCallBacks
);
1096 xpc_object_t policiesArrayXPC
= SecPolicyArrayCopyXPCArray(policiesArray
, error
);
1097 CFReleaseSafe(policiesArray
);
1098 require_action_quiet(policiesArrayXPC
, out
,
1099 SecError(errSecInternal
, error
, CFSTR("Failed to copy XPC policy")));
1101 CFTypeRef objectReadyForXPC
= _CFXPCCreateCFObjectFromXPCObject(policiesArrayXPC
);
1102 xpc_release(policiesArrayXPC
);
1103 require_action_quiet(objectReadyForXPC
, out
,
1104 SecError(errSecInternal
, error
, CFSTR("Failed to create CFObject from XPC policy")));
1106 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecMatchPolicy
, objectReadyForXPC
);
1107 CFRelease(objectReadyForXPC
);
1109 value
= CFDictionaryGetValue(attrs
->dictionary
, kSecAttrIssuer
);
1111 /* convert DN to canonical issuer, if value is DN (top level sequence) */
1112 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(value
), CFDataGetLength(value
) };
1113 DERDecodedInfo content
;
1114 if (!DERDecodeItem(&name
, &content
) &&
1115 (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1117 CFDataRef canonical_issuer
= createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1118 if (canonical_issuer
) {
1119 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecAttrIssuer
, canonical_issuer
);
1120 CFRelease(canonical_issuer
);
1128 if (la_lib
!= NULL
) {
1131 CFReleaseSafe(ac_data
);
1132 CFReleaseSafe(acm_context
);
1136 static bool SecItemAuthMaxAttemptsReached(CFArrayRef ac_pairs
, CFErrorRef
*error
)
1138 CFMutableStringRef log_string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1140 CFArrayForEachC(ac_pairs
, ac_pair
) {
1141 CFStringRef acl_hex_string
= CFDataCopyHexString(CFArrayGetValueAtIndex(ac_pair
, 0));
1142 CFStringRef str
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("operation: %@ acl:%@\n"), CFArrayGetValueAtIndex(ac_pair
, 1), acl_hex_string
);
1143 CFStringAppend(log_string
, str
);
1144 CFRelease(acl_hex_string
);
1148 CFStringRef reason
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("Reached maximum count of authentication attempts\n %@"), log_string
);
1149 SecError(errSecAuthFailed
, error
, reason
);
1150 __security_simulatecrash(reason
, __sec_exception_code_AuthLoop
);
1153 CFRelease(log_string
);
1157 bool SecItemAuthDo(SecCFDictionaryCOW
*auth_params
, CFErrorRef
*error
, SecItemAuthResult (^perform
)(CFDictionaryRef auth_params
, CFArrayRef
*ac_pairs
, CFErrorRef
*error
)) {
1159 CFArrayRef ac_pairs
= NULL
;
1160 SecCFDictionaryCOW auth_options
= { NULL
};
1161 // We need to create shared LAContext for Mail to reduce popups with Auth UI.
1162 // This app-hack will be removed by:<rdar://problem/28305552>
1163 // Similar workaround is for Safari, will be removed by fixing <rdar://problem/29683072>
1164 static CFTypeRef sharedLAContext
= NULL
;
1165 static CFDataRef sharedACMContext
= NULL
;
1166 static dispatch_once_t onceToken
;
1167 dispatch_once(&onceToken
, ^{
1168 CFBundleRef bundle
= CFBundleGetMainBundle();
1169 CFStringRef bundleName
= (bundle
!= NULL
) ? CFBundleGetIdentifier(bundle
) : NULL
;
1170 if (CFEqualSafe(bundleName
, CFSTR("com.apple.mail")) ||
1171 CFEqualSafe(bundleName
, CFSTR("com.apple.WebKit.Networking"))) {
1172 sharedLAContext
= LACreateNewContextWithACMContext(NULL
, error
);
1173 sharedACMContext
= (sharedLAContext
!= NULL
) ? LACopyACMContext(sharedLAContext
, error
) : NULL
;
1176 if (sharedLAContext
&& sharedACMContext
&&
1177 (auth_params
->dictionary
== NULL
|| (CFDictionaryGetValue(auth_params
->dictionary
, kSecUseAuthenticationContext
) == NULL
&&
1178 CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCredentialReference
) == NULL
))) {
1179 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationContext
, sharedLAContext
);
1180 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, sharedACMContext
);
1183 for (uint32_t i
= 0;; ++i
) {
1184 // If the operation succeeded or failed with other than auth-needed error, just leave.
1185 SecItemAuthResult auth_result
= perform(auth_params
->dictionary
, &ac_pairs
, error
);
1186 require_quiet(auth_result
!= kSecItemAuthResultError
, out
);
1187 require_action_quiet(auth_result
== kSecItemAuthResultNeedAuth
, out
, ok
= true);
1189 // If auth_params were not created up to now, do create them because we will definitely need them.
1190 SecCFDictionaryCOWGetMutable(auth_params
);
1192 // Retrieve or create authentication handle and/or ACM context.
1193 CFTypeRef auth_handle
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseAuthenticationContext
);
1194 if (auth_handle
== NULL
) {
1195 CFDataRef acm_context
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCredentialReference
);
1196 require_quiet(auth_handle
= LACreateNewContextWithACMContext(acm_context
, error
), out
);
1197 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationContext
, auth_handle
);
1198 CFRelease(auth_handle
);
1199 if (acm_context
== NULL
) {
1200 require_quiet(acm_context
= LACopyACMContext(auth_handle
, error
), out
);
1201 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, acm_context
);
1202 CFRelease(acm_context
);
1206 // Throttle max authentication attempts. This is mainly protection against exceptional states, not ordinary
1207 // user retry limit.
1208 require_action(i
< 20, out
, SecItemAuthMaxAttemptsReached(ac_pairs
, error
));
1210 // Prepare auth options dictionary.
1211 if (auth_options
.dictionary
== NULL
) {
1212 CFStringRef operation_prompt
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseOperationPrompt
);
1213 if (operation_prompt
!= NULL
) {
1214 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionAuthenticationReason
);
1215 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, operation_prompt
);
1219 CFStringRef caller_name
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCallerName
);
1220 if (caller_name
!= NULL
) {
1221 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionCallerName
);
1222 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, caller_name
);
1226 CFTypeRef auth_ui
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseAuthenticationUI
);
1227 if (CFEqualSafe(auth_ui
, kSecUseAuthenticationUIFail
)) {
1228 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionNotInteractive
);
1229 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, kCFBooleanTrue
);
1234 // Go through all access_control-operation pairs and evaluate them.
1236 CFArrayForEachC(ac_pairs
, ac_pair
) {
1237 CFDataRef updated_acl
= NULL
;
1238 require_quiet(LAEvaluateAndUpdateACL(auth_handle
,
1239 CFArrayGetValueAtIndex(ac_pair
, 0), CFArrayGetValueAtIndex(ac_pair
, 1),
1240 auth_options
.dictionary
, &updated_acl
, error
), out
);
1242 if (updated_acl
|| CFEqual(CFArrayGetValueAtIndex(ac_pair
, 1), CFSTR(""))) {
1243 // we assume that only one ACL can be modified during ItemAdd or ItemUpdate
1244 SecAccessControlRef ac
= NULL
;
1245 require(ac
= SecAccessControlCreateFromData(kCFAllocatorDefault
,
1246 updated_acl
? updated_acl
: CFArrayGetValueAtIndex(ac_pair
, 0), error
), out
);
1247 SecAccessControlSetBound(ac
, true);
1248 CFAssignRetained(updated_acl
, SecAccessControlCopyData(ac
));
1249 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecAttrAccessControl
, updated_acl
);
1250 CFRelease(updated_acl
);
1259 CFReleaseSafe(auth_options
.mutable_dictionary
);
1260 CFReleaseSafe(ac_pairs
);
1264 void SecItemAuthCopyParams(SecCFDictionaryCOW
*auth_params
, SecCFDictionaryCOW
*query
) {
1265 // Store operation prompt.
1266 CFStringRef operation_prompt
= CFDictionaryGetValue(query
->dictionary
, kSecUseOperationPrompt
);
1267 if (operation_prompt
!= NULL
) {
1268 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseOperationPrompt
, operation_prompt
);
1269 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseOperationPrompt
);
1272 // Store caller name.
1273 CFStringRef caller_name
= CFDictionaryGetValue(query
->dictionary
, kSecUseCallerName
);
1274 if (caller_name
!= NULL
) {
1275 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCallerName
, caller_name
);
1276 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseCallerName
);
1279 // Find out whether we are allowed to pop up a UI.
1280 CFTypeRef auth_ui
= CFDictionaryGetValue(query
->dictionary
, kSecUseAuthenticationUI
) ?:
1281 (CFEqualSafe(CFDictionaryGetValue(query
->dictionary
, kSecUseNoAuthenticationUI
), kCFBooleanTrue
) ?
1282 kSecUseAuthenticationUIFail
: kSecUseAuthenticationUIAllow
);
1283 if (!CFEqual(auth_ui
, kSecUseAuthenticationUISkip
) || CFDictionaryGetValue(query
->dictionary
, kSecUseNoAuthenticationUI
)) {
1284 if (CFDictionaryContainsKey(query
->dictionary
, kSecUseNoAuthenticationUI
))
1285 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseNoAuthenticationUI
);
1286 if (!CFEqualSafe(auth_ui
, kSecUseAuthenticationUISkip
) && CFDictionaryContainsKey(query
->dictionary
, kSecUseAuthenticationUI
))
1287 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseAuthenticationUI
);
1290 if (!CFEqual(auth_ui
, kSecUseAuthenticationUIAllow
)) {
1291 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationUI
, auth_ui
);
1294 CFDataRef acm_context
= CFDictionaryGetValue(query
->dictionary
, kSecUseCredentialReference
);
1295 if (acm_context
!= NULL
) {
1296 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, acm_context
);
1300 static SecItemAuthResult
SecItemCreatePairsFromError(CFErrorRef
*error
, CFArrayRef
*ac_pairs
)
1302 if (error
&& *error
&& CFErrorGetCode(*error
) == errSecAuthNeeded
&& CFEqualSafe(CFErrorGetDomain(*error
), kSecErrorDomain
)) {
1303 // Extract ACLs to be verified from the error.
1304 CFDictionaryRef user_info
= CFErrorCopyUserInfo(*error
);
1305 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, errSecAuthNeeded
);
1306 CFRetainAssign(*ac_pairs
, CFDictionaryGetValue(user_info
, key
));
1307 if (*ac_pairs
== NULL
)
1308 CFAssignRetained(*ac_pairs
, CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
));
1311 CFRelease(user_info
);
1312 CFReleaseNull(*error
);
1313 return kSecItemAuthResultNeedAuth
;
1315 return kSecItemAuthResultError
;
1318 // Wrapper to handle automatic authentication and token/secd case switching.
1319 static bool SecItemAuthDoQuery(SecCFDictionaryCOW
*query
, SecCFDictionaryCOW
*attributes
, const void *secItemOperation
, CFErrorRef
*error
,
1320 bool (^perform
)(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
)) {
1322 SecCFDictionaryCOW auth_params
= { NULL
};
1323 SecAccessControlRef access_control
= NULL
;
1324 __block TKTokenRef token
= NULL
;
1326 if (secItemOperation
== SecItemAdd
|| secItemOperation
== SecItemUpdate
) {
1327 CFDictionaryRef dict
= attributes
? attributes
->dictionary
: query
->dictionary
;
1328 access_control
= (SecAccessControlRef
)CFDictionaryGetValue(dict
, kSecAttrAccessControl
);
1329 if (access_control
&& SecAccessControlGetConstraints(access_control
) &&
1330 CFEqualSafe(CFDictionaryGetValue(dict
, kSecAttrSynchronizable
), kCFBooleanTrue
))
1331 require_quiet(SecError(errSecParam
, error
, CFSTR("item with kSecAttrAccessControl is not synchronizable")), out
);
1334 // Perform initial surgery on query/attributes (resolve LAContext to serialized ACM handle, resolve
1335 // SecAccessControlRef to serialized forms, expand kSecValueRef etc.)
1337 secItemOperation
== SecItemCopyMatching
||
1338 secItemOperation
== SecItemUpdate
||
1339 secItemOperation
== SecItemDelete
;
1341 require_quiet(SecItemAttributesPrepare(query
, forQuery
, error
), out
);
1342 if (attributes
!= NULL
)
1343 require_quiet(SecItemAttributesPrepare(attributes
, false, error
), out
);
1345 // Populate auth_params dictionary according to initial query contents.
1346 SecItemAuthCopyParams(&auth_params
, query
);
1348 if (secItemOperation
!= SecItemCopyMatching
) {
1349 // UISkip is allowed only for CopyMatching.
1350 require_action_quiet(!CFEqualSafe(CFDictionaryGetValue(query
->dictionary
, kSecUseAuthenticationUI
), kSecUseAuthenticationUISkip
), out
,
1351 SecError(errSecParam
, error
,
1352 CFSTR("kSecUseAuthenticationUISkip is allowed only for SecItemCopyMatching")));
1355 ok
= SecItemAuthDo(&auth_params
, error
, ^SecItemAuthResult(CFDictionaryRef auth_params
, CFArrayRef
*ac_pairs
, CFErrorRef
*error
) {
1356 SecItemAuthResult result
= kSecItemAuthResultError
;
1358 // Propagate actual credential reference to the query.
1359 if (auth_params
!= NULL
) {
1360 CFDataRef acm_context
= CFDictionaryGetValue(auth_params
, kSecUseCredentialReference
);
1361 if (acm_context
!= NULL
) {
1362 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query
), kSecUseCredentialReference
, acm_context
);
1365 CFDataRef acl_data_ref
= CFDictionaryGetValue(auth_params
, kSecAttrAccessControl
);
1366 if (acl_data_ref
!= NULL
) {
1367 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes
?: query
), kSecAttrAccessControl
, acl_data_ref
);
1371 // Prepare connection to target token if it is present.
1372 CFStringRef token_id
= CFDictionaryGetValue(query
->dictionary
, kSecAttrTokenID
);
1373 if (secItemOperation
!= SecItemCopyMatching
&& token_id
!= NULL
) {
1374 require_quiet(CFAssignRetained(token
, SecTokenCreate(token_id
, auth_params
, error
)), out
);
1377 CFDictionaryRef attrs
= (attributes
!= NULL
) ? attributes
->dictionary
: NULL
;
1378 if(!perform(token
, query
->dictionary
, attrs
, auth_params
, error
)) {
1379 require_quiet((result
= SecItemCreatePairsFromError(error
, ac_pairs
)) == kSecItemAuthResultOK
, out
);
1382 result
= kSecItemAuthResultOK
;
1387 require_quiet(ok
, out
);
1392 CFReleaseSafe(token
);
1393 CFReleaseSafe(auth_params
.mutable_dictionary
);
1397 static bool cftype_to_bool_cftype_error_request(enum SecXPCOperation op
, CFTypeRef attributes
, CFTypeRef
*result
, CFErrorRef
*error
)
1399 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1400 return SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, attributes
, error
);
1401 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1403 return SecXPCDictionaryCopyPListOptional(response
, kSecXPCKeyResult
, result
, error
);
1409 static CFArrayRef
dict_to_array_error_request(enum SecXPCOperation op
, CFDictionaryRef attributes
, CFErrorRef
*error
)
1411 CFArrayRef result
= NULL
;
1412 bool success
= cftype_to_bool_cftype_error_request(op
, attributes
, (CFTypeRef
*)&result
, error
);
1413 if(success
&& !isArray(result
)){
1414 SecError(errSecUnimplemented
, error
, CFSTR("Unexpected nonarray returned: %@"), result
);
1415 CFReleaseNull(result
);
1420 bool cftype_client_to_bool_cftype_error_request(enum SecXPCOperation op
, CFTypeRef attributes
, __unused SecurityClient
*client
, CFTypeRef
*result
, CFErrorRef
*error
) {
1421 return cftype_to_bool_cftype_error_request(op
, attributes
, result
, error
);
1424 static bool dict_to_error_request(enum SecXPCOperation op
, CFDictionaryRef query
, CFErrorRef
*error
)
1426 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1427 return SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, query
, error
);
1431 static bool cfstring_array_to_error_request(enum SecXPCOperation op
, CFStringRef string
, CFArrayRef attributes
, __unused SecurityClient
*client
, CFErrorRef
*error
)
1433 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1435 if (!SecXPCDictionarySetString(message
, kSecXPCKeyString
, string
, error
))
1440 if (!SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, attributes
, error
))
1448 static bool dict_client_to_error_request(enum SecXPCOperation op
, CFDictionaryRef query
, __unused SecurityClient
*client
, CFErrorRef
*error
)
1450 return dict_to_error_request(op
, query
, error
);
1453 static bool SecTokenCreateAccessControlError(CFStringRef operation
, CFDataRef access_control
, CFErrorRef
*error
) {
1454 CFArrayRef ac_pair
= CFArrayCreateForCFTypes(NULL
, access_control
, operation
, NULL
);
1455 const void *ac_pairs
[] = { CFArrayCreateForCFTypes(NULL
, ac_pair
, NULL
) };
1456 const void *keys
[] = { CFNumberCreateWithCFIndex(NULL
, errSecAuthNeeded
) };
1457 CFAssignRetained(*error
, CFErrorCreateWithUserInfoKeysAndValues(NULL
, kSecErrorDomain
, errSecAuthNeeded
,
1458 keys
, ac_pairs
, 1));
1460 CFRelease(ac_pairs
[0]);
1465 static bool SecTokenProcessError(CFStringRef operation
, TKTokenRef token
, CFTypeRef object_or_attrs
, CFErrorRef
*error
) {
1466 if (CFEqualSafe(CFErrorGetDomain(*error
), CFSTR(kTKErrorDomain
)) &&
1467 CFErrorGetCode(*error
) == kTKErrorCodeAuthenticationFailed
) {
1468 // Replace error with the one which is augmented with access control and operation which failed,
1469 // which will cause SecItemDoWithAuth to throw UI.
1470 // Create array containing tuple (array) with error and requested operation.
1471 CFDataRef access_control
= (CFGetTypeID(object_or_attrs
) == CFDataGetTypeID()) ?
1472 TKTokenCopyObjectAccessControl(token
, object_or_attrs
, error
) :
1473 TKTokenCopyObjectCreationAccessControl(token
, object_or_attrs
, error
);
1474 if (access_control
!= NULL
) {
1475 SecTokenCreateAccessControlError(operation
, access_control
, error
);
1476 CFRelease(access_control
);
1482 static CFTypeRef
SecTokenCopyUpdatedObjectID(TKTokenRef token
, CFDataRef object_id
, CFMutableDictionaryRef attributes
, CFErrorRef
*error
) {
1483 CFDataRef access_control
= NULL
, db_value
= NULL
, new_object_id
= NULL
;
1484 SecAccessControlRef ac
= NULL
;
1486 // Make sure that ACL is bound - if not, generate an error which will trigger binding.
1487 CFDataRef ac_data
= CFDictionaryGetValue(attributes
, kSecAttrAccessControl
);
1488 if (ac_data
!= NULL
) {
1489 require_quiet(ac
= SecAccessControlCreateFromData(NULL
, ac_data
, error
), out
);
1490 require_action_quiet(SecAccessControlIsBound(ac
), out
,
1491 SecTokenCreateAccessControlError(CFSTR(""), ac_data
, error
));
1494 // Create or update the object on the token.
1495 require_action_quiet(new_object_id
= TKTokenCreateOrUpdateObject(token
, object_id
, attributes
, error
), out
,
1496 SecTokenProcessError(kAKSKeyOpEncrypt
, token
, object_id
?: (CFTypeRef
)attributes
, error
));
1498 // Prepare kSecValueData field for the item to be stored into the keychain DB.
1499 require_quiet(access_control
= TKTokenCopyObjectAccessControl(token
, new_object_id
, error
), out
);
1500 require_quiet(db_value
= SecTokenItemValueCreate(new_object_id
, access_control
,
1501 CFDictionaryGetValue(attributes
, kSecValueData
), error
), out
);
1502 CFDictionarySetValue(attributes
, kSecValueData
, db_value
);
1504 // kSecAttrAccessControl is handled directly by the token and stored inside data field.
1505 CFDictionaryRemoveValue(attributes
, kSecAttrAccessControl
);
1509 CFReleaseSafe(access_control
);
1510 CFReleaseSafe(db_value
);
1511 return new_object_id
;
1514 static bool SecTokenItemAdd(TKTokenRef token
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
,
1515 CFTypeRef
*result
, CFErrorRef
*error
) {
1517 CFTypeRef object_id
= NULL
, ref
= NULL
;
1518 CFDictionaryRef ref_attrs
= NULL
;
1519 CFTypeRef db_result
= NULL
;
1521 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutableCopy(NULL
, 0, attributes
);
1522 require_quiet(object_id
= SecTokenCopyUpdatedObjectID(token
, NULL
, attrs
, error
), out
);
1524 // Augment attributes from default attributes of the related ref (SecKeyRef, SecCertificateRef). This is best done
1525 // by creating ref and getting back its attributes.
1526 require_quiet(SecTokenItemCreateFromAttributes(attrs
, auth_params
, token
, object_id
, &ref
, error
), out
);
1528 if ((ref_attrs
= SecItemCopyAttributeDictionary(ref
, false)) != NULL
) {
1529 CFDictionaryForEach(ref_attrs
, ^(const void *key
, const void *value
) {
1530 if (!CFEqual(key
, kSecValueData
)) {
1531 CFDictionaryAddValue(attrs
, key
, value
);
1537 // Make sure that both attributes and data are returned.
1538 CFDictionarySetValue(attrs
, kSecReturnAttributes
, kCFBooleanTrue
);
1539 CFDictionarySetValue(attrs
, kSecReturnData
, kCFBooleanTrue
);
1541 if (!CFEqualSafe(CFDictionaryGetValue(attrs
, kSecAttrIsPermanent
), kCFBooleanFalse
)) {
1542 // IsPermanent is not present or is true, so add item to the db.
1543 require_quiet(SECURITYD_XPC(sec_item_add
, cftype_client_to_bool_cftype_error_request
, attrs
,
1544 SecSecurityClientGet(), &db_result
, error
), out
);
1546 // Process directly result of token call.
1547 db_result
= CFRetain(attrs
);
1549 require_quiet(SecItemResultProcess(attributes
, auth_params
, token
, db_result
, result
, error
), out
);
1554 CFReleaseSafe(db_result
);
1555 CFReleaseSafe(attrs
);
1556 CFReleaseSafe(ref_attrs
);
1557 CFReleaseSafe(object_id
);
1562 OSStatus
SecItemAdd(CFDictionaryRef attributes
, CFTypeRef
*result
) {
1563 __block SecCFDictionaryCOW attrs
= { attributes
};
1566 os_activity_t trace_activity
= os_activity_start("SecItemAdd_ios", OS_ACTIVITY_FLAG_DEFAULT
);
1568 require_quiet(!explode_identity(attrs
.dictionary
, (secitem_operation
)SecItemAdd
, &status
, result
), errOut
);
1569 infer_cert_label(&attrs
);
1571 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1572 return SecItemAuthDoQuery(&attrs
, NULL
, SecItemAdd
, error
, ^bool(TKTokenRef token
, CFDictionaryRef attributes
, CFDictionaryRef unused
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1573 if (token
== NULL
) {
1574 CFTypeRef raw_result
= NULL
;
1575 if (!SECURITYD_XPC(sec_item_add
, cftype_client_to_bool_cftype_error_request
, attributes
, SecSecurityClientGet(), &raw_result
, error
))
1578 bool ok
= SecItemResultProcess(attributes
, auth_params
, token
, raw_result
, result
, error
);
1579 CFReleaseSafe(raw_result
);
1582 // Send request to an appropriate token instead of secd.
1583 return SecTokenItemAdd(token
, attributes
, auth_params
, result
, error
);
1589 CFReleaseSafe(attrs
.mutable_dictionary
);
1591 os_activity_end(trace_activity
);
1596 OSStatus
SecItemCopyMatching(CFDictionaryRef inQuery
, CFTypeRef
*result
) {
1598 __block SecCFDictionaryCOW query
= { inQuery
};
1600 os_activity_t trace_activity
= os_activity_start("SecItemCopyMatching_ios", OS_ACTIVITY_FLAG_DEFAULT
);
1602 require_quiet(!explode_identity(query
.dictionary
, (secitem_operation
)SecItemCopyMatching
, &status
, result
), errOut
);
1604 bool wants_data
= cf_bool_value(CFDictionaryGetValue(query
.dictionary
, kSecReturnData
));
1605 bool wants_attributes
= cf_bool_value(CFDictionaryGetValue(query
.dictionary
, kSecReturnAttributes
));
1606 if ((wants_data
&& !wants_attributes
) || (!wants_data
&& wants_attributes
)) {
1607 // When either attributes or data are requested, we need to query both, because for token based items,
1608 // both are needed in order to generate proper data and/or attributes results.
1609 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query
), kSecReturnAttributes
, kCFBooleanTrue
);
1610 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query
), kSecReturnData
, kCFBooleanTrue
);
1613 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1614 return SecItemAuthDoQuery(&query
, NULL
, SecItemCopyMatching
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1615 CFTypeRef raw_result
= NULL
;
1616 if (!SECURITYD_XPC(sec_item_copy_matching
, cftype_client_to_bool_cftype_error_request
, query
, SecSecurityClientGet(), &raw_result
, error
))
1619 // We intentionally pass NULL as token argument, because we want to be able to decide about token on which the item lives
1620 // on per-record basis, not wholesale. Logic inside SecItemResultCopyPrepared will open proper token according
1621 // to currently processed item.
1622 bool ok
= SecItemResultProcess(inQuery
, auth_params
, NULL
, raw_result
, result
, error
);
1623 CFReleaseSafe(raw_result
);
1630 CFReleaseSafe(query
.mutable_dictionary
);
1631 os_activity_end(trace_activity
);
1635 // Invokes token-object handler for each item matching specified query.
1636 static bool SecTokenItemForEachMatching(CFDictionaryRef query
, CFErrorRef
*error
,
1637 bool (^perform
)(CFDictionaryRef item_value
, CFDictionaryRef item_query
,
1638 CFErrorRef
*error
)) {
1640 CFMutableDictionaryRef list_query
= NULL
;
1641 CFTypeRef items
= NULL
;
1642 CFArrayRef ref_array
= NULL
;
1643 CFDictionaryRef item_query
= NULL
, item_data
= NULL
;
1645 // Query all items with data and persistent_ref, so that we can extract objectIDs and also identify originating
1646 // items in the keychain.
1647 list_query
= CFDictionaryCreateMutableCopy(NULL
, 0, query
);
1648 if (CFDictionaryGetValue(list_query
, kSecMatchLimit
) == NULL
) {
1649 CFDictionarySetValue(list_query
, kSecMatchLimit
, kSecMatchLimitAll
);
1651 CFDictionarySetValue(list_query
, kSecReturnData
, kCFBooleanTrue
);
1652 CFDictionarySetValue(list_query
, kSecReturnPersistentRef
, kCFBooleanTrue
);
1653 require_quiet(SECURITYD_XPC(sec_item_copy_matching
, cftype_client_to_bool_cftype_error_request
, list_query
,
1654 SecSecurityClientGet(), &items
, error
), out
);
1655 if (CFGetTypeID(items
) != CFArrayGetTypeID()) {
1656 // Wrap single returned item into the array.
1657 CFArrayRef item_array
= CFArrayCreateForCFTypes(NULL
, items
, NULL
);
1658 CFAssignRetained(items
, item_array
);
1662 CFArrayForEachC(items
, item
) {
1663 CFDataRef data
= CFDictionaryGetValue(item
, kSecValueData
);
1664 require_action_quiet(data
!= NULL
, out
, SecError(errSecInternal
, error
, CFSTR("value not present for token item")));
1666 CFAssignRetained(item_data
, SecTokenItemValueCopy(data
, error
));
1667 require_quiet(item_data
, out
);
1669 CFAssignRetained(item_query
,
1670 CFDictionaryCreateForCFTypes(NULL
,
1671 kSecValuePersistentRef
, CFDictionaryGetValue(item
, kSecValuePersistentRef
),
1673 require_quiet(perform(item_data
, item_query
, error
), out
);
1679 CFReleaseSafe(list_query
);
1680 CFReleaseSafe(items
);
1681 CFReleaseSafe(item_data
);
1682 CFReleaseSafe(ref_array
);
1683 CFReleaseSafe(item_query
);
1687 static bool SecItemRawUpdate(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
, CFErrorRef
*error
) {
1690 // Ensure the dictionary passed to securityd has proper kCFTypeDictionaryKeyCallBacks.
1691 CFMutableDictionaryRef tmp
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
1692 CFDictionaryForEach(attributesToUpdate
, ^(const void *key
, const void *value
) { CFDictionaryAddValue(tmp
, key
, value
); });
1693 ok
= gSecurityd
->sec_item_update(query
, tmp
, SecSecurityClientGet(), error
);
1696 xpc_object_t message
= securityd_create_message(sec_item_update_id
, error
);
1698 if (SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, query
, error
) &&
1699 SecXPCDictionarySetPList(message
, kSecXPCKeyAttributesToUpdate
, attributesToUpdate
, error
)) {
1700 xpc_object_t reply
= securityd_message_with_reply_sync(message
, error
);
1702 ok
= securityd_message_no_error(reply
, error
);
1706 xpc_release(message
);
1712 static bool SecTokenItemUpdate(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
, CFErrorRef
*error
) {
1713 return SecTokenItemForEachMatching(query
, error
, ^bool(CFDictionaryRef object_data
, CFDictionaryRef item_query
, CFErrorRef
*error
) {
1715 CFDataRef object_id
= NULL
;
1716 CFMutableDictionaryRef db_value
= NULL
;
1718 // Update attributes on the token.
1719 CFMutableDictionaryRef attributes
= CFDictionaryCreateMutableCopy(NULL
, 0, attributesToUpdate
);
1720 require_quiet(object_id
= SecTokenCopyUpdatedObjectID(token
, CFDictionaryGetValue(object_data
, kSecTokenValueObjectIDKey
),
1721 attributes
, error
), out
);
1723 // Update attributes in the database.
1724 require_quiet(SecItemRawUpdate(item_query
, attributes
, error
), out
);
1729 CFReleaseSafe(object_id
);
1730 CFReleaseSafe(attributes
);
1731 CFReleaseSafe(db_value
);
1736 OSStatus
SecItemUpdate(CFDictionaryRef inQuery
, CFDictionaryRef inAttributesToUpdate
) {
1737 return SecOSStatusWith(^bool(CFErrorRef
*error
) {
1738 return SecItemUpdateWithError(inQuery
, inAttributesToUpdate
, error
);
1743 SecItemUpdateWithError(CFDictionaryRef inQuery
,
1744 CFDictionaryRef inAttributesToUpdate
,
1747 __block SecCFDictionaryCOW query
= { inQuery
};
1748 __block SecCFDictionaryCOW attributesToUpdate
= { inAttributesToUpdate
};
1749 bool result
= false;
1751 if (handleUpdateIdentity(inQuery
, inAttributesToUpdate
, &result
, error
))
1754 result
= SecItemAuthDoQuery(&query
, &attributesToUpdate
, SecItemUpdate
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1755 if (token
== NULL
) {
1756 return SecItemRawUpdate(query
, attributes
, error
);
1758 return SecTokenItemUpdate(token
, query
, attributes
, error
);
1763 CFReleaseSafe(query
.mutable_dictionary
);
1764 CFReleaseSafe(attributesToUpdate
.mutable_dictionary
);
1768 static OSStatus
explode_persistent_identity_ref(SecCFDictionaryCOW
*query
)
1770 OSStatus status
= errSecSuccess
;
1771 CFTypeRef persist
= CFDictionaryGetValue(query
->dictionary
, kSecValuePersistentRef
);
1773 if (persist
&& _SecItemParsePersistentRef(persist
, &class, NULL
)
1774 && CFEqual(class, kSecClassIdentity
)) {
1775 const void *keys
[] = { kSecReturnRef
, kSecValuePersistentRef
};
1776 const void *vals
[] = { kCFBooleanTrue
, persist
};
1777 CFDictionaryRef persistent_query
= CFDictionaryCreate(NULL
, keys
,
1778 vals
, (array_size(keys
)), NULL
, NULL
);
1779 CFTypeRef item_query
= NULL
;
1780 status
= SecItemCopyMatching(persistent_query
, &item_query
);
1781 CFReleaseNull(persistent_query
);
1785 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecValuePersistentRef
);
1786 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query
), kSecValueRef
, item_query
);
1787 CFRelease(item_query
);
1793 OSStatus
SecItemDelete(CFDictionaryRef inQuery
) {
1795 __block SecCFDictionaryCOW query
= { inQuery
};
1797 os_activity_t trace_activity
= os_activity_start("SecItemDelete_ios", OS_ACTIVITY_FLAG_DEFAULT
);
1799 require_noerr_quiet(status
= explode_persistent_identity_ref(&query
), errOut
);
1800 require_quiet(!explode_identity(query
.dictionary
, (secitem_operation
)SecItemDelete
, &status
, NULL
), errOut
);
1802 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1803 return SecItemAuthDoQuery(&query
, NULL
, SecItemDelete
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1804 if (token
== NULL
) {
1805 return SECURITYD_XPC(sec_item_delete
, dict_client_to_error_request
, query
, SecSecurityClientGet(), error
);
1807 return SecTokenItemForEachMatching(query
, error
, ^bool(CFDictionaryRef object_data
, CFDictionaryRef item_query
, CFErrorRef
*error
) {
1810 // Delete item from the token.
1811 CFDataRef object_id
= CFDictionaryGetValue(object_data
, kSecTokenValueObjectIDKey
);
1812 require_action_quiet(TKTokenDeleteObject(token
, object_id
, error
), out
,
1813 SecTokenProcessError(kAKSKeyOpDelete
, token
, object_id
, error
));
1815 // Delete the item from the keychain.
1816 require_quiet(SECURITYD_XPC(sec_item_delete
, dict_client_to_error_request
, item_query
,
1817 SecSecurityClientGet(), error
), out
);
1828 CFReleaseSafe(query
.mutable_dictionary
);
1830 os_activity_end(trace_activity
);
1836 SecItemDeleteAll(void)
1838 return SecOSStatusWith(^bool (CFErrorRef
*error
) {
1841 #ifndef SECITEM_SHIM_OSX
1842 SecTrustStoreRef ts
= SecTrustStoreForDomain(kSecTrustStoreDomainUser
);
1843 if (!gSecurityd
->sec_truststore_remove_all(ts
, error
))
1844 ok
&= SecError(errSecInternal
, error
, CFSTR("sec_truststore_remove_all is NULL"));
1845 #endif // *** END SECITEM_SHIM_OSX ***
1846 if (!gSecurityd
->sec_item_delete_all(error
))
1847 ok
&= SecError(errSecInternal
, error
, CFSTR("sec_item_delete_all is NULL"));
1849 ok
&= securityd_send_sync_and_do(sec_delete_all_id
, error
, NULL
, NULL
);
1856 agrps_client_to_error_request(enum SecXPCOperation op
, CFArrayRef agrps
, __unused SecurityClient
*client
, CFErrorRef
*error
)
1858 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1859 return SecXPCDictionarySetPList(message
, kSecXPCKeyAccessGroups
, agrps
, error
);
1863 bool SecItemDeleteAllWithAccessGroups(CFArrayRef accessGroups
, CFErrorRef
*error
) {
1864 os_activity_t trace_activity
= os_activity_start("SecItemDeleteAllWithAccessGroups", OS_ACTIVITY_FLAG_DEFAULT
);
1866 bool ok
= SECURITYD_XPC(sec_delete_items_with_access_groups
, agrps_client_to_error_request
, accessGroups
,
1867 SecSecurityClientGet(), error
);
1869 os_activity_end(trace_activity
);
1874 #if SECITEM_SHIM_OSX
1875 SecItemUpdateTokenItems_ios(CFTypeRef tokenID
, CFArrayRef tokenItemsAttributes
)
1877 SecItemUpdateTokenItems(CFTypeRef tokenID
, CFArrayRef tokenItemsAttributes
)
1882 os_activity_t trace_activity
= os_activity_start("SecItemDelete_ios", OS_ACTIVITY_FLAG_DEFAULT
);
1884 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1885 CFArrayRef tmpArrayRef
= tokenItemsAttributes
;
1886 if (tokenItemsAttributes
) {
1887 CFMutableArrayRef tokenItems
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1888 for (CFIndex i
= 0; i
< CFArrayGetCount(tokenItemsAttributes
); ++i
) {
1889 CFDictionaryRef itemAttributes
= CFArrayGetValueAtIndex(tokenItemsAttributes
, i
);
1890 CFTypeRef accessControl
= CFDictionaryGetValue(itemAttributes
, kSecAttrAccessControl
);
1891 CFTypeRef tokenOID
= CFDictionaryGetValue(itemAttributes
, kSecAttrTokenOID
);
1892 CFTypeRef valueData
= CFDictionaryGetValue(itemAttributes
, kSecValueData
);
1893 if (tokenOID
!= NULL
&& accessControl
!= NULL
&& CFDataGetTypeID() == CFGetTypeID(accessControl
)) {
1894 CFDataRef data
= SecTokenItemValueCreate(tokenOID
, accessControl
, valueData
, error
);
1896 CFRelease(tokenItems
);
1900 CFMutableDictionaryRef attributes
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, itemAttributes
);
1901 CFDictionarySetValue(attributes
, kSecValueData
, data
);
1902 CFDictionarySetValue(attributes
, kSecAttrTokenID
, tokenID
);
1903 CFDictionaryRemoveValue(attributes
, kSecAttrAccessControl
);
1904 CFDictionaryRemoveValue(attributes
, kSecAttrTokenOID
);
1905 CFArrayAppendValue(tokenItems
, attributes
);
1906 CFRelease(attributes
);
1910 CFArrayAppendValue(tokenItems
, itemAttributes
);
1913 tmpArrayRef
= tokenItems
;
1916 return SECURITYD_XPC(sec_item_update_token_items
, cfstring_array_to_error_request
, tokenID
, tmpArrayRef
, SecSecurityClientGet(), error
);
1919 os_activity_end(trace_activity
);
1924 CFArrayRef
_SecKeychainSyncUpdateMessage(CFDictionaryRef updates
, CFErrorRef
*error
) {
1925 __block CFArrayRef result
;
1926 os_activity_initiate("_SecKeychainSyncUpdateMessage", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1927 result
= SECURITYD_XPC(sec_keychain_sync_update_message
, dict_to_array_error_request
, updates
, error
);
1932 #ifndef SECITEM_SHIM_OSX
1933 OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
);
1935 OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
)
1937 return -1; /* this is only on OS X currently */
1942 extern OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
);
1946 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
1948 bool _SecKeychainRollKeys(bool force
, CFErrorRef
*error
)
1950 do_if_registered(sec_roll_keys
, force
, error
);
1952 __block
bool result
= false;
1954 secdebug("secitem","enter - %s", __FUNCTION__
);
1955 securityd_send_sync_and_do(kSecXPCOpRollKeys
, error
,
1956 ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1957 xpc_dictionary_set_bool(message
, "force", force
);
1960 ^bool(xpc_object_t response
, __unused CFErrorRef
*error
) {
1961 result
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);