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 static CFTypeRef sharedLAContext
= NULL
;
1164 static CFDataRef sharedACMContext
= NULL
;
1165 static dispatch_once_t onceToken
;
1166 dispatch_once(&onceToken
, ^{
1167 CFBundleRef bundle
= CFBundleGetMainBundle();
1168 CFStringRef bundleName
= (bundle
!= NULL
)?CFBundleGetIdentifier(bundle
):NULL
;
1169 if (bundleName
&& CFEqual(bundleName
, CFSTR("com.apple.mail"))) {
1170 sharedLAContext
= LACreateNewContextWithACMContext(NULL
, error
);
1171 sharedACMContext
= (sharedLAContext
!= NULL
)?LACopyACMContext(sharedLAContext
, error
):NULL
;
1174 if (sharedLAContext
&& sharedACMContext
&&
1175 (auth_params
->dictionary
== NULL
|| (CFDictionaryGetValue(auth_params
->dictionary
, kSecUseAuthenticationContext
) == NULL
&&
1176 CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCredentialReference
) == NULL
))) {
1177 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationContext
, sharedLAContext
);
1178 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, sharedACMContext
);
1181 for (uint32_t i
= 0;; ++i
) {
1182 // If the operation succeeded or failed with other than auth-needed error, just leave.
1183 SecItemAuthResult auth_result
= perform(auth_params
->dictionary
, &ac_pairs
, error
);
1184 require_quiet(auth_result
!= kSecItemAuthResultError
, out
);
1185 require_action_quiet(auth_result
== kSecItemAuthResultNeedAuth
, out
, ok
= true);
1187 // If auth_params were not created up to now, do create them because we will definitely need them.
1188 SecCFDictionaryCOWGetMutable(auth_params
);
1190 // Retrieve or create authentication handle and/or ACM context.
1191 CFTypeRef auth_handle
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseAuthenticationContext
);
1192 if (auth_handle
== NULL
) {
1193 CFDataRef acm_context
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCredentialReference
);
1194 require_quiet(auth_handle
= LACreateNewContextWithACMContext(acm_context
, error
), out
);
1195 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationContext
, auth_handle
);
1196 CFRelease(auth_handle
);
1197 if (acm_context
== NULL
) {
1198 require_quiet(acm_context
= LACopyACMContext(auth_handle
, error
), out
);
1199 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, acm_context
);
1200 CFRelease(acm_context
);
1204 // Throttle max authentication attempts. This is mainly protection against exceptional states, not ordinary
1205 // user retry limit.
1206 require_action(i
< 20, out
, SecItemAuthMaxAttemptsReached(ac_pairs
, error
));
1208 // Prepare auth options dictionary.
1209 if (auth_options
.dictionary
== NULL
) {
1210 CFStringRef operation_prompt
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseOperationPrompt
);
1211 if (operation_prompt
!= NULL
) {
1212 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionAuthenticationReason
);
1213 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, operation_prompt
);
1217 CFStringRef caller_name
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCallerName
);
1218 if (caller_name
!= NULL
) {
1219 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionCallerName
);
1220 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, caller_name
);
1224 CFTypeRef auth_ui
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseAuthenticationUI
);
1225 if (CFEqualSafe(auth_ui
, kSecUseAuthenticationUIFail
)) {
1226 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionNotInteractive
);
1227 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, kCFBooleanTrue
);
1232 // Go through all access_control-operation pairs and evaluate them.
1234 CFArrayForEachC(ac_pairs
, ac_pair
) {
1235 CFDataRef updated_acl
= NULL
;
1236 require_quiet(LAEvaluateAndUpdateACL(auth_handle
,
1237 CFArrayGetValueAtIndex(ac_pair
, 0), CFArrayGetValueAtIndex(ac_pair
, 1),
1238 auth_options
.dictionary
, &updated_acl
, error
), out
);
1240 if (updated_acl
|| CFEqual(CFArrayGetValueAtIndex(ac_pair
, 1), CFSTR(""))) {
1241 // we assume that only one ACL can be modified during ItemAdd or ItemUpdate
1242 SecAccessControlRef ac
= NULL
;
1243 require(ac
= SecAccessControlCreateFromData(kCFAllocatorDefault
,
1244 updated_acl
? updated_acl
: CFArrayGetValueAtIndex(ac_pair
, 0), error
), out
);
1245 SecAccessControlSetBound(ac
, true);
1246 CFAssignRetained(updated_acl
, SecAccessControlCopyData(ac
));
1247 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecAttrAccessControl
, updated_acl
);
1248 CFRelease(updated_acl
);
1257 CFReleaseSafe(auth_options
.mutable_dictionary
);
1258 CFReleaseSafe(ac_pairs
);
1262 void SecItemAuthCopyParams(SecCFDictionaryCOW
*auth_params
, SecCFDictionaryCOW
*query
) {
1263 // Store operation prompt.
1264 CFStringRef operation_prompt
= CFDictionaryGetValue(query
->dictionary
, kSecUseOperationPrompt
);
1265 if (operation_prompt
!= NULL
) {
1266 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseOperationPrompt
, operation_prompt
);
1267 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseOperationPrompt
);
1270 // Store caller name.
1271 CFStringRef caller_name
= CFDictionaryGetValue(query
->dictionary
, kSecUseCallerName
);
1272 if (caller_name
!= NULL
) {
1273 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCallerName
, caller_name
);
1274 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseCallerName
);
1277 // Find out whether we are allowed to pop up a UI.
1278 CFTypeRef auth_ui
= CFDictionaryGetValue(query
->dictionary
, kSecUseAuthenticationUI
) ?:
1279 (CFEqualSafe(CFDictionaryGetValue(query
->dictionary
, kSecUseNoAuthenticationUI
), kCFBooleanTrue
) ?
1280 kSecUseAuthenticationUIFail
: kSecUseAuthenticationUIAllow
);
1281 if (!CFEqual(auth_ui
, kSecUseAuthenticationUISkip
) || CFDictionaryGetValue(query
->dictionary
, kSecUseNoAuthenticationUI
)) {
1282 if (CFDictionaryContainsKey(query
->dictionary
, kSecUseNoAuthenticationUI
))
1283 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseNoAuthenticationUI
);
1284 if (!CFEqualSafe(auth_ui
, kSecUseAuthenticationUISkip
) && CFDictionaryContainsKey(query
->dictionary
, kSecUseAuthenticationUI
))
1285 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseAuthenticationUI
);
1288 if (!CFEqual(auth_ui
, kSecUseAuthenticationUIAllow
)) {
1289 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationUI
, auth_ui
);
1292 CFDataRef acm_context
= CFDictionaryGetValue(query
->dictionary
, kSecUseCredentialReference
);
1293 if (acm_context
!= NULL
) {
1294 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, acm_context
);
1298 static SecItemAuthResult
SecItemCreatePairsFromError(CFErrorRef
*error
, CFArrayRef
*ac_pairs
)
1300 if (error
&& *error
&& CFErrorGetCode(*error
) == errSecAuthNeeded
&& CFEqualSafe(CFErrorGetDomain(*error
), kSecErrorDomain
)) {
1301 // Extract ACLs to be verified from the error.
1302 CFDictionaryRef user_info
= CFErrorCopyUserInfo(*error
);
1303 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, errSecAuthNeeded
);
1304 CFRetainAssign(*ac_pairs
, CFDictionaryGetValue(user_info
, key
));
1305 if (*ac_pairs
== NULL
)
1306 CFAssignRetained(*ac_pairs
, CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
));
1309 CFRelease(user_info
);
1310 CFReleaseNull(*error
);
1311 return kSecItemAuthResultNeedAuth
;
1313 return kSecItemAuthResultError
;
1316 // Wrapper to handle automatic authentication and token/secd case switching.
1317 static bool SecItemAuthDoQuery(SecCFDictionaryCOW
*query
, SecCFDictionaryCOW
*attributes
, const void *secItemOperation
, CFErrorRef
*error
,
1318 bool (^perform
)(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
)) {
1320 SecCFDictionaryCOW auth_params
= { NULL
};
1321 SecAccessControlRef access_control
= NULL
;
1322 __block TKTokenRef token
= NULL
;
1324 if (secItemOperation
== SecItemAdd
|| secItemOperation
== SecItemUpdate
) {
1325 CFDictionaryRef dict
= attributes
? attributes
->dictionary
: query
->dictionary
;
1326 access_control
= (SecAccessControlRef
)CFDictionaryGetValue(dict
, kSecAttrAccessControl
);
1327 if (access_control
&& SecAccessControlGetConstraints(access_control
) &&
1328 CFEqualSafe(CFDictionaryGetValue(dict
, kSecAttrSynchronizable
), kCFBooleanTrue
))
1329 require_quiet(SecError(errSecParam
, error
, CFSTR("item with kSecAttrAccessControl is not synchronizable")), out
);
1332 // Perform initial surgery on query/attributes (resolve LAContext to serialized ACM handle, resolve
1333 // SecAccessControlRef to serialized forms, expand kSecValueRef etc.)
1335 secItemOperation
== SecItemCopyMatching
||
1336 secItemOperation
== SecItemUpdate
||
1337 secItemOperation
== SecItemDelete
;
1339 require_quiet(SecItemAttributesPrepare(query
, forQuery
, error
), out
);
1340 if (attributes
!= NULL
)
1341 require_quiet(SecItemAttributesPrepare(attributes
, false, error
), out
);
1343 // Populate auth_params dictionary according to initial query contents.
1344 SecItemAuthCopyParams(&auth_params
, query
);
1346 if (secItemOperation
!= SecItemCopyMatching
) {
1347 // UISkip is allowed only for CopyMatching.
1348 require_action_quiet(!CFEqualSafe(CFDictionaryGetValue(query
->dictionary
, kSecUseAuthenticationUI
), kSecUseAuthenticationUISkip
), out
,
1349 SecError(errSecParam
, error
,
1350 CFSTR("kSecUseAuthenticationUISkip is allowed only for SecItemCopyMatching")));
1353 ok
= SecItemAuthDo(&auth_params
, error
, ^SecItemAuthResult(CFDictionaryRef auth_params
, CFArrayRef
*ac_pairs
, CFErrorRef
*error
) {
1354 SecItemAuthResult result
= kSecItemAuthResultError
;
1356 // Propagate actual credential reference to the query.
1357 if (auth_params
!= NULL
) {
1358 CFDataRef acm_context
= CFDictionaryGetValue(auth_params
, kSecUseCredentialReference
);
1359 if (acm_context
!= NULL
) {
1360 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query
), kSecUseCredentialReference
, acm_context
);
1363 CFDataRef acl_data_ref
= CFDictionaryGetValue(auth_params
, kSecAttrAccessControl
);
1364 if (acl_data_ref
!= NULL
) {
1365 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes
?: query
), kSecAttrAccessControl
, acl_data_ref
);
1369 // Prepare connection to target token if it is present.
1370 CFStringRef token_id
= CFDictionaryGetValue(query
->dictionary
, kSecAttrTokenID
);
1371 if (secItemOperation
!= SecItemCopyMatching
&& token_id
!= NULL
) {
1372 require_quiet(CFAssignRetained(token
, SecTokenCreate(token_id
, auth_params
, error
)), out
);
1375 CFDictionaryRef attrs
= (attributes
!= NULL
) ? attributes
->dictionary
: NULL
;
1376 if(!perform(token
, query
->dictionary
, attrs
, auth_params
, error
)) {
1377 require_quiet((result
= SecItemCreatePairsFromError(error
, ac_pairs
)) == kSecItemAuthResultOK
, out
);
1380 result
= kSecItemAuthResultOK
;
1385 require_quiet(ok
, out
);
1390 CFReleaseSafe(token
);
1391 CFReleaseSafe(auth_params
.mutable_dictionary
);
1395 static bool cftype_to_bool_cftype_error_request(enum SecXPCOperation op
, CFTypeRef attributes
, CFTypeRef
*result
, CFErrorRef
*error
)
1397 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1398 return SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, attributes
, error
);
1399 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1401 return SecXPCDictionaryCopyPListOptional(response
, kSecXPCKeyResult
, result
, error
);
1407 static CFArrayRef
dict_to_array_error_request(enum SecXPCOperation op
, CFDictionaryRef attributes
, CFErrorRef
*error
)
1409 CFArrayRef result
= NULL
;
1410 bool success
= cftype_to_bool_cftype_error_request(op
, attributes
, (CFTypeRef
*)&result
, error
);
1411 if(success
&& !isArray(result
)){
1412 SecError(errSecUnimplemented
, error
, CFSTR("Unexpected nonarray returned: %@"), result
);
1413 CFReleaseNull(result
);
1418 bool cftype_client_to_bool_cftype_error_request(enum SecXPCOperation op
, CFTypeRef attributes
, __unused SecurityClient
*client
, CFTypeRef
*result
, CFErrorRef
*error
) {
1419 return cftype_to_bool_cftype_error_request(op
, attributes
, result
, error
);
1422 static bool dict_to_error_request(enum SecXPCOperation op
, CFDictionaryRef query
, CFErrorRef
*error
)
1424 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1425 return SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, query
, error
);
1429 static bool cfstring_array_to_error_request(enum SecXPCOperation op
, CFStringRef string
, CFArrayRef attributes
, __unused SecurityClient
*client
, CFErrorRef
*error
)
1431 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1433 if (!SecXPCDictionarySetString(message
, kSecXPCKeyString
, string
, error
))
1438 if (!SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, attributes
, error
))
1446 static bool dict_client_to_error_request(enum SecXPCOperation op
, CFDictionaryRef query
, __unused SecurityClient
*client
, CFErrorRef
*error
)
1448 return dict_to_error_request(op
, query
, error
);
1451 static bool SecTokenCreateAccessControlError(CFStringRef operation
, CFDataRef access_control
, CFErrorRef
*error
) {
1452 CFArrayRef ac_pair
= CFArrayCreateForCFTypes(NULL
, access_control
, operation
, NULL
);
1453 const void *ac_pairs
[] = { CFArrayCreateForCFTypes(NULL
, ac_pair
, NULL
) };
1454 const void *keys
[] = { CFNumberCreateWithCFIndex(NULL
, errSecAuthNeeded
) };
1455 CFAssignRetained(*error
, CFErrorCreateWithUserInfoKeysAndValues(NULL
, kSecErrorDomain
, errSecAuthNeeded
,
1456 keys
, ac_pairs
, 1));
1458 CFRelease(ac_pairs
[0]);
1463 static bool SecTokenProcessError(CFStringRef operation
, TKTokenRef token
, CFTypeRef object_or_attrs
, CFErrorRef
*error
) {
1464 if (CFEqualSafe(CFErrorGetDomain(*error
), CFSTR(kTKErrorDomain
)) &&
1465 CFErrorGetCode(*error
) == kTKErrorCodeAuthenticationFailed
) {
1466 // Replace error with the one which is augmented with access control and operation which failed,
1467 // which will cause SecItemDoWithAuth to throw UI.
1468 // Create array containing tuple (array) with error and requested operation.
1469 CFDataRef access_control
= (CFGetTypeID(object_or_attrs
) == CFDataGetTypeID()) ?
1470 TKTokenCopyObjectAccessControl(token
, object_or_attrs
, error
) :
1471 TKTokenCopyObjectCreationAccessControl(token
, object_or_attrs
, error
);
1472 if (access_control
!= NULL
) {
1473 SecTokenCreateAccessControlError(operation
, access_control
, error
);
1474 CFRelease(access_control
);
1480 static CFTypeRef
SecTokenCopyUpdatedObjectID(TKTokenRef token
, CFDataRef object_id
, CFMutableDictionaryRef attributes
, CFErrorRef
*error
) {
1481 CFDataRef access_control
= NULL
, db_value
= NULL
, new_object_id
= NULL
;
1482 SecAccessControlRef ac
= NULL
;
1484 // Make sure that ACL is bound - if not, generate an error which will trigger binding.
1485 CFDataRef ac_data
= CFDictionaryGetValue(attributes
, kSecAttrAccessControl
);
1486 if (ac_data
!= NULL
) {
1487 require_quiet(ac
= SecAccessControlCreateFromData(NULL
, ac_data
, error
), out
);
1488 require_action_quiet(SecAccessControlIsBound(ac
), out
,
1489 SecTokenCreateAccessControlError(CFSTR(""), ac_data
, error
));
1492 // Create or update the object on the token.
1493 require_action_quiet(new_object_id
= TKTokenCreateOrUpdateObject(token
, object_id
, attributes
, error
), out
,
1494 SecTokenProcessError(kAKSKeyOpEncrypt
, token
, object_id
?: (CFTypeRef
)attributes
, error
));
1496 // Prepare kSecValueData field for the item to be stored into the keychain DB.
1497 require_quiet(access_control
= TKTokenCopyObjectAccessControl(token
, new_object_id
, error
), out
);
1498 require_quiet(db_value
= SecTokenItemValueCreate(new_object_id
, access_control
,
1499 CFDictionaryGetValue(attributes
, kSecValueData
), error
), out
);
1500 CFDictionarySetValue(attributes
, kSecValueData
, db_value
);
1502 // kSecAttrAccessControl is handled directly by the token and stored inside data field.
1503 CFDictionaryRemoveValue(attributes
, kSecAttrAccessControl
);
1507 CFReleaseSafe(access_control
);
1508 CFReleaseSafe(db_value
);
1509 return new_object_id
;
1512 static bool SecTokenItemAdd(TKTokenRef token
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
,
1513 CFTypeRef
*result
, CFErrorRef
*error
) {
1515 CFTypeRef object_id
= NULL
, ref
= NULL
;
1516 CFDictionaryRef ref_attrs
= NULL
;
1517 CFTypeRef db_result
= NULL
;
1519 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutableCopy(NULL
, 0, attributes
);
1520 require_quiet(object_id
= SecTokenCopyUpdatedObjectID(token
, NULL
, attrs
, error
), out
);
1522 // Augment attributes from default attributes of the related ref (SecKeyRef, SecCertificateRef). This is best done
1523 // by creating ref and getting back its attributes.
1524 require_quiet(SecTokenItemCreateFromAttributes(attrs
, auth_params
, token
, object_id
, &ref
, error
), out
);
1526 if ((ref_attrs
= SecItemCopyAttributeDictionary(ref
, false)) != NULL
) {
1527 CFDictionaryForEach(ref_attrs
, ^(const void *key
, const void *value
) {
1528 if (!CFEqual(key
, kSecValueData
)) {
1529 CFDictionaryAddValue(attrs
, key
, value
);
1535 // Make sure that both attributes and data are returned.
1536 CFDictionarySetValue(attrs
, kSecReturnAttributes
, kCFBooleanTrue
);
1537 CFDictionarySetValue(attrs
, kSecReturnData
, kCFBooleanTrue
);
1539 if (!CFEqualSafe(CFDictionaryGetValue(attrs
, kSecAttrIsPermanent
), kCFBooleanFalse
)) {
1540 // IsPermanent is not present or is true, so add item to the db.
1541 require_quiet(SECURITYD_XPC(sec_item_add
, cftype_client_to_bool_cftype_error_request
, attrs
,
1542 SecSecurityClientGet(), &db_result
, error
), out
);
1544 // Process directly result of token call.
1545 db_result
= CFRetain(attrs
);
1547 require_quiet(SecItemResultProcess(attributes
, auth_params
, token
, db_result
, result
, error
), out
);
1552 CFReleaseSafe(db_result
);
1553 CFReleaseSafe(attrs
);
1554 CFReleaseSafe(ref_attrs
);
1555 CFReleaseSafe(object_id
);
1560 OSStatus
SecItemAdd(CFDictionaryRef attributes
, CFTypeRef
*result
) {
1561 __block SecCFDictionaryCOW attrs
= { attributes
};
1564 os_activity_t trace_activity
= os_activity_start("SecItemAdd_ios", OS_ACTIVITY_FLAG_DEFAULT
);
1566 require_quiet(!explode_identity(attrs
.dictionary
, (secitem_operation
)SecItemAdd
, &status
, result
), errOut
);
1567 infer_cert_label(&attrs
);
1569 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1570 return SecItemAuthDoQuery(&attrs
, NULL
, SecItemAdd
, error
, ^bool(TKTokenRef token
, CFDictionaryRef attributes
, CFDictionaryRef unused
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1571 if (token
== NULL
) {
1572 CFTypeRef raw_result
= NULL
;
1573 if (!SECURITYD_XPC(sec_item_add
, cftype_client_to_bool_cftype_error_request
, attributes
, SecSecurityClientGet(), &raw_result
, error
))
1576 bool ok
= SecItemResultProcess(attributes
, auth_params
, token
, raw_result
, result
, error
);
1577 CFReleaseSafe(raw_result
);
1580 // Send request to an appropriate token instead of secd.
1581 return SecTokenItemAdd(token
, attributes
, auth_params
, result
, error
);
1587 CFReleaseSafe(attrs
.mutable_dictionary
);
1589 os_activity_end(trace_activity
);
1594 OSStatus
SecItemCopyMatching(CFDictionaryRef inQuery
, CFTypeRef
*result
) {
1596 __block SecCFDictionaryCOW query
= { inQuery
};
1598 os_activity_t trace_activity
= os_activity_start("SecItemCopyMatching_ios", OS_ACTIVITY_FLAG_DEFAULT
);
1600 require_quiet(!explode_identity(query
.dictionary
, (secitem_operation
)SecItemCopyMatching
, &status
, result
), errOut
);
1602 bool wants_data
= cf_bool_value(CFDictionaryGetValue(query
.dictionary
, kSecReturnData
));
1603 bool wants_attributes
= cf_bool_value(CFDictionaryGetValue(query
.dictionary
, kSecReturnAttributes
));
1604 if ((wants_data
&& !wants_attributes
) || (!wants_data
&& wants_attributes
)) {
1605 // When either attributes or data are requested, we need to query both, because for token based items,
1606 // both are needed in order to generate proper data and/or attributes results.
1607 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query
), kSecReturnAttributes
, kCFBooleanTrue
);
1608 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query
), kSecReturnData
, kCFBooleanTrue
);
1611 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1612 return SecItemAuthDoQuery(&query
, NULL
, SecItemCopyMatching
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1613 CFTypeRef raw_result
= NULL
;
1614 if (!SECURITYD_XPC(sec_item_copy_matching
, cftype_client_to_bool_cftype_error_request
, query
, SecSecurityClientGet(), &raw_result
, error
))
1617 // We intentionally pass NULL as token argument, because we want to be able to decide about token on which the item lives
1618 // on per-record basis, not wholesale. Logic inside SecItemResultCopyPrepared will open proper token according
1619 // to currently processed item.
1620 bool ok
= SecItemResultProcess(inQuery
, auth_params
, NULL
, raw_result
, result
, error
);
1621 CFReleaseSafe(raw_result
);
1628 CFReleaseSafe(query
.mutable_dictionary
);
1629 os_activity_end(trace_activity
);
1633 // Invokes token-object handler for each item matching specified query.
1634 static bool SecTokenItemForEachMatching(CFDictionaryRef query
, CFErrorRef
*error
,
1635 bool (^perform
)(CFDictionaryRef item_value
, CFDictionaryRef item_query
,
1636 CFErrorRef
*error
)) {
1638 CFMutableDictionaryRef list_query
= NULL
;
1639 CFTypeRef items
= NULL
;
1640 CFArrayRef ref_array
= NULL
;
1641 CFDictionaryRef item_query
= NULL
, item_data
= NULL
;
1643 // Query all items with data and persistent_ref, so that we can extract objectIDs and also identify originating
1644 // items in the keychain.
1645 list_query
= CFDictionaryCreateMutableCopy(NULL
, 0, query
);
1646 if (CFDictionaryGetValue(list_query
, kSecMatchLimit
) == NULL
) {
1647 CFDictionarySetValue(list_query
, kSecMatchLimit
, kSecMatchLimitAll
);
1649 CFDictionarySetValue(list_query
, kSecReturnData
, kCFBooleanTrue
);
1650 CFDictionarySetValue(list_query
, kSecReturnPersistentRef
, kCFBooleanTrue
);
1651 require_quiet(SECURITYD_XPC(sec_item_copy_matching
, cftype_client_to_bool_cftype_error_request
, list_query
,
1652 SecSecurityClientGet(), &items
, error
), out
);
1653 if (CFGetTypeID(items
) != CFArrayGetTypeID()) {
1654 // Wrap single returned item into the array.
1655 CFArrayRef item_array
= CFArrayCreateForCFTypes(NULL
, items
, NULL
);
1656 CFAssignRetained(items
, item_array
);
1660 CFArrayForEachC(items
, item
) {
1661 CFDataRef data
= CFDictionaryGetValue(item
, kSecValueData
);
1662 require_action_quiet(data
!= NULL
, out
, SecError(errSecInternal
, error
, CFSTR("value not present for token item")));
1664 CFAssignRetained(item_data
, SecTokenItemValueCopy(data
, error
));
1665 require_quiet(item_data
, out
);
1667 CFAssignRetained(item_query
,
1668 CFDictionaryCreateForCFTypes(NULL
,
1669 kSecValuePersistentRef
, CFDictionaryGetValue(item
, kSecValuePersistentRef
),
1671 require_quiet(perform(item_data
, item_query
, error
), out
);
1677 CFReleaseSafe(list_query
);
1678 CFReleaseSafe(items
);
1679 CFReleaseSafe(item_data
);
1680 CFReleaseSafe(ref_array
);
1681 CFReleaseSafe(item_query
);
1685 static bool SecItemRawUpdate(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
, CFErrorRef
*error
) {
1688 // Ensure the dictionary passed to securityd has proper kCFTypeDictionaryKeyCallBacks.
1689 CFMutableDictionaryRef tmp
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
1690 CFDictionaryForEach(attributesToUpdate
, ^(const void *key
, const void *value
) { CFDictionaryAddValue(tmp
, key
, value
); });
1691 ok
= gSecurityd
->sec_item_update(query
, tmp
, SecSecurityClientGet(), error
);
1694 xpc_object_t message
= securityd_create_message(sec_item_update_id
, error
);
1696 if (SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, query
, error
) &&
1697 SecXPCDictionarySetPList(message
, kSecXPCKeyAttributesToUpdate
, attributesToUpdate
, error
)) {
1698 xpc_object_t reply
= securityd_message_with_reply_sync(message
, error
);
1700 ok
= securityd_message_no_error(reply
, error
);
1704 xpc_release(message
);
1710 static bool SecTokenItemUpdate(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
, CFErrorRef
*error
) {
1711 return SecTokenItemForEachMatching(query
, error
, ^bool(CFDictionaryRef object_data
, CFDictionaryRef item_query
, CFErrorRef
*error
) {
1713 CFDataRef object_id
= NULL
;
1714 CFMutableDictionaryRef db_value
= NULL
;
1716 // Update attributes on the token.
1717 CFMutableDictionaryRef attributes
= CFDictionaryCreateMutableCopy(NULL
, 0, attributesToUpdate
);
1718 require_quiet(object_id
= SecTokenCopyUpdatedObjectID(token
, CFDictionaryGetValue(object_data
, kSecTokenValueObjectIDKey
),
1719 attributes
, error
), out
);
1721 // Update attributes in the database.
1722 require_quiet(SecItemRawUpdate(item_query
, attributes
, error
), out
);
1727 CFReleaseSafe(object_id
);
1728 CFReleaseSafe(attributes
);
1729 CFReleaseSafe(db_value
);
1734 OSStatus
SecItemUpdate(CFDictionaryRef inQuery
, CFDictionaryRef inAttributesToUpdate
) {
1735 return SecOSStatusWith(^bool(CFErrorRef
*error
) {
1736 return SecItemUpdateWithError(inQuery
, inAttributesToUpdate
, error
);
1741 SecItemUpdateWithError(CFDictionaryRef inQuery
,
1742 CFDictionaryRef inAttributesToUpdate
,
1745 __block SecCFDictionaryCOW query
= { inQuery
};
1746 __block SecCFDictionaryCOW attributesToUpdate
= { inAttributesToUpdate
};
1747 bool result
= false;
1749 if (handleUpdateIdentity(inQuery
, inAttributesToUpdate
, &result
, error
))
1752 result
= SecItemAuthDoQuery(&query
, &attributesToUpdate
, SecItemUpdate
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1753 if (token
== NULL
) {
1754 return SecItemRawUpdate(query
, attributes
, error
);
1756 return SecTokenItemUpdate(token
, query
, attributes
, error
);
1761 CFReleaseSafe(query
.mutable_dictionary
);
1762 CFReleaseSafe(attributesToUpdate
.mutable_dictionary
);
1766 static OSStatus
explode_persistent_identity_ref(SecCFDictionaryCOW
*query
)
1768 OSStatus status
= errSecSuccess
;
1769 CFTypeRef persist
= CFDictionaryGetValue(query
->dictionary
, kSecValuePersistentRef
);
1771 if (persist
&& _SecItemParsePersistentRef(persist
, &class, NULL
)
1772 && CFEqual(class, kSecClassIdentity
)) {
1773 const void *keys
[] = { kSecReturnRef
, kSecValuePersistentRef
};
1774 const void *vals
[] = { kCFBooleanTrue
, persist
};
1775 CFDictionaryRef persistent_query
= CFDictionaryCreate(NULL
, keys
,
1776 vals
, (array_size(keys
)), NULL
, NULL
);
1777 CFTypeRef item_query
= NULL
;
1778 status
= SecItemCopyMatching(persistent_query
, &item_query
);
1779 CFReleaseNull(persistent_query
);
1783 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecValuePersistentRef
);
1784 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query
), kSecValueRef
, item_query
);
1785 CFRelease(item_query
);
1791 OSStatus
SecItemDelete(CFDictionaryRef inQuery
) {
1793 __block SecCFDictionaryCOW query
= { inQuery
};
1795 os_activity_t trace_activity
= os_activity_start("SecItemDelete_ios", OS_ACTIVITY_FLAG_DEFAULT
);
1797 require_noerr_quiet(status
= explode_persistent_identity_ref(&query
), errOut
);
1798 require_quiet(!explode_identity(query
.dictionary
, (secitem_operation
)SecItemDelete
, &status
, NULL
), errOut
);
1800 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1801 return SecItemAuthDoQuery(&query
, NULL
, SecItemDelete
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1802 if (token
== NULL
) {
1803 return SECURITYD_XPC(sec_item_delete
, dict_client_to_error_request
, query
, SecSecurityClientGet(), error
);
1805 return SecTokenItemForEachMatching(query
, error
, ^bool(CFDictionaryRef object_data
, CFDictionaryRef item_query
, CFErrorRef
*error
) {
1808 // Delete item from the token.
1809 CFDataRef object_id
= CFDictionaryGetValue(object_data
, kSecTokenValueObjectIDKey
);
1810 require_action_quiet(TKTokenDeleteObject(token
, object_id
, error
), out
,
1811 SecTokenProcessError(kAKSKeyOpDelete
, token
, object_id
, error
));
1813 // Delete the item from the keychain.
1814 require_quiet(SECURITYD_XPC(sec_item_delete
, dict_client_to_error_request
, item_query
,
1815 SecSecurityClientGet(), error
), out
);
1826 CFReleaseSafe(query
.mutable_dictionary
);
1828 os_activity_end(trace_activity
);
1834 SecItemDeleteAll(void)
1836 return SecOSStatusWith(^bool (CFErrorRef
*error
) {
1840 #ifndef SECITEM_SHIM_OSX
1841 SecTrustStoreRef ts
= SecTrustStoreForDomain(kSecTrustStoreDomainUser
);
1842 if (!gSecurityd
->sec_truststore_remove_all(ts
, error
))
1843 ok
= SecError(errSecInternal
, error
, CFSTR("sec_truststore_remove_all is NULL"));
1844 #endif // *** END SECITEM_SHIM_OSX ***
1845 if (!gSecurityd
->sec_item_delete_all(error
))
1846 ok
= SecError(errSecInternal
, error
, CFSTR("sec_item_delete_all is NULL"));
1848 ok
= securityd_send_sync_and_do(sec_delete_all_id
, error
, NULL
, NULL
);
1855 agrps_client_to_error_request(enum SecXPCOperation op
, CFArrayRef agrps
, __unused SecurityClient
*client
, CFErrorRef
*error
)
1857 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1858 return SecXPCDictionarySetPList(message
, kSecXPCKeyAccessGroups
, agrps
, error
);
1862 bool SecItemDeleteAllWithAccessGroups(CFArrayRef accessGroups
, CFErrorRef
*error
) {
1863 os_activity_t trace_activity
= os_activity_start("SecItemDeleteAllWithAccessGroups", OS_ACTIVITY_FLAG_DEFAULT
);
1865 bool ok
= SECURITYD_XPC(sec_delete_items_with_access_groups
, agrps_client_to_error_request
, accessGroups
,
1866 SecSecurityClientGet(), error
);
1868 os_activity_end(trace_activity
);
1873 #if SECITEM_SHIM_OSX
1874 SecItemUpdateTokenItems_ios(CFTypeRef tokenID
, CFArrayRef tokenItemsAttributes
)
1876 SecItemUpdateTokenItems(CFTypeRef tokenID
, CFArrayRef tokenItemsAttributes
)
1881 os_activity_t trace_activity
= os_activity_start("SecItemDelete_ios", OS_ACTIVITY_FLAG_DEFAULT
);
1883 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1884 CFArrayRef tmpArrayRef
= tokenItemsAttributes
;
1885 if (tokenItemsAttributes
) {
1886 CFMutableArrayRef tokenItems
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1887 for (CFIndex i
= 0; i
< CFArrayGetCount(tokenItemsAttributes
); ++i
) {
1888 CFDictionaryRef itemAttributes
= CFArrayGetValueAtIndex(tokenItemsAttributes
, i
);
1889 CFTypeRef accessControl
= CFDictionaryGetValue(itemAttributes
, kSecAttrAccessControl
);
1890 CFTypeRef tokenOID
= CFDictionaryGetValue(itemAttributes
, kSecAttrTokenOID
);
1891 CFTypeRef valueData
= CFDictionaryGetValue(itemAttributes
, kSecValueData
);
1892 if (tokenOID
!= NULL
&& accessControl
!= NULL
&& CFDataGetTypeID() == CFGetTypeID(accessControl
)) {
1893 CFDataRef data
= SecTokenItemValueCreate(tokenOID
, accessControl
, valueData
, error
);
1895 CFRelease(tokenItems
);
1899 CFMutableDictionaryRef attributes
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, itemAttributes
);
1900 CFDictionarySetValue(attributes
, kSecValueData
, data
);
1901 CFDictionarySetValue(attributes
, kSecAttrTokenID
, tokenID
);
1902 CFDictionaryRemoveValue(attributes
, kSecAttrAccessControl
);
1903 CFDictionaryRemoveValue(attributes
, kSecAttrTokenOID
);
1904 CFArrayAppendValue(tokenItems
, attributes
);
1905 CFRelease(attributes
);
1909 CFArrayAppendValue(tokenItems
, itemAttributes
);
1912 tmpArrayRef
= tokenItems
;
1915 return SECURITYD_XPC(sec_item_update_token_items
, cfstring_array_to_error_request
, tokenID
, tmpArrayRef
, SecSecurityClientGet(), error
);
1918 os_activity_end(trace_activity
);
1923 CFArrayRef
_SecKeychainSyncUpdateMessage(CFDictionaryRef updates
, CFErrorRef
*error
) {
1924 __block CFArrayRef result
;
1925 os_activity_initiate("_SecKeychainSyncUpdateMessage", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1926 result
= SECURITYD_XPC(sec_keychain_sync_update_message
, dict_to_array_error_request
, updates
, error
);
1931 #ifndef SECITEM_SHIM_OSX
1932 OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
);
1934 OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
)
1936 return -1; /* this is only on OS X currently */
1941 extern OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
);
1945 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
1947 bool _SecKeychainRollKeys(bool force
, CFErrorRef
*error
)
1949 do_if_registered(sec_roll_keys
, force
, error
);
1951 __block
bool result
= false;
1953 secdebug("secitem","enter - %s", __FUNCTION__
);
1954 securityd_send_sync_and_do(kSecXPCOpRollKeys
, error
,
1955 ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1956 xpc_dictionary_set_bool(message
, "force", force
);
1959 ^bool(xpc_object_t response
, __unused CFErrorRef
*error
) {
1960 result
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);