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/SecAccessControl.h>
35 #include <Security/SecAccessControlPriv.h>
36 #ifndef SECITEM_SHIM_OSX
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 #endif // *** END SECITEM_SHIM_OSX ***
45 #include <Security/SecCTKKeyPriv.h>
46 #include <Security/SecTask.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>
79 #include <Security/SecInternal.h>
80 #include <TargetConditionals.h>
81 #include <ipc/securityd_client.h>
82 #include <Security/SecuritydXPC.h>
83 #include <AssertMacros.h>
85 #include <sys/types.h>
89 #ifndef SECITEM_SHIM_OSX
90 #include <libDER/asn1Types.h>
91 #endif // *** END SECITEM_SHIM_OSX ***
93 /* label when certificate data is joined with key data */
94 #define CERTIFICATE_DATA_COLUMN_LABEL "certdata"
96 #include <utilities/SecDb.h>
97 #include <IOKit/IOReturn.h>
99 #include <coreauthd_spi.h>
100 #include <LocalAuthentication/LAPrivateDefines.h>
101 #include <LocalAuthentication/LACFSupport.h>
103 #include <ctkclient.h>
105 /* Return an OSStatus for a sqlite3 error code. */
106 static OSStatus
osstatus_for_s3e(int s3e
)
108 if (s3e
> 0 && s3e
<= SQLITE_DONE
) switch (s3e
)
113 return errSecNotAvailable
; /* errSecDuplicateItem; */
114 case SQLITE_FULL
: /* Happens if we run out of uniqueids */
115 return errSecNotAvailable
; /* TODO: Replace with a better error code. */
117 case SQLITE_READONLY
:
118 return errSecNotAvailable
;
119 case SQLITE_CANTOPEN
:
120 return errSecNotAvailable
;
122 return errSecNotAvailable
;
123 case SQLITE_CONSTRAINT
:
124 return errSecDuplicateItem
;
127 case SQLITE_MISMATCH
:
128 return errSecNoSuchAttr
;
130 return errSecNotAvailable
;
132 return -2; /* TODO: Replace with a real error code. */
133 case SQLITE_INTERNAL
:
135 return errSecNotAvailable
; /* TODO: Replace with a real error code. */
140 static OSStatus
osstatus_for_kern_return(CFIndex kernResult
)
145 return errSecSuccess
;
146 case kIOReturnNotReadable
:
147 case kIOReturnNotWritable
:
148 return errSecAuthFailed
;
149 case kIOReturnNotPermitted
:
150 case kIOReturnNotPrivileged
:
151 case kIOReturnLockedRead
:
152 case kIOReturnLockedWrite
:
153 return errSecInteractionNotAllowed
;
156 case kIOReturnBadArgument
:
159 return errSecNotAvailable
; /* TODO: Replace with a real error code. */
163 static OSStatus
osstatus_for_xpc_error(CFIndex xpcError
) {
166 case kSecXPCErrorSuccess
:
167 return errSecSuccess
;
168 case kSecXPCErrorUnexpectedType
:
169 case kSecXPCErrorUnexpectedNull
:
171 case kSecXPCErrorConnectionFailed
:
172 return errSecNotAvailable
;
173 case kSecXPCErrorUnknown
:
175 return errSecInternal
;
179 static OSStatus
osstatus_for_der_error(CFIndex derError
) {
182 case kSecDERErrorUnknownEncoding
:
183 case kSecDERErrorUnsupportedDERType
:
184 case kSecDERErrorUnsupportedNumberType
:
186 case kSecDERErrorUnsupportedCFObject
:
188 case kSecDERErrorAllocationFailure
:
189 return errSecAllocate
;
191 return errSecInternal
;
195 static OSStatus
osstatus_for_ids_error(CFIndex idsError
) {
198 case kSecIDSErrorNoDeviceID
:
199 return errSecDeviceIDNeeded
;
200 case kSecIDSErrorNotRegistered
:
201 return errSecIDSNotRegistered
;
202 case kSecIDSErrorFailedToSend
:
203 return errSecFailedToSendIDSMessage
;
204 case kSecIDSErrorCouldNotFindMatchingAuthToken
:
205 return errSecDeviceIDNoMatch
;
206 case kSecIDSErrorNoPeersAvailable
:
207 return errSecPeersNotAvailable
;
209 return errSecInternal
;
213 static OSStatus
osstatus_for_localauthentication_error(CFIndex laError
) {
214 // Wrap LA error in Sec error.
216 case kLAErrorUserCancel
:
217 return errSecUserCanceled
;
218 case kLAErrorParameter
:
220 case kLAErrorNotInteractive
:
221 return errSecInteractionNotAllowed
;
223 return errSecAuthFailed
;
227 static OSStatus
osstatus_for_ctk_error(CFIndex ctkError
) {
228 // Hack, get rid of it once dep lands: <rdar://problem/21181736> Export error code constants from ctkclient.h header
229 #ifndef kTKErrorCodeNotImplemented
230 #define kTKErrorCodeNotImplemented -1
232 #ifndef kTKErrorCodeCanceledByUser
233 #define kTKErrorCodeCanceledByUser -4
237 case kTKErrorCodeBadParameter
:
239 case kTKErrorCodeNotImplemented
:
240 return errSecUnimplemented
;
241 case kTKErrorCodeCanceledByUser
:
242 return errSecUserCanceled
;
244 return errSecInternal
;
248 // Convert from securityd error codes to OSStatus for legacy API.
249 OSStatus
SecErrorGetOSStatus(CFErrorRef error
) {
252 status
= errSecSuccess
;
254 CFStringRef domain
= CFErrorGetDomain(error
);
255 if (domain
== NULL
) {
256 secerror("No error domain for error: %@", error
);
257 status
= errSecInternal
;
258 } else if (CFEqual(kSecErrorDomain
, domain
)) {
259 status
= (OSStatus
)CFErrorGetCode(error
);
260 } else if (CFEqual(kSecDbErrorDomain
, domain
)) {
261 status
= osstatus_for_s3e((int)CFErrorGetCode(error
));
262 } else if (CFEqual(kSecErrnoDomain
, domain
)) {
263 status
= (OSStatus
)CFErrorGetCode(error
);
264 } else if (CFEqual(kSecKernDomain
, domain
)) {
265 status
= osstatus_for_kern_return(CFErrorGetCode(error
));
266 } else if (CFEqual(sSecXPCErrorDomain
, domain
)) {
267 status
= osstatus_for_xpc_error(CFErrorGetCode(error
));
268 } else if (CFEqual(sSecDERErrorDomain
, domain
)) {
269 status
= osstatus_for_der_error(CFErrorGetCode(error
));
270 } else if (CFEqual(kSecIDSErrorDomain
, domain
)) {
271 status
= osstatus_for_ids_error(CFErrorGetCode(error
));
272 } else if (CFEqual(CFSTR(kLAErrorDomain
), domain
)) {
273 status
= osstatus_for_localauthentication_error(CFErrorGetCode(error
));
274 } else if (CFEqual(CFSTR(kTKErrorDomain
), domain
)) {
275 status
= osstatus_for_ctk_error(CFErrorGetCode(error
));
277 secnotice("securityd", "unknown error domain: %@ for error: %@", domain
, error
);
278 status
= errSecInternal
;
284 // Wrapper to provide a CFErrorRef for legacy API.
285 OSStatus
SecOSStatusWith(bool (^perform
)(CFErrorRef
*error
)) {
286 CFErrorRef error
= NULL
;
288 if (perform(&error
)) {
289 assert(error
== NULL
);
290 status
= errSecSuccess
;
293 status
= SecErrorGetOSStatus(error
);
294 if (status
!= errSecItemNotFound
) // Occurs in normal operation, so exclude
295 secerror("error:[%" PRIdOSStatus
"] %@", status
, error
);
296 CFReleaseNull(error
);
301 /* IPC uses CFPropertyList to un/marshall input/output data and can handle:
302 CFData, CFString, CFArray, CFDictionary, CFDate, CFBoolean, and CFNumber
304 Currently in need of conversion below:
305 @@@ kSecValueRef allows SecKeychainItemRef and SecIdentityRef
306 @@@ kSecMatchPolicy allows a query with a SecPolicyRef
307 @@@ kSecUseItemList allows a query against a list of itemrefs, this isn't
308 currently implemented at all, but when it is needs to short circuit to
309 local evaluation, different from the sql query abilities
312 #ifndef SECITEM_SHIM_OSX
313 static CFDictionaryRef
314 SecItemCopyAttributeDictionary(CFTypeRef ref
) {
315 CFDictionaryRef refDictionary
= NULL
;
316 CFTypeID typeID
= CFGetTypeID(ref
);
317 if (typeID
== SecKeyGetTypeID()) {
318 refDictionary
= SecKeyCopyAttributeDictionary((SecKeyRef
)ref
);
319 } else if (typeID
== SecCertificateGetTypeID()) {
321 SecCertificateCopyAttributeDictionary((SecCertificateRef
)ref
);
322 } else if (typeID
== SecIdentityGetTypeID()) {
324 SecIdentityRef identity
= (SecIdentityRef
)ref
;
325 SecCertificateRef cert
= NULL
;
326 SecKeyRef key
= NULL
;
327 if (!SecIdentityCopyCertificate(identity
, &cert
) &&
328 !SecIdentityCopyPrivateKey(identity
, &key
))
330 CFDataRef data
= SecCertificateCopyData(cert
);
331 CFDictionaryRef key_dict
= SecKeyCopyAttributeDictionary(key
);
333 if (key_dict
&& data
) {
334 refDictionary
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, key_dict
);
335 CFDictionarySetValue((CFMutableDictionaryRef
)refDictionary
,
336 CFSTR(CERTIFICATE_DATA_COLUMN_LABEL
), data
);
338 CFReleaseNull(key_dict
);
344 refDictionary
= NULL
;
346 return refDictionary
;
350 SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes
) {
351 CFTypeRef ref
= NULL
;
352 CFStringRef
class = CFDictionaryGetValue(refAttributes
, kSecClass
);
353 if (CFEqual(class, kSecClassCertificate
)) {
354 ref
= SecCertificateCreateFromAttributeDictionary(refAttributes
);
355 } else if (CFEqual(class, kSecClassKey
)) {
356 ref
= SecKeyCreateFromAttributeDictionary(refAttributes
);
357 } else if (CFEqual(class, kSecClassIdentity
)) {
358 CFAllocatorRef allocator
= NULL
;
359 CFDataRef data
= CFDictionaryGetValue(refAttributes
, CFSTR(CERTIFICATE_DATA_COLUMN_LABEL
));
360 SecCertificateRef cert
= SecCertificateCreateWithData(allocator
, data
);
361 SecKeyRef key
= SecKeyCreateFromAttributeDictionary(refAttributes
);
363 ref
= SecIdentityCreate(allocator
, cert
, key
);
367 /* We don't support SecKeychainItemRefs yet. */
368 } else if (CFEqual(class, kSecClassGenericPassword
)) {
369 } else if (CFEqual(class, kSecClassInternetPassword
)) {
370 } else if (CFEqual(class, kSecClassAppleSharePassword
)) {
379 extern CFTypeRef
SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes
);
384 SecItemCopyDisplayNames(CFArrayRef items
, CFArrayRef
*displayNames
)
387 return -1 /* errSecUnimplemented */;
390 #ifndef SECITEM_SHIM_OSX
391 typedef OSStatus (*secitem_operation
)(CFDictionaryRef attributes
, CFTypeRef
*result
);
393 static bool explode_identity(CFDictionaryRef attributes
, secitem_operation operation
,
394 OSStatus
*return_status
, CFTypeRef
*return_result
)
396 bool handled
= false;
397 CFTypeRef value
= CFDictionaryGetValue(attributes
, kSecValueRef
);
399 CFTypeID typeID
= CFGetTypeID(value
);
400 if (typeID
== SecIdentityGetTypeID()) {
402 OSStatus status
= errSecSuccess
;
403 SecIdentityRef identity
= (SecIdentityRef
)value
;
404 SecCertificateRef cert
= NULL
;
405 SecKeyRef key
= NULL
;
406 if (!SecIdentityCopyCertificate(identity
, &cert
) &&
407 !SecIdentityCopyPrivateKey(identity
, &key
))
409 CFMutableDictionaryRef partial_query
=
410 CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, attributes
);
411 CFDictionarySetValue(partial_query
, kSecValueRef
, cert
);
412 CFTypeRef result
= NULL
;
413 bool duplicate_cert
= false;
414 /* an identity is first and foremost a key, but it can have multiple
415 certs associated with it: so we identify it by the cert */
416 status
= operation(partial_query
, return_result
? &result
: NULL
);
417 if ((operation
== (secitem_operation
)SecItemAdd
) &&
418 (status
== errSecDuplicateItem
)) {
419 duplicate_cert
= true;
420 status
= errSecSuccess
;
423 if (!status
|| status
== errSecItemNotFound
) {
424 bool skip_key_operation
= false;
426 /* if the key is still in use, skip deleting it */
427 if (operation
== (secitem_operation
)SecItemDelete
) {
428 // find certs with cert.pkhh == keys.klbl
429 CFDictionaryRef key_dict
= NULL
, query_dict
= NULL
;
430 CFDataRef pkhh
= NULL
;
432 key_dict
= SecKeyCopyAttributeDictionary(key
);
434 pkhh
= (CFDataRef
)CFDictionaryGetValue(key_dict
, kSecAttrApplicationLabel
);
435 const void *keys
[] = { kSecClass
, kSecAttrPublicKeyHash
};
436 const void *vals
[] = { kSecClassCertificate
, pkhh
};
438 query_dict
= CFDictionaryCreate(NULL
, keys
,
439 vals
, (array_size(keys
)),
442 if (errSecSuccess
== SecItemCopyMatching(query_dict
, NULL
))
443 skip_key_operation
= true;
444 CFReleaseSafe(query_dict
);
445 CFReleaseSafe(key_dict
);
448 if (!skip_key_operation
) {
449 /* now perform the operation for the key */
450 CFDictionarySetValue(partial_query
, kSecValueRef
, key
);
451 CFDictionarySetValue(partial_query
, kSecReturnPersistentRef
, kCFBooleanFalse
);
452 status
= operation(partial_query
, NULL
);
453 if ((operation
== (secitem_operation
)SecItemAdd
) &&
454 (status
== errSecDuplicateItem
) &&
456 status
= errSecSuccess
;
459 /* add and copy matching for an identityref have a persistent ref result */
462 /* result is a persistent ref to a cert */
464 if (_SecItemParsePersistentRef(result
, NULL
, &rowid
)) {
465 *return_result
= _SecItemMakePersistentRef(kSecClassIdentity
, rowid
);
471 CFReleaseNull(partial_query
);
474 status
= errSecInvalidItemRef
;
478 *return_status
= status
;
481 value
= CFDictionaryGetValue(attributes
, kSecClass
);
482 if (value
&& CFEqual(kSecClassIdentity
, value
) &&
483 (operation
== (secitem_operation
)SecItemDelete
)) {
484 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, attributes
);
485 CFDictionaryRemoveValue(dict
, kSecClass
);
486 CFDictionarySetValue(dict
, kSecClass
, kSecClassCertificate
);
487 OSStatus status
= SecItemDelete(dict
);
489 CFDictionarySetValue(dict
, kSecClass
, kSecClassKey
);
490 status
= SecItemDelete(dict
);
493 *return_status
= status
;
500 static void infer_cert_label(SecCFDictionaryCOW
*attributes
)
502 if (!CFDictionaryContainsKey(attributes
->dictionary
, kSecAttrLabel
)) {
503 CFTypeRef value_ref
= CFDictionaryGetValue(attributes
->dictionary
, kSecValueRef
);
504 if (value_ref
&& CFGetTypeID(value_ref
) == SecCertificateGetTypeID()) {
505 SecCertificateRef certificate
= (SecCertificateRef
)value_ref
;
506 CFStringRef label
= SecCertificateCopySubjectSummary(certificate
);
508 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes
), kSecAttrLabel
, label
);
509 CFReleaseNull(label
);
515 /* A persistent ref is just the class and the rowid of the record. */
516 CF_RETURNS_RETAINED CFDataRef
_SecItemMakePersistentRef(CFTypeRef
class, sqlite_int64 rowid
)
518 uint8_t bytes
[sizeof(sqlite_int64
) + 4];
521 if (CFStringGetCString(class, (char *)bytes
, 4 + 1 /*null-term*/,
522 kCFStringEncodingUTF8
))
524 OSWriteBigInt64(bytes
+ 4, 0, rowid
);
525 return CFDataCreate(NULL
, bytes
, sizeof(bytes
));
530 /* AUDIT[securityd](done):
531 persistent_ref (ok) is a caller provided, non NULL CFTypeRef.
533 bool _SecItemParsePersistentRef(CFDataRef persistent_ref
, CFStringRef
*return_class
, sqlite_int64
*return_rowid
)
535 bool valid_ref
= false;
536 if (CFGetTypeID(persistent_ref
) == CFDataGetTypeID() &&
537 CFDataGetLength(persistent_ref
) == (CFIndex
)(sizeof(sqlite_int64
) + 4)) {
538 const uint8_t *bytes
= CFDataGetBytePtr(persistent_ref
);
539 sqlite_int64 rowid
= OSReadBigInt64(bytes
+ 4, 0);
541 CFStringRef
class = CFStringCreateWithBytes(kCFAllocatorDefault
,
542 bytes
, CFStringGetLength(kSecClassGenericPassword
),
543 kCFStringEncodingUTF8
, true);
544 const void *valid_classes
[] = { kSecClassGenericPassword
,
545 kSecClassInternetPassword
,
546 kSecClassAppleSharePassword
,
547 kSecClassCertificate
,
552 for (i
=0; i
< array_size(valid_classes
); i
++) {
553 if (CFEqual(valid_classes
[i
], class)) {
555 *return_class
= valid_classes
[i
];
557 *return_rowid
= rowid
;
567 #endif // *** END SECITEM_SHIM_OSX ***
569 static bool cf_bool_value(CFTypeRef cf_bool
)
571 return (cf_bool
&& CFEqual(kCFBooleanTrue
, cf_bool
));
574 CFMutableDictionaryRef
SecCFDictionaryCOWGetMutable(SecCFDictionaryCOW
*cow_dictionary
) {
575 if (cow_dictionary
->mutable_dictionary
== NULL
) {
576 cow_dictionary
->mutable_dictionary
= CFDictionaryCreateMutableForCFTypes(NULL
);
577 if (cow_dictionary
->dictionary
!= NULL
) {
578 CFDictionaryForEach(cow_dictionary
->dictionary
, ^(const void *key
, const void *value
) {
579 CFDictionarySetValue(cow_dictionary
->mutable_dictionary
, key
, value
);
582 cow_dictionary
->dictionary
= cow_dictionary
->mutable_dictionary
;
585 return cow_dictionary
->mutable_dictionary
;
588 // Keys for dictionary of kSecvalueData of token-based items.
589 static const CFStringRef kSecTokenValueObjectIDKey
= CFSTR("oid");
590 static const CFStringRef kSecTokenValueAccessControlKey
= CFSTR("ac");
591 static const CFStringRef kSecTokenValueDataKey
= CFSTR("data");
593 // Creates kSecValueData field stored in the DB for token-based items. Data field consists of objectID, real
594 // access_control and optionally of the data value.
595 static CFDataRef
SecTokenItemValueCreate(CFDataRef oid
, CFDataRef access_control
, CFDataRef object_value
, CFErrorRef
*error
) {
596 CFMutableDictionaryRef value
= NULL
;
597 value
= CFDictionaryCreateMutableForCFTypesWith(NULL
,
598 kSecTokenValueObjectIDKey
, oid
,
599 kSecTokenValueAccessControlKey
, access_control
,
601 if (object_value
!= NULL
) {
602 CFDictionarySetValue(value
, kSecTokenValueDataKey
, object_value
);
605 CFDataRef value_data
= CFPropertyListCreateDERData(NULL
, value
, error
);
610 static CFDictionaryRef
SecTokenItemValueCopy(CFDataRef db_value
, CFErrorRef
*error
) {
611 CFPropertyListRef plist
= NULL
;
612 const uint8_t *der
= CFDataGetBytePtr(db_value
);
613 const uint8_t *der_end
= der
+ CFDataGetLength(db_value
);
614 require_quiet(der
= der_decode_plist(0, kCFPropertyListImmutable
, &plist
, error
, der
, der_end
), out
);
615 require_action_quiet(der
== der_end
, out
, SecError(errSecDecode
, error
, CFSTR("trailing garbage at end of token data field")));
616 require_action_quiet(CFDictionaryGetValue(plist
, kSecTokenValueObjectIDKey
) != NULL
, out
,
617 SecError(errSecInternal
, error
, CFSTR("token based item data does not have OID")));
623 TKTokenRef
SecTokenCreate(CFStringRef token_id
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
624 CFMutableDictionaryRef token_attrs
= NULL
;
625 TKTokenRef token
= NULL
;
626 token_attrs
= (auth_params
!= NULL
) ?
627 CFDictionaryCreateMutableCopy(NULL
, 0, auth_params
) :
628 CFDictionaryCreateMutableForCFTypes(NULL
);
629 CFDictionarySetValue(token_attrs
, kSecAttrTokenID
, token_id
);
631 CFDictionaryRemoveValue(token_attrs
, kSecUseAuthenticationContext
);
632 token
= TKTokenCreate(token_attrs
, error
);
634 CFReleaseSafe(token_attrs
);
638 static bool SecTokenItemCreateFromAttributes(CFDictionaryRef attributes
, CFDictionaryRef auth_params
,
639 TKTokenRef token
, CFDataRef object_id
, CFTypeRef
*ref
, CFErrorRef
*error
) {
641 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutableCopy(NULL
, 0, attributes
);
642 CFTypeRef token_id
= CFDictionaryGetValue(attributes
, kSecAttrTokenID
);
643 if (token_id
!= NULL
) {
644 if (CFRetainSafe(token
) == NULL
) {
645 require_quiet(token
= SecTokenCreate(token_id
, auth_params
, error
), out
);
648 if (auth_params
!= NULL
) {
649 CFDictionaryForEach(auth_params
, ^(const void *key
, const void *value
) {
650 CFDictionarySetValue(attrs
, key
, value
);
653 CFDictionarySetValue(attrs
, kSecUseToken
, token
);
654 CFDictionarySetValue(attrs
, kSecUseTokenObjectID
, object_id
);
657 *ref
= SecItemCreateFromAttributeDictionary(attrs
);
661 CFReleaseSafe(attrs
);
666 /* Turn the returned single value or dictionary that contains all the attributes to create a
667 ref into the exact result the client asked for */
668 static bool SecItemResultCopyPrepared(CFTypeRef raw_result
, TKTokenRef token
,
669 CFDictionaryRef query
, CFDictionaryRef auth_params
,
670 CFTypeRef
*result
, CFErrorRef
*error
) {
672 CFDataRef ac_data
= NULL
;
673 CFDataRef value
= NULL
;
674 CFTypeRef persistent_ref
= NULL
;
675 CFStringRef token_id
= NULL
;
676 CFDataRef object_id
= NULL
;
677 CFMutableDictionaryRef attrs
= NULL
;
679 bool wants_ref
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnRef
));
680 bool wants_data
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnData
));
681 bool wants_attributes
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnAttributes
));
682 bool wants_persistent_ref
= cf_bool_value(CFDictionaryGetValue(query
, kSecReturnPersistentRef
));
684 // Get token value if not provided by the caller.
685 bool token_item
= false;
687 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID()) {
688 token_id
= CFDictionaryGetValue(raw_result
, kSecAttrTokenID
);
689 token_item
= (token_id
!= NULL
);
696 // Decode and prepare data value, if it is requested at the output, or if we want attributes from token.
697 if (wants_data
|| wants_ref
|| (token_item
&& wants_attributes
)) {
698 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID())
699 value
= CFRetainSafe(CFDictionaryGetValue(raw_result
, kSecValueData
));
701 value
= CFRetainSafe(raw_result
);
702 if (token_item
&& value
!= NULL
) {
703 // Parse token-based item's data field.
704 CFDataRef object_value
= NULL
;
705 CFDictionaryRef parsed_value
= NULL
;
706 require_quiet(parsed_value
= SecTokenItemValueCopy(value
, error
), out
);
707 object_id
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueObjectIDKey
));
708 ac_data
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueAccessControlKey
));
709 object_value
= CFRetainSafe(CFDictionaryGetValue(parsed_value
, kSecTokenValueDataKey
));
710 CFRelease(parsed_value
);
711 if (wants_data
&& object_value
== NULL
) {
712 // Retrieve value directly from the token.
714 require_quiet(token
= SecTokenCreate(token_id
, auth_params
, error
), out
);
716 require_quiet(object_value
= TKTokenCopyObjectData(token
, object_id
, error
), out
);
717 if (CFEqual(object_value
, kCFNull
))
718 CFReleaseNull(object_value
);
720 CFAssignRetained(value
, object_value
);
723 // If only thing requested is data, return them directly.
724 if (!(wants_attributes
|| wants_persistent_ref
|| wants_ref
)) {
725 *result
= CFRetainSafe(value
);
731 // Extract persistent_ref, if caller wants it.
732 if (wants_persistent_ref
) {
733 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID())
734 persistent_ref
= CFRetainSafe(CFDictionaryGetValue(raw_result
, kSecValuePersistentRef
));
736 persistent_ref
= CFRetainSafe(raw_result
);
738 // If only thing requested is persistentref, extract it from dictionary if needed and return it.
739 if (!(wants_attributes
|| wants_data
|| wants_ref
)) {
740 *result
= CFRetainSafe(persistent_ref
);
746 if (wants_ref
|| wants_attributes
|| (wants_data
&& wants_persistent_ref
)) {
747 // For these cases we need output dictionary.
748 if (CFGetTypeID(raw_result
) == CFDictionaryGetTypeID())
749 *result
= CFDictionaryCreateMutableCopy(NULL
, 0, raw_result
);
751 *result
= CFDictionaryCreateForCFTypes(NULL
, NULL
);
752 CFMutableDictionaryRef output
= (CFMutableDictionaryRef
)*result
;
754 if ((wants_data
|| wants_ref
) && value
!= NULL
)
755 CFDictionarySetValue(output
, kSecValueData
, value
);
757 CFDictionaryRemoveValue(output
, kSecValueData
);
759 if (wants_persistent_ref
&& persistent_ref
!= NULL
)
760 CFDictionarySetValue(output
, kSecValuePersistentRef
, persistent_ref
);
762 CFDictionaryRemoveValue(output
, kSecValuePersistentRef
);
766 require_quiet(SecTokenItemCreateFromAttributes(output
, auth_params
, token
, object_id
, &ref
, error
), out
);
767 if (!(wants_attributes
|| wants_data
|| wants_persistent_ref
)) {
768 CFAssignRetained(*result
, ref
);
769 } else if (ref
!= NULL
) {
770 CFDictionarySetValue(output
, kSecValueRef
, ref
);
773 // We could have stored data value previously to make ref creation succeed.
774 // They are not needed any more and in case that caller did not want the data, avoid returning them.
775 CFDictionaryRemoveValue(output
, kSecValueData
);
780 if (wants_attributes
) {
781 // Convert serialized form of access control to object form.
783 CFRetainAssign(ac_data
, CFDictionaryGetValue(output
, kSecAttrAccessControl
));
786 if (ac_data
!= NULL
) {
787 SecAccessControlRef ac
;
788 require_quiet(ac
= SecAccessControlCreateFromData(kCFAllocatorDefault
, ac_data
, error
), out
);
789 CFDictionarySetValue(output
, kSecAttrAccessControl
, ac
);
800 CFReleaseSafe(ac_data
);
801 CFReleaseSafe(value
);
802 CFReleaseSafe(persistent_ref
);
803 CFReleaseSafe(object_id
);
804 CFReleaseSafe(attrs
);
805 CFReleaseSafe(token
);
809 static bool SecItemResultProcess(CFDictionaryRef query
, CFDictionaryRef auth_params
, TKTokenRef token
,
810 CFTypeRef raw_result
, CFTypeRef
*result
, CFErrorRef
*error
) {
812 require_action_quiet(raw_result
!= NULL
, out
, ok
= true);
813 require_action_quiet(result
!= NULL
, out
, ok
= true);
815 if (CFGetTypeID(raw_result
) == CFArrayGetTypeID()) {
816 CFIndex i
, count
= CFArrayGetCount(raw_result
);
817 *result
= CFArrayCreateMutableForCFTypes(NULL
);
818 for (i
= 0; i
< count
; i
++) {
820 require_quiet(SecItemResultCopyPrepared(CFArrayGetValueAtIndex(raw_result
, i
),
821 token
, query
, auth_params
, &ref
, error
), out
);
823 CFArrayAppendValue((CFMutableArrayRef
)*result
, ref
);
828 require_quiet(SecItemResultCopyPrepared(raw_result
, token
, query
, auth_params
, result
, error
), out
);
837 static bool SecItemAttributesPrepare(SecCFDictionaryCOW
*attrs
, CFErrorRef
*error
) {
839 CFDataRef ac_data
= NULL
, acm_context
= NULL
;
842 SecAccessControlRef access_control
= (SecAccessControlRef
)CFDictionaryGetValue(attrs
->dictionary
, kSecAttrAccessControl
);
843 if (access_control
!= NULL
) {
844 require_quiet(ac_data
= SecAccessControlCopyData(access_control
), out
);
845 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecAttrAccessControl
, ac_data
);
848 const CFTypeRef la_context
= CFDictionaryGetValue(attrs
->dictionary
, kSecUseAuthenticationContext
);
850 require_action_quiet(!CFDictionaryContainsKey(attrs
->dictionary
, kSecUseCredentialReference
), out
,
851 SecError(errSecParam
, error
, CFSTR("kSecUseAuthenticationContext cannot be used together with kSecUseCredentialReference")));
852 require_action_quiet(la_lib
= dlopen("/System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication", RTLD_LAZY
), out
,
853 SecError(errSecInternal
, error
, CFSTR("failed to open LocalAuthentication.framework")));
854 LAFunctionCopyExternalizedContext fnCopyExternalizedContext
= NULL
;
855 require_action_quiet(fnCopyExternalizedContext
= dlsym(la_lib
, "LACopyExternalizedContext"), out
,
856 SecError(errSecInternal
, error
, CFSTR("failed to obtain LACopyExternalizedContext")));
857 require_action_quiet(acm_context
= fnCopyExternalizedContext(la_context
), out
,
858 SecError(errSecInternal
, error
, CFSTR("failed to get ACM handle from LAContext")));
859 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs
), kSecUseAuthenticationContext
);
860 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecUseCredentialReference
, acm_context
);
863 #ifndef SECITEM_SHIM_OSX
864 // If a ref was specified we get it's attribute dictionary and parse it.
865 CFTypeRef value
= CFDictionaryGetValue(attrs
->dictionary
, kSecValueRef
);
867 CFDictionaryRef ref_attributes
;
868 require_action_quiet(ref_attributes
= SecItemCopyAttributeDictionary(value
), out
,
869 SecError(errSecValueRefUnsupported
, error
, CFSTR("unsupported kSecValueRef in query")));
871 /* Replace any attributes we already got from the ref with the ones
872 from the attributes dictionary the caller passed us. This allows
873 a caller to add an item using attributes from the ref and still
874 override some of them in the dictionary directly. */
875 CFMutableDictionaryRef new_query
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, ref_attributes
);
876 CFRelease(ref_attributes
);
877 CFDictionaryForEach(attrs
->dictionary
, ^(const void *key
, const void *value
) {
878 if (!CFEqual(key
, kSecValueRef
))
879 CFDictionarySetValue(new_query
, key
, value
);
881 CFAssignRetained(attrs
->mutable_dictionary
, new_query
);
882 attrs
->dictionary
= attrs
->mutable_dictionary
;
884 value
= CFDictionaryGetValue(attrs
->dictionary
, kSecAttrIssuer
);
886 /* convert DN to canonical issuer, if value is DN (top level sequence) */
887 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(value
), CFDataGetLength(value
) };
888 DERDecodedInfo content
;
889 if (!DERDecodeItem(&name
, &content
) &&
890 (content
.tag
== ASN1_CONSTR_SEQUENCE
))
892 CFDataRef canonical_issuer
= createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
893 if (canonical_issuer
) {
894 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs
), kSecAttrIssuer
, canonical_issuer
);
895 CFRelease(canonical_issuer
);
904 if (la_lib
!= NULL
) {
907 CFReleaseSafe(ac_data
);
908 CFReleaseSafe(acm_context
);
912 static bool SecItemAuthMaxAttemptsReached(CFArrayRef ac_pairs
, CFErrorRef
*error
)
914 CFMutableStringRef log_string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
916 CFArrayForEachC(ac_pairs
, ac_pair
) {
917 CFStringRef acl_hex_string
= CFDataCopyHexString(CFArrayGetValueAtIndex(ac_pair
, 0));
918 CFStringRef str
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("operation: %@ acl:%@\n"), CFArrayGetValueAtIndex(ac_pair
, 1), acl_hex_string
);
919 CFStringAppend(log_string
, str
);
920 CFRelease(acl_hex_string
);
924 CFStringRef reason
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("Reached maximum count of authentication attempts\n %@"), log_string
);
925 SecError(errSecAuthFailed
, error
, reason
);
926 __security_simulatecrash(reason
, __sec_exception_code_AuthLoop
);
929 CFRelease(log_string
);
933 bool SecItemAuthDo(SecCFDictionaryCOW
*auth_params
, CFErrorRef
*error
, SecItemAuthResult (^perform
)(CFDictionaryRef auth_params
, CFArrayRef
*ac_pairs
, CFErrorRef
*error
)) {
935 CFArrayRef ac_pairs
= NULL
;
936 SecCFDictionaryCOW auth_options
= { NULL
};
938 for (uint32_t i
= 0;; ++i
) {
939 // If the operation succeeded or failed with other than auth-needed error, just leave.
940 SecItemAuthResult auth_result
= perform(auth_params
->dictionary
, &ac_pairs
, error
);
941 require_quiet(auth_result
!= kSecItemAuthResultError
, out
);
942 require_action_quiet(auth_result
== kSecItemAuthResultNeedAuth
, out
, ok
= true);
944 // If auth_params were not created up to now, do create them because we will definitely need them.
945 SecCFDictionaryCOWGetMutable(auth_params
);
947 // Retrieve or create authentication handle and/or ACM context.
948 CFTypeRef auth_handle
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseAuthenticationContext
);
949 if (auth_handle
== NULL
) {
950 CFDataRef acm_context
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseCredentialReference
);
951 require_quiet(auth_handle
= LACreateNewContextWithACMContext(acm_context
, error
), out
);
952 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationContext
, auth_handle
);
953 CFRelease(auth_handle
);
954 if (acm_context
== NULL
) {
955 require_quiet(acm_context
= LACopyACMContext(auth_handle
, error
), out
);
956 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, acm_context
);
957 CFRelease(acm_context
);
961 // Throttle max authentication attempts. This is mainly protection against exceptional states, not ordinary
963 require_action(i
< 20, out
, SecItemAuthMaxAttemptsReached(ac_pairs
, error
));
965 // Prepare auth options dictionary.
966 if (auth_options
.dictionary
== NULL
) {
967 CFStringRef operation_prompt
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseOperationPrompt
);
968 if (operation_prompt
!= NULL
) {
969 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionAuthenticationReason
);
970 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, operation_prompt
);
974 CFTypeRef auth_ui
= CFDictionaryGetValue(auth_params
->dictionary
, kSecUseAuthenticationUI
);
975 if (CFEqualSafe(auth_ui
, kSecUseAuthenticationUIFail
)) {
976 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, kLAOptionNotInteractive
);
977 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options
), key
, kCFBooleanTrue
);
982 // Go through all access_control-operation pairs and evaluate them.
984 CFArrayForEachC(ac_pairs
, ac_pair
) {
985 CFDataRef updated_acl
= NULL
;
986 require_quiet(LAEvaluateAndUpdateACL(auth_handle
,
987 CFArrayGetValueAtIndex(ac_pair
, 0), CFArrayGetValueAtIndex(ac_pair
, 1),
988 auth_options
.dictionary
, &updated_acl
, error
), out
);
990 if (updated_acl
|| CFEqual(CFArrayGetValueAtIndex(ac_pair
, 1), CFSTR(""))) {
991 // we assume that only one ACL can be modified during ItemAdd or ItemUpdate
992 SecAccessControlRef ac
= NULL
;
993 require(ac
= SecAccessControlCreateFromData(kCFAllocatorDefault
,
994 updated_acl
? updated_acl
: CFArrayGetValueAtIndex(ac_pair
, 0), error
), out
);
995 SecAccessControlSetBound(ac
, true);
996 CFAssignRetained(updated_acl
, SecAccessControlCopyData(ac
));
997 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecAttrAccessControl
, updated_acl
);
998 CFRelease(updated_acl
);
1007 CFReleaseSafe(auth_options
.mutable_dictionary
);
1008 CFReleaseSafe(ac_pairs
);
1012 void SecItemAuthCopyParams(SecCFDictionaryCOW
*auth_params
, SecCFDictionaryCOW
*query
) {
1013 // Store operation prompt.
1014 CFStringRef operation_prompt
= CFDictionaryGetValue(query
->dictionary
, kSecUseOperationPrompt
);
1015 if (operation_prompt
!= NULL
) {
1016 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseOperationPrompt
, operation_prompt
);
1017 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseOperationPrompt
);
1020 // Find out whether we are allowed to pop up a UI.
1021 CFTypeRef auth_ui
= CFDictionaryGetValue(query
->dictionary
, kSecUseAuthenticationUI
) ?:
1022 (CFEqualSafe(CFDictionaryGetValue(query
->dictionary
, kSecUseNoAuthenticationUI
), kCFBooleanTrue
) ?
1023 kSecUseAuthenticationUIFail
: kSecUseAuthenticationUIAllow
);
1024 if (!CFEqual(auth_ui
, kSecUseAuthenticationUISkip
) || CFDictionaryGetValue(query
->dictionary
, kSecUseNoAuthenticationUI
)) {
1025 if (CFDictionaryContainsKey(query
->dictionary
, kSecUseNoAuthenticationUI
))
1026 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseNoAuthenticationUI
);
1027 if (!CFEqualSafe(auth_ui
, kSecUseAuthenticationUISkip
) && CFDictionaryContainsKey(query
->dictionary
, kSecUseAuthenticationUI
))
1028 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecUseAuthenticationUI
);
1031 if (!CFEqual(auth_ui
, kSecUseAuthenticationUIAllow
)) {
1032 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseAuthenticationUI
, auth_ui
);
1035 CFDataRef acm_context
= CFDictionaryGetValue(query
->dictionary
, kSecUseCredentialReference
);
1036 if (acm_context
!= NULL
) {
1037 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params
), kSecUseCredentialReference
, acm_context
);
1041 static SecItemAuthResult
SecItemCreatePairsFromError(CFErrorRef
*error
, CFArrayRef
*ac_pairs
)
1043 if (error
&& CFErrorGetCode(*error
) == errSecAuthNeeded
&& CFEqualSafe(CFErrorGetDomain(*error
), kSecErrorDomain
)) {
1044 // Extract ACLs to be verified from the error.
1045 CFDictionaryRef user_info
= CFErrorCopyUserInfo(*error
);
1046 CFNumberRef key
= CFNumberCreateWithCFIndex(NULL
, errSecAuthNeeded
);
1047 CFRetainAssign(*ac_pairs
, CFDictionaryGetValue(user_info
, key
));
1048 if (*ac_pairs
== NULL
)
1049 CFAssignRetained(*ac_pairs
, CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
));
1052 CFRelease(user_info
);
1053 CFReleaseNull(*error
);
1054 return kSecItemAuthResultNeedAuth
;
1056 return kSecItemAuthResultError
;
1059 // Wrapper to handle automatic authentication and token/secd case switching.
1060 static bool SecItemAuthDoQuery(SecCFDictionaryCOW
*query
, SecCFDictionaryCOW
*attributes
, const void *secItemOperation
, CFErrorRef
*error
,
1061 bool (^perform
)(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
)) {
1063 SecCFDictionaryCOW auth_params
= { NULL
};
1064 SecAccessControlRef access_control
= NULL
;
1065 __block TKTokenRef token
= NULL
;
1067 if (secItemOperation
== SecItemAdd
|| secItemOperation
== SecItemUpdate
) {
1068 CFDictionaryRef dict
= attributes
? attributes
->dictionary
: query
->dictionary
;
1069 access_control
= (SecAccessControlRef
)CFDictionaryGetValue(dict
, kSecAttrAccessControl
);
1070 if (access_control
&& SecAccessControlGetConstraints(access_control
) &&
1071 CFEqualSafe(CFDictionaryGetValue(dict
, kSecAttrSynchronizable
), kCFBooleanTrue
))
1072 require_quiet(SecError(errSecParam
, error
, CFSTR("item with kSecAttrAccessControl is not synchronizable")), out
);
1075 // Perform initial surgery on query/attributes (resolve LAContext to serialized ACM handle, resolve
1076 // SecAccessControlRef to serialized forms, expand kSecValueRef etc.)
1077 require_quiet(SecItemAttributesPrepare(query
, error
), out
);
1078 if (attributes
!= NULL
)
1079 require_quiet(SecItemAttributesPrepare(attributes
, error
), out
);
1081 // Populate auth_params dictionary according to initial query contents.
1082 SecItemAuthCopyParams(&auth_params
, query
);
1084 if (secItemOperation
!= SecItemCopyMatching
) {
1085 // UISkip is allowed only for CopyMatching.
1086 require_action_quiet(!CFEqualSafe(CFDictionaryGetValue(query
->dictionary
, kSecUseAuthenticationUI
), kSecUseAuthenticationUISkip
), out
,
1087 SecError(errSecParam
, error
,
1088 CFSTR("kSecUseAuthenticationUISkip is allowed only for SecItemCopyMatching")));
1091 ok
= SecItemAuthDo(&auth_params
, error
, ^SecItemAuthResult(CFDictionaryRef auth_params
, CFArrayRef
*ac_pairs
, CFErrorRef
*error
) {
1092 SecItemAuthResult result
= kSecItemAuthResultError
;
1094 // Propagate actual credential reference to the query.
1095 if (auth_params
!= NULL
) {
1096 CFDataRef acm_context
= CFDictionaryGetValue(auth_params
, kSecUseCredentialReference
);
1097 if (acm_context
!= NULL
) {
1098 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query
), kSecUseCredentialReference
, acm_context
);
1101 CFDataRef acl_data_ref
= CFDictionaryGetValue(auth_params
, kSecAttrAccessControl
);
1102 if (acl_data_ref
!= NULL
) {
1103 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes
?: query
), kSecAttrAccessControl
, acl_data_ref
);
1107 // Prepare connection to target token if it is present.
1108 CFStringRef token_id
= CFDictionaryGetValue(query
->dictionary
, kSecAttrTokenID
);
1109 if (secItemOperation
!= SecItemCopyMatching
&& token_id
!= NULL
) {
1110 require_quiet(CFAssignRetained(token
, SecTokenCreate(token_id
, auth_params
, error
)), out
);
1113 CFDictionaryRef attrs
= (attributes
!= NULL
) ? attributes
->dictionary
: NULL
;
1114 if(!perform(token
, query
->dictionary
, attrs
, auth_params
, error
)) {
1115 require_quiet((result
= SecItemCreatePairsFromError(error
, ac_pairs
)) == kSecItemAuthResultOK
, out
);
1118 result
= kSecItemAuthResultOK
;
1123 require_quiet(ok
, out
);
1128 CFReleaseSafe(token
);
1129 CFReleaseSafe(auth_params
.mutable_dictionary
);
1133 #if SECITEM_SHIM_OSX
1134 /* TODO: Should be in some header */
1135 OSStatus
SecItemAdd_ios(CFDictionaryRef attributes
, CFTypeRef
*result
);
1136 OSStatus
SecItemCopyMatching_ios(CFDictionaryRef query
, CFTypeRef
*result
);
1137 OSStatus
SecItemUpdate_ios(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
);
1138 OSStatus
SecItemDelete_ios(CFDictionaryRef query
);
1141 static bool cftype_to_bool_cftype_error_request(enum SecXPCOperation op
, CFTypeRef attributes
, CFTypeRef
*result
, CFErrorRef
*error
)
1143 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1144 return SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, attributes
, error
);
1145 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1147 return SecXPCDictionaryCopyPListOptional(response
, kSecXPCKeyResult
, result
, error
);
1153 static CFArrayRef
dict_to_array_error_request(enum SecXPCOperation op
, CFDictionaryRef attributes
, CFErrorRef
*error
)
1155 CFArrayRef result
= NULL
;
1156 bool success
= cftype_to_bool_cftype_error_request(op
, attributes
, (CFTypeRef
*)&result
, error
);
1157 if(success
&& !isArray(result
)){
1158 SecError(errSecUnimplemented
, error
, CFSTR("Unexpected nonarray returned: %@"), result
);
1159 CFReleaseNull(result
);
1164 bool cftype_ag_to_bool_cftype_error_request(enum SecXPCOperation op
, CFTypeRef attributes
, __unused CFArrayRef accessGroups
, CFTypeRef
*result
, CFErrorRef
*error
) {
1165 return cftype_to_bool_cftype_error_request(op
, attributes
, result
, error
);
1168 static bool dict_to_error_request(enum SecXPCOperation op
, CFDictionaryRef query
, CFErrorRef
*error
)
1170 return securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1171 return SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, query
, error
);
1175 static bool dict_ag_to_error_request(enum SecXPCOperation op
, CFDictionaryRef query
, __unused CFArrayRef accessGroups
, CFErrorRef
*error
)
1177 return dict_to_error_request(op
, query
, error
);
1180 static bool SecTokenCreateAccessControlError(CFStringRef operation
, CFDataRef access_control
, CFErrorRef
*error
) {
1181 CFArrayRef ac_pair
= CFArrayCreateForCFTypes(NULL
, access_control
, operation
, NULL
);
1182 const void *ac_pairs
[] = { CFArrayCreateForCFTypes(NULL
, ac_pair
, NULL
) };
1183 const void *keys
[] = { CFNumberCreateWithCFIndex(NULL
, errSecAuthNeeded
) };
1184 CFAssignRetained(*error
, CFErrorCreateWithUserInfoKeysAndValues(NULL
, kSecErrorDomain
, errSecAuthNeeded
,
1185 keys
, ac_pairs
, 1));
1187 CFRelease(ac_pairs
[0]);
1192 static bool SecTokenProcessError(CFStringRef operation
, TKTokenRef token
, CFTypeRef object_or_attrs
, CFErrorRef
*error
) {
1193 if (CFEqualSafe(CFErrorGetDomain(*error
), CFSTR(kTKErrorDomain
)) &&
1194 CFErrorGetCode(*error
) == kTKErrorCodeAuthenticationFailed
) {
1195 // Replace error with the one which is augmented with access control and operation which failed,
1196 // which will cause SecItemDoWithAuth to throw UI.
1197 // Create array containing tuple (array) with error and requested operation.
1198 CFDataRef access_control
= (CFGetTypeID(object_or_attrs
) == CFDataGetTypeID()) ?
1199 TKTokenCopyObjectAccessControl(token
, object_or_attrs
, error
) :
1200 TKTokenCopyObjectCreationAccessControl(token
, object_or_attrs
, error
);
1201 if (access_control
!= NULL
) {
1202 SecTokenCreateAccessControlError(operation
, access_control
, error
);
1203 CFRelease(access_control
);
1209 static CFTypeRef
SecTokenCopyUpdatedObjectID(TKTokenRef token
, CFDataRef object_id
, CFMutableDictionaryRef attributes
, CFErrorRef
*error
) {
1210 CFDataRef access_control
= NULL
, db_value
= NULL
, new_object_id
= NULL
;
1211 SecAccessControlRef ac
= NULL
;
1213 // Make sure that ACL is bound - if not, generate an error which will trigger binding.
1214 CFDataRef ac_data
= CFDictionaryGetValue(attributes
, kSecAttrAccessControl
);
1215 if (ac_data
!= NULL
) {
1216 require_quiet(ac
= SecAccessControlCreateFromData(NULL
, ac_data
, error
), out
);
1217 require_action_quiet(SecAccessControlIsBound(ac
), out
,
1218 SecTokenCreateAccessControlError(CFSTR(""), ac_data
, error
));
1221 // Create or update the object on the token.
1222 require_action_quiet(new_object_id
= TKTokenCreateOrUpdateObject(token
, object_id
, attributes
, error
), out
,
1223 SecTokenProcessError(kAKSKeyOpEncrypt
, token
, object_id
?: (CFTypeRef
)attributes
, error
));
1225 // Prepare kSecValueData field for the item to be stored into the keychain DB.
1226 require_quiet(access_control
= TKTokenCopyObjectAccessControl(token
, new_object_id
, error
), out
);
1227 require_quiet(db_value
= SecTokenItemValueCreate(new_object_id
, access_control
,
1228 CFDictionaryGetValue(attributes
, kSecValueData
), error
), out
);
1229 CFDictionarySetValue(attributes
, kSecValueData
, db_value
);
1231 // kSecAttrAccessControl is handled directly by the token and stored inside data field.
1232 CFDictionaryRemoveValue(attributes
, kSecAttrAccessControl
);
1236 CFReleaseSafe(access_control
);
1237 CFReleaseSafe(db_value
);
1238 return new_object_id
;
1241 static bool SecTokenItemAdd(TKTokenRef token
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
,
1242 CFTypeRef
*result
, CFErrorRef
*error
) {
1244 CFTypeRef object_id
= NULL
, ref
= NULL
;
1245 CFDictionaryRef ref_attrs
= NULL
;
1246 CFTypeRef db_result
= NULL
;
1248 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutableCopy(NULL
, 0, attributes
);
1249 require_quiet(object_id
= SecTokenCopyUpdatedObjectID(token
, NULL
, attrs
, error
), out
);
1251 #ifndef SECITEM_SHIM_OSX
1252 // Augment attributes from default attributes of the related ref (SecKeyRef, SecCertificateRef). This is best done
1253 // by creating ref and getting back its attributes.
1254 require_quiet(SecTokenItemCreateFromAttributes(attrs
, auth_params
, token
, object_id
, &ref
, error
), out
);
1256 if ((ref_attrs
= SecItemCopyAttributeDictionary(ref
)) != NULL
) {
1257 CFDictionaryForEach(ref_attrs
, ^(const void *key
, const void *value
) {
1258 if (!CFEqual(key
, kSecValueData
)) {
1259 CFDictionaryAddValue(attrs
, key
, value
);
1266 // Make sure that both attributes and data are returned.
1267 CFDictionarySetValue(attrs
, kSecReturnAttributes
, kCFBooleanTrue
);
1268 CFDictionarySetValue(attrs
, kSecReturnData
, kCFBooleanTrue
);
1270 if (!CFEqualSafe(CFDictionaryGetValue(attrs
, kSecAttrIsPermanent
), kCFBooleanFalse
)) {
1271 // IsPermanent is not present or is true, so add item to the db.
1272 require_quiet(SECURITYD_XPC(sec_item_add
, cftype_ag_to_bool_cftype_error_request
, attrs
,
1273 SecAccessGroupsGetCurrent(), &db_result
, error
), out
);
1275 // Process directly result of token call.
1276 db_result
= CFRetain(attrs
);
1278 require_quiet(SecItemResultProcess(attributes
, auth_params
, token
, db_result
, result
, error
), out
);
1283 CFReleaseSafe(db_result
);
1284 CFReleaseSafe(attrs
);
1285 CFReleaseSafe(ref_attrs
);
1286 CFReleaseSafe(object_id
);
1292 #if SECITEM_SHIM_OSX
1293 SecItemAdd_ios(CFDictionaryRef attributes
, CFTypeRef
*result
)
1295 SecItemAdd(CFDictionaryRef attributes
, CFTypeRef
*result
)
1296 #endif // *** END SECITEM_SHIM_OSX ***
1298 __block SecCFDictionaryCOW attrs
= { attributes
};
1301 os_activity_t trace_activity
= os_activity_start("SecItemAdd_ios", OS_ACTIVITY_FLAG_DEFAULT
);
1303 #ifndef SECITEM_SHIM_OSX
1304 require_quiet(!explode_identity(attrs
.dictionary
, (secitem_operation
)SecItemAdd
, &status
, result
), errOut
);
1305 infer_cert_label(&attrs
);
1306 #endif // *** END SECITEM_SHIM_OSX ***
1308 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1309 return SecItemAuthDoQuery(&attrs
, NULL
, SecItemAdd
, error
, ^bool(TKTokenRef token
, CFDictionaryRef attributes
, CFDictionaryRef unused
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1310 if (token
== NULL
) {
1311 CFTypeRef raw_result
= NULL
;
1312 if (!SECURITYD_XPC(sec_item_add
, cftype_ag_to_bool_cftype_error_request
, attributes
, SecAccessGroupsGetCurrent(), &raw_result
, error
))
1315 bool ok
= SecItemResultProcess(attributes
, auth_params
, token
, raw_result
, result
, error
);
1316 CFReleaseSafe(raw_result
);
1319 // Send request to an appropriate token instead of secd.
1320 return SecTokenItemAdd(token
, attributes
, auth_params
, result
, error
);
1325 #ifndef SECITEM_SHIM_OSX
1328 CFReleaseSafe(attrs
.mutable_dictionary
);
1330 os_activity_end(trace_activity
);
1336 #if SECITEM_SHIM_OSX
1337 SecItemCopyMatching_ios(CFDictionaryRef inQuery
, CFTypeRef
*result
)
1339 SecItemCopyMatching(CFDictionaryRef inQuery
, CFTypeRef
*result
)
1340 #endif // *** END SECITEM_SHIM_OSX ***
1343 __block SecCFDictionaryCOW query
= { inQuery
};
1345 os_activity_t trace_activity
= os_activity_start("SecItemCopyMatching_ios", OS_ACTIVITY_FLAG_DEFAULT
);
1347 #ifndef SECITEM_SHIM_OSX
1348 require_quiet(!explode_identity(query
.dictionary
, (secitem_operation
)SecItemCopyMatching
, &status
, result
), errOut
);
1349 #endif // *** END SECITEM_SHIM_OSX ***
1351 bool wants_data
= cf_bool_value(CFDictionaryGetValue(query
.dictionary
, kSecReturnData
));
1352 bool wants_attributes
= cf_bool_value(CFDictionaryGetValue(query
.dictionary
, kSecReturnAttributes
));
1353 if ((wants_data
&& !wants_attributes
) || (!wants_data
&& wants_attributes
)) {
1354 // When either attributes or data are requested, we need to query both, because for token based items,
1355 // both are needed in order to generate proper data and/or attributes results.
1356 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query
), kSecReturnAttributes
, kCFBooleanTrue
);
1357 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query
), kSecReturnData
, kCFBooleanTrue
);
1360 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1361 return SecItemAuthDoQuery(&query
, NULL
, SecItemCopyMatching
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1362 CFTypeRef raw_result
= NULL
;
1363 if (!SECURITYD_XPC(sec_item_copy_matching
, cftype_ag_to_bool_cftype_error_request
, query
, SecAccessGroupsGetCurrent(), &raw_result
, error
))
1366 // We intentionally pass NULL as token argument, because we want to be able to decide about token on which the item lives
1367 // on per-record basis, not wholesale. Logic inside SecItemResultCopyPrepared will open proper token according
1368 // to currently processed item.
1369 bool ok
= SecItemResultProcess(inQuery
, auth_params
, NULL
, raw_result
, result
, error
);
1370 CFReleaseSafe(raw_result
);
1375 #ifndef SECITEM_SHIM_OSX
1379 CFReleaseSafe(query
.mutable_dictionary
);
1380 os_activity_end(trace_activity
);
1384 // Invokes token-object handler for each item matching specified query.
1385 static bool SecTokenItemForEachMatching(CFDictionaryRef query
, CFErrorRef
*error
,
1386 bool (^perform
)(CFDictionaryRef item_value
, CFDictionaryRef item_query
,
1387 CFErrorRef
*error
)) {
1389 CFMutableDictionaryRef list_query
= NULL
;
1390 CFTypeRef items
= NULL
;
1391 CFArrayRef ref_array
= NULL
;
1392 CFDictionaryRef item_query
= NULL
, item_data
= NULL
;
1394 // Query all items with data and persistent_ref, so that we can extract objectIDs and also identify originating
1395 // items in the keychain.
1396 list_query
= CFDictionaryCreateMutableCopy(NULL
, 0, query
);
1397 if (CFDictionaryGetValue(list_query
, kSecMatchLimit
) == NULL
) {
1398 CFDictionarySetValue(list_query
, kSecMatchLimit
, kSecMatchLimitAll
);
1400 CFDictionarySetValue(list_query
, kSecReturnData
, kCFBooleanTrue
);
1401 CFDictionarySetValue(list_query
, kSecReturnPersistentRef
, kCFBooleanTrue
);
1402 require_quiet(SECURITYD_XPC(sec_item_copy_matching
, cftype_ag_to_bool_cftype_error_request
, list_query
,
1403 SecAccessGroupsGetCurrent(), &items
, error
), out
);
1404 if (CFGetTypeID(items
) != CFArrayGetTypeID()) {
1405 // Wrap single returned item into the array.
1406 CFArrayRef item_array
= CFArrayCreateForCFTypes(NULL
, items
, NULL
);
1407 CFAssignRetained(items
, item_array
);
1411 CFArrayForEachC(items
, item
) {
1412 CFDataRef data
= CFDictionaryGetValue(item
, kSecValueData
);
1413 require_action_quiet(data
!= NULL
, out
, SecError(errSecInternal
, error
, CFSTR("value not present for token item")));
1415 CFAssignRetained(item_data
, SecTokenItemValueCopy(data
, error
));
1416 require_quiet(item_data
, out
);
1418 CFAssignRetained(item_query
,
1419 CFDictionaryCreateForCFTypes(NULL
,
1420 kSecValuePersistentRef
, CFDictionaryGetValue(item
, kSecValuePersistentRef
),
1422 require_quiet(perform(item_data
, item_query
, error
), out
);
1428 CFReleaseSafe(list_query
);
1429 CFReleaseSafe(items
);
1430 CFReleaseSafe(item_data
);
1431 CFReleaseSafe(ref_array
);
1432 CFReleaseSafe(item_query
);
1436 static bool SecItemRawUpdate(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
, CFErrorRef
*error
) {
1439 // Ensure the dictionary passed to securityd has proper kCFTypeDictionaryKeyCallBacks.
1440 CFMutableDictionaryRef tmp
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
1441 CFDictionaryForEach(attributesToUpdate
, ^(const void *key
, const void *value
) { CFDictionaryAddValue(tmp
, key
, value
); });
1442 ok
= gSecurityd
->sec_item_update(query
, tmp
, SecAccessGroupsGetCurrent(), error
);
1445 xpc_object_t message
= securityd_create_message(sec_item_update_id
, error
);
1447 if (SecXPCDictionarySetPList(message
, kSecXPCKeyQuery
, query
, error
) &&
1448 SecXPCDictionarySetPList(message
, kSecXPCKeyAttributesToUpdate
, attributesToUpdate
, error
)) {
1449 xpc_object_t reply
= securityd_message_with_reply_sync(message
, error
);
1451 ok
= securityd_message_no_error(reply
, error
);
1455 xpc_release(message
);
1461 static bool SecTokenItemUpdate(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
, CFErrorRef
*error
) {
1462 return SecTokenItemForEachMatching(query
, error
, ^bool(CFDictionaryRef object_data
, CFDictionaryRef item_query
, CFErrorRef
*error
) {
1464 CFDataRef object_id
= NULL
;
1465 CFMutableDictionaryRef db_value
= NULL
;
1467 // Update attributes on the token.
1468 CFMutableDictionaryRef attributes
= CFDictionaryCreateMutableCopy(NULL
, 0, attributesToUpdate
);
1469 require_quiet(object_id
= SecTokenCopyUpdatedObjectID(token
, CFDictionaryGetValue(object_data
, kSecTokenValueObjectIDKey
),
1470 attributes
, error
), out
);
1472 // Update attributes in the database.
1473 require_quiet(SecItemRawUpdate(item_query
, attributes
, error
), out
);
1478 CFReleaseSafe(object_id
);
1479 CFReleaseSafe(attributes
);
1480 CFReleaseSafe(db_value
);
1486 #if SECITEM_SHIM_OSX
1487 SecItemUpdate_ios(CFDictionaryRef inQuery
, CFDictionaryRef inAttributesToUpdate
)
1489 SecItemUpdate(CFDictionaryRef inQuery
, CFDictionaryRef inAttributesToUpdate
)
1490 #endif // *** END SECITEM_SHIM_OSX ***
1493 __block SecCFDictionaryCOW query
= { inQuery
};
1494 __block SecCFDictionaryCOW attributesToUpdate
= { inAttributesToUpdate
};
1496 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1497 return SecItemAuthDoQuery(&query
, &attributesToUpdate
, SecItemUpdate
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1498 if (token
== NULL
) {
1499 return SecItemRawUpdate(query
, attributes
, error
);
1501 return SecTokenItemUpdate(token
, query
, attributes
, error
);
1506 CFReleaseSafe(query
.mutable_dictionary
);
1507 CFReleaseSafe(attributesToUpdate
.mutable_dictionary
);
1511 #ifndef SECITEM_SHIM_OSX
1512 static OSStatus
explode_persistent_identity_ref(SecCFDictionaryCOW
*query
)
1514 OSStatus status
= errSecSuccess
;
1515 CFTypeRef persist
= CFDictionaryGetValue(query
->dictionary
, kSecValuePersistentRef
);
1517 if (persist
&& _SecItemParsePersistentRef(persist
, &class, NULL
)
1518 && CFEqual(class, kSecClassIdentity
)) {
1519 const void *keys
[] = { kSecReturnRef
, kSecValuePersistentRef
};
1520 const void *vals
[] = { kCFBooleanTrue
, persist
};
1521 CFDictionaryRef persistent_query
= CFDictionaryCreate(NULL
, keys
,
1522 vals
, (array_size(keys
)), NULL
, NULL
);
1523 CFTypeRef item_query
= NULL
;
1524 status
= SecItemCopyMatching(persistent_query
, &item_query
);
1525 CFReleaseNull(persistent_query
);
1529 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query
), kSecValuePersistentRef
);
1530 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query
), kSecValueRef
, item_query
);
1531 CFRelease(item_query
);
1539 #if SECITEM_SHIM_OSX
1540 SecItemDelete_ios(CFDictionaryRef inQuery
)
1542 SecItemDelete(CFDictionaryRef inQuery
)
1543 #endif // *** END SECITEM_SHIM_OSX ***
1546 __block SecCFDictionaryCOW query
= { inQuery
};
1548 os_activity_t trace_activity
= os_activity_start("SecItemDelete_ios", OS_ACTIVITY_FLAG_DEFAULT
);
1550 #ifndef SECITEM_SHIM_OSX
1551 require_noerr_quiet(status
= explode_persistent_identity_ref(&query
), errOut
);
1552 require_quiet(!explode_identity(query
.dictionary
, (secitem_operation
)SecItemDelete
, &status
, NULL
), errOut
);
1553 #endif // *** END SECITEM_SHIM_OSX ***
1555 status
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1556 return SecItemAuthDoQuery(&query
, NULL
, SecItemDelete
, error
, ^bool(TKTokenRef token
, CFDictionaryRef query
, CFDictionaryRef attributes
, CFDictionaryRef auth_params
, CFErrorRef
*error
) {
1557 if (token
== NULL
) {
1558 return SECURITYD_XPC(sec_item_delete
, dict_ag_to_error_request
, query
, SecAccessGroupsGetCurrent(), error
);
1560 return SecTokenItemForEachMatching(query
, error
, ^bool(CFDictionaryRef object_data
, CFDictionaryRef item_query
, CFErrorRef
*error
) {
1563 // Delete item from the token.
1564 CFDataRef object_id
= CFDictionaryGetValue(object_data
, kSecTokenValueObjectIDKey
);
1565 require_action_quiet(TKTokenDeleteObject(token
, object_id
, error
), out
,
1566 SecTokenProcessError(kAKSKeyOpDelete
, token
, object_id
, error
));
1568 // Delete the item from the keychain.
1569 require_quiet(SECURITYD_XPC(sec_item_delete
, dict_ag_to_error_request
, item_query
,
1570 SecAccessGroupsGetCurrent(), error
), out
);
1580 #ifndef SECITEM_SHIM_OSX
1584 CFReleaseSafe(query
.mutable_dictionary
);
1586 os_activity_end(trace_activity
);
1592 SecItemDeleteAll(void)
1594 return SecOSStatusWith(^bool (CFErrorRef
*error
) {
1598 #ifndef SECITEM_SHIM_OSX
1599 SecTrustStoreRef ts
= SecTrustStoreForDomain(kSecTrustStoreDomainUser
);
1600 if (!gSecurityd
->sec_truststore_remove_all(ts
, error
))
1601 ok
= SecError(errSecInternal
, error
, CFSTR("sec_truststore_remove_all is NULL"));
1602 #endif // *** END SECITEM_SHIM_OSX ***
1603 if (!gSecurityd
->sec_item_delete_all(error
))
1604 ok
= SecError(errSecInternal
, error
, CFSTR("sec_item_delete_all is NULL"));
1606 ok
= securityd_send_sync_and_do(sec_delete_all_id
, error
, NULL
, NULL
);
1613 CFArrayRef
_SecKeychainSyncUpdateMessage(CFDictionaryRef updates
, CFErrorRef
*error
) {
1614 __block CFArrayRef result
;
1615 os_activity_initiate("_SecKeychainSyncUpdateMessage", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1616 result
= SECURITYD_XPC(sec_keychain_sync_update_message
, dict_to_array_error_request
, updates
, error
);
1621 #ifndef SECITEM_SHIM_OSX
1622 OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
);
1624 OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
)
1626 return -1; /* this is only on OS X currently */
1631 extern OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
);
1635 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
1637 bool _SecKeychainRollKeys(bool force
, CFErrorRef
*error
)
1639 do_if_registered(sec_roll_keys
, force
, error
);
1641 __block
bool result
= false;
1643 secdebug("secitem","enter - %s", __FUNCTION__
);
1644 securityd_send_sync_and_do(kSecXPCOpRollKeys
, error
,
1645 ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1646 xpc_dictionary_set_bool(message
, "force", force
);
1649 ^bool(xpc_object_t response
, __unused CFErrorRef
*error
) {
1650 result
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);