]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecItem.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / sec / Security / SecItem.c
1 /*
2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * SecItem.c - CoreFoundation-based constants and functions for
26 access to Security items (certificates, keys, identities, and
27 passwords.)
28 */
29
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>
47 #include <errno.h>
48 #include <limits.h>
49 #include <sqlite3.h>
50 #include <stdint.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <sys/param.h>
54 #include <sys/stat.h>
55 #include <Security/SecBase.h>
56 #include <CoreFoundation/CFData.h>
57 #include <CoreFoundation/CFDate.h>
58 #include <CoreFoundation/CFDictionary.h>
59 #include <CoreFoundation/CFNumber.h>
60 #include <CoreFoundation/CFString.h>
61 #include <CoreFoundation/CFURL.h>
62 #include <CommonCrypto/CommonDigest.h>
63 #include <libkern/OSByteOrder.h>
64 #include <corecrypto/ccder.h>
65 #include <utilities/array_size.h>
66 #include <utilities/debugging.h>
67 #include <utilities/SecCFError.h>
68 #include <utilities/SecCFWrappers.h>
69 #include <utilities/SecIOFormat.h>
70 #include <utilities/SecXPCError.h>
71 #include <utilities/der_plist.h>
72 #include <utilities/der_plist_internal.h>
73 #include <assert.h>
74 #include <dlfcn.h>
75 #include <libaks_acl_cf_keys.h>
76 #include <os/activity.h>
77 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
78
79 #include <Security/SecInternal.h>
80 #include <TargetConditionals.h>
81 #include <ipc/securityd_client.h>
82 #include <Security/SecuritydXPC.h>
83 #include <AssertMacros.h>
84 #include <asl.h>
85 #include <sys/types.h>
86 #include <pwd.h>
87 #include <grp.h>
88 #include <unistd.h>
89 #ifndef SECITEM_SHIM_OSX
90 #include <libDER/asn1Types.h>
91 #endif // *** END SECITEM_SHIM_OSX ***
92
93 /* label when certificate data is joined with key data */
94 #define CERTIFICATE_DATA_COLUMN_LABEL "certdata"
95
96 #include <utilities/SecDb.h>
97 #include <IOKit/IOReturn.h>
98
99 #include <coreauthd_spi.h>
100 #include <LocalAuthentication/LAPrivateDefines.h>
101 #include <LocalAuthentication/LACFSupport.h>
102
103 #include <ctkclient.h>
104
105 /* Return an OSStatus for a sqlite3 error code. */
106 static OSStatus osstatus_for_s3e(int s3e)
107 {
108 if (s3e > 0 && s3e <= SQLITE_DONE) switch (s3e)
109 {
110 case SQLITE_OK:
111 return 0;
112 case SQLITE_ERROR:
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. */
116 case SQLITE_PERM:
117 case SQLITE_READONLY:
118 return errSecNotAvailable;
119 case SQLITE_CANTOPEN:
120 return errSecNotAvailable;
121 case SQLITE_EMPTY:
122 return errSecNotAvailable;
123 case SQLITE_CONSTRAINT:
124 return errSecDuplicateItem;
125 case SQLITE_ABORT:
126 return -1;
127 case SQLITE_MISMATCH:
128 return errSecNoSuchAttr;
129 case SQLITE_AUTH:
130 return errSecNotAvailable;
131 case SQLITE_NOMEM:
132 return -2; /* TODO: Replace with a real error code. */
133 case SQLITE_INTERNAL:
134 default:
135 return errSecNotAvailable; /* TODO: Replace with a real error code. */
136 }
137 return s3e;
138 }
139
140 static OSStatus osstatus_for_kern_return(CFIndex kernResult)
141 {
142 switch (kernResult)
143 {
144 case KERN_SUCCESS:
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;
154 case kIOReturnError:
155 return errSecDecode;
156 case kIOReturnBadArgument:
157 return errSecParam;
158 default:
159 return errSecNotAvailable; /* TODO: Replace with a real error code. */
160 }
161 }
162
163 static OSStatus osstatus_for_xpc_error(CFIndex xpcError) {
164 switch (xpcError)
165 {
166 case kSecXPCErrorSuccess:
167 return errSecSuccess;
168 case kSecXPCErrorUnexpectedType:
169 case kSecXPCErrorUnexpectedNull:
170 return errSecParam;
171 case kSecXPCErrorConnectionFailed:
172 return errSecNotAvailable;
173 case kSecXPCErrorUnknown:
174 default:
175 return errSecInternal;
176 }
177 }
178
179 static OSStatus osstatus_for_der_error(CFIndex derError) {
180 switch (derError)
181 {
182 case kSecDERErrorUnknownEncoding:
183 case kSecDERErrorUnsupportedDERType:
184 case kSecDERErrorUnsupportedNumberType:
185 return errSecDecode;
186 case kSecDERErrorUnsupportedCFObject:
187 return errSecParam;
188 case kSecDERErrorAllocationFailure:
189 return errSecAllocate;
190 default:
191 return errSecInternal;
192 }
193 }
194
195 static OSStatus osstatus_for_ids_error(CFIndex idsError) {
196 switch (idsError)
197 {
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 default:
207 return errSecInternal;
208 }
209 }
210
211 static OSStatus osstatus_for_localauthentication_error(CFIndex laError) {
212 // Wrap LA error in Sec error.
213 switch (laError) {
214 case kLAErrorUserCancel:
215 return errSecUserCanceled;
216 case kLAErrorParameter:
217 return errSecParam;
218 case kLAErrorNotInteractive:
219 return errSecInteractionNotAllowed;
220 default:
221 return errSecAuthFailed;
222 }
223 }
224
225 static OSStatus osstatus_for_ctk_error(CFIndex ctkError) {
226 // Hack, get rid of it once dep lands: <rdar://problem/21181736> Export error code constants from ctkclient.h header
227 #ifndef kTKErrorCodeNotImplemented
228 #define kTKErrorCodeNotImplemented -1
229 #endif
230 #ifndef kTKErrorCodeCanceledByUser
231 #define kTKErrorCodeCanceledByUser -4
232 #endif
233
234 switch (ctkError) {
235 case kTKErrorCodeBadParameter:
236 return errSecParam;
237 case kTKErrorCodeNotImplemented:
238 return errSecUnimplemented;
239 case kTKErrorCodeCanceledByUser:
240 return errSecUserCanceled;
241 default:
242 return errSecInternal;
243 }
244 }
245
246 // Convert from securityd error codes to OSStatus for legacy API.
247 OSStatus SecErrorGetOSStatus(CFErrorRef error) {
248 OSStatus status;
249 if (error == NULL) {
250 status = errSecSuccess;
251 } else {
252 CFStringRef domain = CFErrorGetDomain(error);
253 if (domain == NULL) {
254 secerror("No error domain for error: %@", error);
255 status = errSecInternal;
256 } else if (CFEqual(kSecErrorDomain, domain)) {
257 status = (OSStatus)CFErrorGetCode(error);
258 } else if (CFEqual(kSecDbErrorDomain, domain)) {
259 status = osstatus_for_s3e((int)CFErrorGetCode(error));
260 } else if (CFEqual(kSecErrnoDomain, domain)) {
261 status = (OSStatus)CFErrorGetCode(error);
262 } else if (CFEqual(kSecKernDomain, domain)) {
263 status = osstatus_for_kern_return(CFErrorGetCode(error));
264 } else if (CFEqual(sSecXPCErrorDomain, domain)) {
265 status = osstatus_for_xpc_error(CFErrorGetCode(error));
266 } else if (CFEqual(sSecDERErrorDomain, domain)) {
267 status = osstatus_for_der_error(CFErrorGetCode(error));
268 } else if (CFEqual(kSecIDSErrorDomain, domain)) {
269 status = osstatus_for_ids_error(CFErrorGetCode(error));
270 } else if (CFEqual(CFSTR(kLAErrorDomain), domain)) {
271 status = osstatus_for_localauthentication_error(CFErrorGetCode(error));
272 } else if (CFEqual(CFSTR(kTKErrorDomain), domain)) {
273 status = osstatus_for_ctk_error(CFErrorGetCode(error));
274 } else {
275 secnotice("securityd", "unknown error domain: %@ for error: %@", domain, error);
276 status = errSecInternal;
277 }
278 }
279 return status;
280 }
281
282 // Wrapper to provide a CFErrorRef for legacy API.
283 OSStatus SecOSStatusWith(bool (^perform)(CFErrorRef *error)) {
284 CFErrorRef error = NULL;
285 OSStatus status;
286 if (perform(&error)) {
287 assert(error == NULL);
288 status = errSecSuccess;
289 } else {
290 assert(error);
291 status = SecErrorGetOSStatus(error);
292 if (status != errSecItemNotFound) // Occurs in normal operation, so exclude
293 secerror("error:[%" PRIdOSStatus "] %@", status, error);
294 CFReleaseNull(error);
295 }
296 return status;
297 }
298
299 /* IPC uses CFPropertyList to un/marshall input/output data and can handle:
300 CFData, CFString, CFArray, CFDictionary, CFDate, CFBoolean, and CFNumber
301
302 Currently in need of conversion below:
303 @@@ kSecValueRef allows SecKeychainItemRef and SecIdentityRef
304 @@@ kSecMatchPolicy allows a query with a SecPolicyRef
305 @@@ kSecUseItemList allows a query against a list of itemrefs, this isn't
306 currently implemented at all, but when it is needs to short circuit to
307 local evaluation, different from the sql query abilities
308 */
309
310 #ifndef SECITEM_SHIM_OSX
311 static CFDictionaryRef
312 SecItemCopyAttributeDictionary(CFTypeRef ref) {
313 CFDictionaryRef refDictionary = NULL;
314 CFTypeID typeID = CFGetTypeID(ref);
315 if (typeID == SecKeyGetTypeID()) {
316 refDictionary = SecKeyCopyAttributeDictionary((SecKeyRef)ref);
317 } else if (typeID == SecCertificateGetTypeID()) {
318 refDictionary =
319 SecCertificateCopyAttributeDictionary((SecCertificateRef)ref);
320 } else if (typeID == SecIdentityGetTypeID()) {
321 assert(false);
322 SecIdentityRef identity = (SecIdentityRef)ref;
323 SecCertificateRef cert = NULL;
324 SecKeyRef key = NULL;
325 if (!SecIdentityCopyCertificate(identity, &cert) &&
326 !SecIdentityCopyPrivateKey(identity, &key))
327 {
328 CFDataRef data = SecCertificateCopyData(cert);
329 CFDictionaryRef key_dict = SecKeyCopyAttributeDictionary(key);
330
331 if (key_dict && data) {
332 refDictionary = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, key_dict);
333 CFDictionarySetValue((CFMutableDictionaryRef)refDictionary,
334 CFSTR(CERTIFICATE_DATA_COLUMN_LABEL), data);
335 }
336 CFReleaseNull(key_dict);
337 CFReleaseNull(data);
338 }
339 CFReleaseNull(cert);
340 CFReleaseNull(key);
341 } else {
342 refDictionary = NULL;
343 }
344 return refDictionary;
345 }
346
347 static CFTypeRef
348 SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes) {
349 CFTypeRef ref = NULL;
350 CFStringRef class = CFDictionaryGetValue(refAttributes, kSecClass);
351 if (CFEqual(class, kSecClassCertificate)) {
352 ref = SecCertificateCreateFromAttributeDictionary(refAttributes);
353 } else if (CFEqual(class, kSecClassKey)) {
354 ref = SecKeyCreateFromAttributeDictionary(refAttributes);
355 } else if (CFEqual(class, kSecClassIdentity)) {
356 CFAllocatorRef allocator = NULL;
357 CFDataRef data = CFDictionaryGetValue(refAttributes, CFSTR(CERTIFICATE_DATA_COLUMN_LABEL));
358 SecCertificateRef cert = SecCertificateCreateWithData(allocator, data);
359 SecKeyRef key = SecKeyCreateFromAttributeDictionary(refAttributes);
360 if (key && cert)
361 ref = SecIdentityCreate(allocator, cert, key);
362 CFReleaseSafe(cert);
363 CFReleaseSafe(key);
364 #if 0
365 /* We don't support SecKeychainItemRefs yet. */
366 } else if (CFEqual(class, kSecClassGenericPassword)) {
367 } else if (CFEqual(class, kSecClassInternetPassword)) {
368 } else if (CFEqual(class, kSecClassAppleSharePassword)) {
369 #endif
370 } else {
371 ref = NULL;
372 }
373 return ref;
374 }
375 #else
376
377 extern CFTypeRef SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes);
378
379 #endif
380
381 OSStatus
382 SecItemCopyDisplayNames(CFArrayRef items, CFArrayRef *displayNames)
383 {
384 // @@@ TBI
385 return -1 /* errSecUnimplemented */;
386 }
387
388 #ifndef SECITEM_SHIM_OSX
389 typedef OSStatus (*secitem_operation)(CFDictionaryRef attributes, CFTypeRef *result);
390
391 static bool explode_identity(CFDictionaryRef attributes, secitem_operation operation,
392 OSStatus *return_status, CFTypeRef *return_result)
393 {
394 bool handled = false;
395 CFTypeRef value = CFDictionaryGetValue(attributes, kSecValueRef);
396 if (value) {
397 CFTypeID typeID = CFGetTypeID(value);
398 if (typeID == SecIdentityGetTypeID()) {
399 handled = true;
400 OSStatus status = errSecSuccess;
401 SecIdentityRef identity = (SecIdentityRef)value;
402 SecCertificateRef cert = NULL;
403 SecKeyRef key = NULL;
404 if (!SecIdentityCopyCertificate(identity, &cert) &&
405 !SecIdentityCopyPrivateKey(identity, &key))
406 {
407 CFMutableDictionaryRef partial_query =
408 CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, attributes);
409 CFDictionarySetValue(partial_query, kSecValueRef, cert);
410 CFTypeRef result = NULL;
411 bool duplicate_cert = false;
412 /* an identity is first and foremost a key, but it can have multiple
413 certs associated with it: so we identify it by the cert */
414 status = operation(partial_query, return_result ? &result : NULL);
415 if ((operation == (secitem_operation)SecItemAdd) &&
416 (status == errSecDuplicateItem)) {
417 duplicate_cert = true;
418 status = errSecSuccess;
419 }
420
421 if (!status || status == errSecItemNotFound) {
422 bool skip_key_operation = false;
423
424 /* if the key is still in use, skip deleting it */
425 if (operation == (secitem_operation)SecItemDelete) {
426 // find certs with cert.pkhh == keys.klbl
427 CFDictionaryRef key_dict = NULL, query_dict = NULL;
428 CFDataRef pkhh = NULL;
429
430 key_dict = SecKeyCopyAttributeDictionary(key);
431 if (key_dict)
432 pkhh = (CFDataRef)CFDictionaryGetValue(key_dict, kSecAttrApplicationLabel);
433 const void *keys[] = { kSecClass, kSecAttrPublicKeyHash };
434 const void *vals[] = { kSecClassCertificate, pkhh };
435 if (pkhh)
436 query_dict = CFDictionaryCreate(NULL, keys,
437 vals, (array_size(keys)),
438 NULL, NULL);
439 if (query_dict)
440 if (errSecSuccess == SecItemCopyMatching(query_dict, NULL))
441 skip_key_operation = true;
442 CFReleaseSafe(query_dict);
443 CFReleaseSafe(key_dict);
444 }
445
446 if (!skip_key_operation) {
447 /* now perform the operation for the key */
448 CFDictionarySetValue(partial_query, kSecValueRef, key);
449 CFDictionarySetValue(partial_query, kSecReturnPersistentRef, kCFBooleanFalse);
450 status = operation(partial_query, NULL);
451 if ((operation == (secitem_operation)SecItemAdd) &&
452 (status == errSecDuplicateItem) &&
453 !duplicate_cert)
454 status = errSecSuccess;
455 }
456
457 /* add and copy matching for an identityref have a persistent ref result */
458 if (result) {
459 if (!status) {
460 /* result is a persistent ref to a cert */
461 sqlite_int64 rowid;
462 if (_SecItemParsePersistentRef(result, NULL, &rowid)) {
463 *return_result = _SecItemMakePersistentRef(kSecClassIdentity, rowid);
464 }
465 }
466 CFRelease(result);
467 }
468 }
469 CFReleaseNull(partial_query);
470 }
471 else
472 status = errSecInvalidItemRef;
473
474 CFReleaseNull(cert);
475 CFReleaseNull(key);
476 *return_status = status;
477 }
478 } else {
479 value = CFDictionaryGetValue(attributes, kSecClass);
480 if (value && CFEqual(kSecClassIdentity, value) &&
481 (operation == (secitem_operation)SecItemDelete)) {
482 CFMutableDictionaryRef dict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, attributes);
483 CFDictionaryRemoveValue(dict, kSecClass);
484 CFDictionarySetValue(dict, kSecClass, kSecClassCertificate);
485 OSStatus status = SecItemDelete(dict);
486 if (!status) {
487 CFDictionarySetValue(dict, kSecClass, kSecClassKey);
488 status = SecItemDelete(dict);
489 }
490 CFRelease(dict);
491 *return_status = status;
492 handled = true;
493 }
494 }
495 return handled;
496 }
497
498 static void infer_cert_label(SecCFDictionaryCOW *attributes)
499 {
500 if (!CFDictionaryContainsKey(attributes->dictionary, kSecAttrLabel)) {
501 CFTypeRef value_ref = CFDictionaryGetValue(attributes->dictionary, kSecValueRef);
502 if (value_ref && CFGetTypeID(value_ref) == SecCertificateGetTypeID()) {
503 SecCertificateRef certificate = (SecCertificateRef)value_ref;
504 CFStringRef label = SecCertificateCopySubjectSummary(certificate);
505 if (label) {
506 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes), kSecAttrLabel, label);
507 CFReleaseNull(label);
508 }
509 }
510 }
511 }
512
513 /* A persistent ref is just the class and the rowid of the record. */
514 CF_RETURNS_RETAINED CFDataRef _SecItemMakePersistentRef(CFTypeRef class, sqlite_int64 rowid)
515 {
516 uint8_t bytes[sizeof(sqlite_int64) + 4];
517 if (rowid < 0)
518 return NULL;
519 if (CFStringGetCString(class, (char *)bytes, 4 + 1 /*null-term*/,
520 kCFStringEncodingUTF8))
521 {
522 OSWriteBigInt64(bytes + 4, 0, rowid);
523 return CFDataCreate(NULL, bytes, sizeof(bytes));
524 }
525 return NULL;
526 }
527
528 /* AUDIT[securityd](done):
529 persistent_ref (ok) is a caller provided, non NULL CFTypeRef.
530 */
531 bool _SecItemParsePersistentRef(CFDataRef persistent_ref, CFStringRef *return_class, sqlite_int64 *return_rowid)
532 {
533 bool valid_ref = false;
534 if (CFGetTypeID(persistent_ref) == CFDataGetTypeID() &&
535 CFDataGetLength(persistent_ref) == (CFIndex)(sizeof(sqlite_int64) + 4)) {
536 const uint8_t *bytes = CFDataGetBytePtr(persistent_ref);
537 sqlite_int64 rowid = OSReadBigInt64(bytes + 4, 0);
538
539 CFStringRef class = CFStringCreateWithBytes(kCFAllocatorDefault,
540 bytes, CFStringGetLength(kSecClassGenericPassword),
541 kCFStringEncodingUTF8, true);
542 const void *valid_classes[] = { kSecClassGenericPassword,
543 kSecClassInternetPassword,
544 kSecClassAppleSharePassword,
545 kSecClassCertificate,
546 kSecClassKey,
547 kSecClassIdentity };
548
549 unsigned i;
550 for (i=0; i< array_size(valid_classes); i++) {
551 if (CFEqual(valid_classes[i], class)) {
552 if (return_class)
553 *return_class = valid_classes[i];
554 if (return_rowid)
555 *return_rowid = rowid;
556 valid_ref = true;
557 break;
558 }
559 }
560 CFRelease(class);
561 }
562 return valid_ref;
563 }
564
565 #endif // *** END SECITEM_SHIM_OSX ***
566
567 static bool cf_bool_value(CFTypeRef cf_bool)
568 {
569 return (cf_bool && CFEqual(kCFBooleanTrue, cf_bool));
570 }
571
572 CFMutableDictionaryRef SecCFDictionaryCOWGetMutable(SecCFDictionaryCOW *cow_dictionary) {
573 if (cow_dictionary->mutable_dictionary == NULL) {
574 cow_dictionary->mutable_dictionary = CFDictionaryCreateMutableForCFTypes(NULL);
575 if (cow_dictionary->dictionary != NULL) {
576 CFDictionaryForEach(cow_dictionary->dictionary, ^(const void *key, const void *value) {
577 CFDictionarySetValue(cow_dictionary->mutable_dictionary, key, value);
578 });
579 }
580 cow_dictionary->dictionary = cow_dictionary->mutable_dictionary;
581 }
582
583 return cow_dictionary->mutable_dictionary;
584 }
585
586 // Keys for dictionary of kSecvalueData of token-based items.
587 static const CFStringRef kSecTokenValueObjectIDKey = CFSTR("oid");
588 static const CFStringRef kSecTokenValueAccessControlKey = CFSTR("ac");
589 static const CFStringRef kSecTokenValueDataKey = CFSTR("data");
590
591 // Creates kSecValueData field stored in the DB for token-based items. Data field consists of objectID, real
592 // access_control and optionally of the data value.
593 static CFDataRef SecTokenItemValueCreate(CFDataRef oid, CFDataRef access_control, CFDataRef object_value, CFErrorRef *error) {
594 CFMutableDictionaryRef value = NULL;
595 value = CFDictionaryCreateMutableForCFTypesWith(NULL,
596 kSecTokenValueObjectIDKey, oid,
597 kSecTokenValueAccessControlKey, access_control,
598 NULL);
599 if (object_value != NULL) {
600 CFDictionarySetValue(value, kSecTokenValueDataKey, object_value);
601 }
602
603 CFDataRef value_data = CFPropertyListCreateDERData(NULL, value, error);
604 CFRelease(value);
605 return value_data;
606 }
607
608 static CFDictionaryRef SecTokenItemValueCopy(CFDataRef db_value, CFErrorRef *error) {
609 CFPropertyListRef plist = NULL;
610 const uint8_t *der = CFDataGetBytePtr(db_value);
611 const uint8_t *der_end = der + CFDataGetLength(db_value);
612 require_quiet(der = der_decode_plist(0, kCFPropertyListImmutable, &plist, error, der, der_end), out);
613 require_action_quiet(der == der_end, out, SecError(errSecDecode, error, CFSTR("trailing garbage at end of token data field")));
614 require_action_quiet(CFDictionaryGetValue(plist, kSecTokenValueObjectIDKey) != NULL, out,
615 SecError(errSecInternal, error, CFSTR("token based item data does not have OID")));
616
617 out:
618 return plist;
619 }
620
621 TKTokenRef SecTokenCreate(CFStringRef token_id, CFDictionaryRef auth_params, CFErrorRef *error) {
622 CFMutableDictionaryRef token_attrs = NULL;
623 TKTokenRef token = NULL;
624 token_attrs = (auth_params != NULL) ?
625 CFDictionaryCreateMutableCopy(NULL, 0, auth_params) :
626 CFDictionaryCreateMutableForCFTypes(NULL);
627 CFDictionarySetValue(token_attrs, kSecAttrTokenID, token_id);
628
629 CFDictionaryRemoveValue(token_attrs, kSecUseAuthenticationContext);
630 token = TKTokenCreate(token_attrs, error);
631
632 CFReleaseSafe(token_attrs);
633 return token;
634 }
635
636 static bool SecTokenItemCreateFromAttributes(CFDictionaryRef attributes, CFDictionaryRef auth_params,
637 TKTokenRef token, CFDataRef object_id, CFTypeRef *ref, CFErrorRef *error) {
638 bool ok = false;
639 CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, attributes);
640 CFTypeRef token_id = CFDictionaryGetValue(attributes, kSecAttrTokenID);
641 if (token_id != NULL) {
642 if (CFRetainSafe(token) == NULL) {
643 require_quiet(token = SecTokenCreate(token_id, auth_params, error), out);
644 }
645
646 if (auth_params != NULL) {
647 CFDictionaryForEach(auth_params, ^(const void *key, const void *value) {
648 CFDictionarySetValue(attrs, key, value);
649 });
650 }
651 CFDictionarySetValue(attrs, kSecUseToken, token);
652 CFDictionarySetValue(attrs, kSecUseTokenObjectID, object_id);
653 CFRelease(token);
654 }
655 *ref = SecItemCreateFromAttributeDictionary(attrs);
656 ok = true;
657
658 out:
659 CFReleaseSafe(attrs);
660 return ok;
661 }
662
663
664 /* Turn the returned single value or dictionary that contains all the attributes to create a
665 ref into the exact result the client asked for */
666 static bool SecItemResultCopyPrepared(CFTypeRef raw_result, TKTokenRef token,
667 CFDictionaryRef query, CFDictionaryRef auth_params,
668 CFTypeRef *result, CFErrorRef *error) {
669 bool ok = false;
670 CFDataRef ac_data = NULL;
671 CFDataRef value = NULL;
672 CFTypeRef persistent_ref = NULL;
673 CFStringRef token_id = NULL;
674 CFDataRef object_id = NULL;
675 CFMutableDictionaryRef attrs = NULL;
676
677 bool wants_ref = cf_bool_value(CFDictionaryGetValue(query, kSecReturnRef));
678 bool wants_data = cf_bool_value(CFDictionaryGetValue(query, kSecReturnData));
679 bool wants_attributes = cf_bool_value(CFDictionaryGetValue(query, kSecReturnAttributes));
680 bool wants_persistent_ref = cf_bool_value(CFDictionaryGetValue(query, kSecReturnPersistentRef));
681
682 // Get token value if not provided by the caller.
683 bool token_item = false;
684 if (token == NULL) {
685 if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID()) {
686 token_id = CFDictionaryGetValue(raw_result, kSecAttrTokenID);
687 token_item = (token_id != NULL);
688 }
689 } else {
690 token_item = true;
691 CFRetain(token);
692 }
693
694 // Decode and prepare data value, if it is requested at the output, or if we want attributes from token.
695 if (wants_data || wants_ref || (token_item && wants_attributes)) {
696 if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID())
697 value = CFRetainSafe(CFDictionaryGetValue(raw_result, kSecValueData));
698 else
699 value = CFRetainSafe(raw_result);
700 if (token_item && value != NULL) {
701 // Parse token-based item's data field.
702 CFDataRef object_value = NULL;
703 CFDictionaryRef parsed_value = NULL;
704 require_quiet(parsed_value = SecTokenItemValueCopy(value, error), out);
705 object_id = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueObjectIDKey));
706 ac_data = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueAccessControlKey));
707 object_value = CFRetainSafe(CFDictionaryGetValue(parsed_value, kSecTokenValueDataKey));
708 CFRelease(parsed_value);
709 if (wants_data && object_value == NULL) {
710 // Retrieve value directly from the token.
711 if (token == NULL) {
712 require_quiet(token = SecTokenCreate(token_id, auth_params, error), out);
713 }
714 require_quiet(object_value = TKTokenCopyObjectData(token, object_id, error), out);
715 if (CFEqual(object_value, kCFNull))
716 CFReleaseNull(object_value);
717 }
718 CFAssignRetained(value, object_value);
719 }
720
721 // If only thing requested is data, return them directly.
722 if (!(wants_attributes || wants_persistent_ref || wants_ref)) {
723 *result = CFRetainSafe(value);
724 ok = true;
725 goto out;
726 }
727 }
728
729 // Extract persistent_ref, if caller wants it.
730 if (wants_persistent_ref) {
731 if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID())
732 persistent_ref = CFRetainSafe(CFDictionaryGetValue(raw_result, kSecValuePersistentRef));
733 else
734 persistent_ref = CFRetainSafe(raw_result);
735
736 // If only thing requested is persistentref, extract it from dictionary if needed and return it.
737 if (!(wants_attributes || wants_data || wants_ref)) {
738 *result = CFRetainSafe(persistent_ref);
739 ok = true;
740 goto out;
741 }
742 }
743
744 if (wants_ref || wants_attributes || (wants_data && wants_persistent_ref)) {
745 // For these cases we need output dictionary.
746 if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID())
747 *result = CFDictionaryCreateMutableCopy(NULL, 0, raw_result);
748 else
749 *result = CFDictionaryCreateForCFTypes(NULL, NULL);
750 CFMutableDictionaryRef output = (CFMutableDictionaryRef)*result;
751
752 if ((wants_data || wants_ref) && value != NULL)
753 CFDictionarySetValue(output, kSecValueData, value);
754 else
755 CFDictionaryRemoveValue(output, kSecValueData);
756
757 if (wants_persistent_ref && persistent_ref != NULL)
758 CFDictionarySetValue(output, kSecValuePersistentRef, persistent_ref);
759 else
760 CFDictionaryRemoveValue(output, kSecValuePersistentRef);
761
762 if (wants_ref) {
763 CFTypeRef ref;
764 require_quiet(SecTokenItemCreateFromAttributes(output, auth_params, token, object_id, &ref, error), out);
765 if (!(wants_attributes || wants_data || wants_persistent_ref)) {
766 CFAssignRetained(*result, ref);
767 } else if (ref != NULL) {
768 CFDictionarySetValue(output, kSecValueRef, ref);
769 CFRelease(ref);
770 if (!wants_data) {
771 // We could have stored data value previously to make ref creation succeed.
772 // They are not needed any more and in case that caller did not want the data, avoid returning them.
773 CFDictionaryRemoveValue(output, kSecValueData);
774 }
775 }
776 }
777
778 if (wants_attributes) {
779 // Convert serialized form of access control to object form.
780 if (!token_item) {
781 CFRetainAssign(ac_data, CFDictionaryGetValue(output, kSecAttrAccessControl));
782 }
783
784 if (ac_data != NULL) {
785 SecAccessControlRef ac;
786 require_quiet(ac = SecAccessControlCreateFromData(kCFAllocatorDefault, ac_data, error), out);
787 CFDictionarySetValue(output, kSecAttrAccessControl, ac);
788 CFRelease(ac);
789 }
790 }
791 } else {
792 *result = NULL;
793 }
794
795 ok = true;
796
797 out:
798 CFReleaseSafe(ac_data);
799 CFReleaseSafe(value);
800 CFReleaseSafe(persistent_ref);
801 CFReleaseSafe(object_id);
802 CFReleaseSafe(attrs);
803 CFReleaseSafe(token);
804 return ok;
805 }
806
807 static bool SecItemResultProcess(CFDictionaryRef query, CFDictionaryRef auth_params, TKTokenRef token,
808 CFTypeRef raw_result, CFTypeRef *result, CFErrorRef *error) {
809 bool ok = false;
810 require_action_quiet(raw_result != NULL, out, ok = true);
811 require_action_quiet(result != NULL, out, ok = true);
812
813 if (CFGetTypeID(raw_result) == CFArrayGetTypeID()) {
814 CFIndex i, count = CFArrayGetCount(raw_result);
815 *result = CFArrayCreateMutableForCFTypes(NULL);
816 for (i = 0; i < count; i++) {
817 CFTypeRef ref;
818 require_quiet(SecItemResultCopyPrepared(CFArrayGetValueAtIndex(raw_result, i),
819 token, query, auth_params, &ref, error), out);
820 if (ref != NULL) {
821 CFArrayAppendValue((CFMutableArrayRef)*result, ref);
822 CFRelease(ref);
823 }
824 }
825 } else {
826 require_quiet(SecItemResultCopyPrepared(raw_result, token, query, auth_params, result, error), out);
827 }
828
829 ok = true;
830
831 out:
832 return ok;
833 }
834
835 static bool SecItemAttributesPrepare(SecCFDictionaryCOW *attrs, CFErrorRef *error) {
836 bool ok = false;
837 CFDataRef ac_data = NULL, acm_context = NULL;
838 void *la_lib = NULL;
839
840 SecAccessControlRef access_control = (SecAccessControlRef)CFDictionaryGetValue(attrs->dictionary, kSecAttrAccessControl);
841 if (access_control != NULL) {
842 require_quiet(ac_data = SecAccessControlCopyData(access_control), out);
843 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecAttrAccessControl, ac_data);
844 }
845
846 const CFTypeRef la_context = CFDictionaryGetValue(attrs->dictionary, kSecUseAuthenticationContext);
847 if (la_context) {
848 require_action_quiet(!CFDictionaryContainsKey(attrs->dictionary, kSecUseCredentialReference), out,
849 SecError(errSecParam, error, CFSTR("kSecUseAuthenticationContext cannot be used together with kSecUseCredentialReference")));
850 require_action_quiet(la_lib = dlopen("/System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication", RTLD_LAZY), out,
851 SecError(errSecInternal, error, CFSTR("failed to open LocalAuthentication.framework")));
852 LAFunctionCopyExternalizedContext fnCopyExternalizedContext = NULL;
853 require_action_quiet(fnCopyExternalizedContext = dlsym(la_lib, "LACopyExternalizedContext"), out,
854 SecError(errSecInternal, error, CFSTR("failed to obtain LACopyExternalizedContext")));
855 require_action_quiet(acm_context = fnCopyExternalizedContext(la_context), out,
856 SecError(errSecInternal, error, CFSTR("failed to get ACM handle from LAContext")));
857 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseAuthenticationContext);
858 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseCredentialReference, acm_context);
859 }
860
861 #ifndef SECITEM_SHIM_OSX
862 // If a ref was specified we get it's attribute dictionary and parse it.
863 CFTypeRef value = CFDictionaryGetValue(attrs->dictionary, kSecValueRef);
864 if (value) {
865 CFDictionaryRef ref_attributes;
866 require_action_quiet(ref_attributes = SecItemCopyAttributeDictionary(value), out,
867 SecError(errSecValueRefUnsupported, error, CFSTR("unsupported kSecValueRef in query")));
868
869 /* Replace any attributes we already got from the ref with the ones
870 from the attributes dictionary the caller passed us. This allows
871 a caller to add an item using attributes from the ref and still
872 override some of them in the dictionary directly. */
873 CFMutableDictionaryRef new_query = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, ref_attributes);
874 CFRelease(ref_attributes);
875 CFDictionaryForEach(attrs->dictionary, ^(const void *key, const void *value) {
876 if (!CFEqual(key, kSecValueRef))
877 CFDictionarySetValue(new_query, key, value);
878 });
879 CFAssignRetained(attrs->mutable_dictionary, new_query);
880 attrs->dictionary = attrs->mutable_dictionary;
881 }
882 value = CFDictionaryGetValue(attrs->dictionary, kSecAttrIssuer);
883 if (value) {
884 /* convert DN to canonical issuer, if value is DN (top level sequence) */
885 const DERItem name = { (unsigned char *)CFDataGetBytePtr(value), CFDataGetLength(value) };
886 DERDecodedInfo content;
887 if (!DERDecodeItem(&name, &content) &&
888 (content.tag == ASN1_CONSTR_SEQUENCE))
889 {
890 CFDataRef canonical_issuer = createNormalizedX501Name(kCFAllocatorDefault, &content.content);
891 if (canonical_issuer) {
892 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecAttrIssuer, canonical_issuer);
893 CFRelease(canonical_issuer);
894 }
895 }
896 }
897 #endif
898
899 ok = true;
900
901 out:
902 if (la_lib != NULL) {
903 dlclose(la_lib);
904 }
905 CFReleaseSafe(ac_data);
906 CFReleaseSafe(acm_context);
907 return ok;
908 }
909
910 static bool SecItemAuthMaxAttemptsReached(CFArrayRef ac_pairs, CFErrorRef *error)
911 {
912 CFMutableStringRef log_string = CFStringCreateMutable(kCFAllocatorDefault, 0);
913 CFArrayRef ac_pair;
914 CFArrayForEachC(ac_pairs, ac_pair) {
915 CFStringRef acl_hex_string = CFDataCopyHexString(CFArrayGetValueAtIndex(ac_pair, 0));
916 CFStringRef str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("operation: %@ acl:%@\n"), CFArrayGetValueAtIndex(ac_pair, 1), acl_hex_string);
917 CFStringAppend(log_string, str);
918 CFRelease(acl_hex_string);
919 CFRelease(str);
920 }
921
922 CFStringRef reason = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("Reached maximum count of authentication attempts\n %@"), log_string);
923 SecError(errSecAuthFailed, error, reason);
924 __security_simulatecrash(reason, __sec_exception_code_AuthLoop);
925
926 CFRelease(reason);
927 CFRelease(log_string);
928 return false;
929 }
930
931 bool SecItemAuthDo(SecCFDictionaryCOW *auth_params, CFErrorRef *error, SecItemAuthResult (^perform)(CFDictionaryRef auth_params, CFArrayRef *ac_pairs, CFErrorRef *error)) {
932 bool ok = false;
933 CFArrayRef ac_pairs = NULL;
934 SecCFDictionaryCOW auth_options = { NULL };
935
936 for (uint32_t i = 0;; ++i) {
937 // If the operation succeeded or failed with other than auth-needed error, just leave.
938 SecItemAuthResult auth_result = perform(auth_params->dictionary, &ac_pairs, error);
939 require_quiet(auth_result != kSecItemAuthResultError, out);
940 require_action_quiet(auth_result == kSecItemAuthResultNeedAuth, out, ok = true);
941
942 // If auth_params were not created up to now, do create them because we will definitely need them.
943 SecCFDictionaryCOWGetMutable(auth_params);
944
945 // Retrieve or create authentication handle and/or ACM context.
946 CFTypeRef auth_handle = CFDictionaryGetValue(auth_params->dictionary, kSecUseAuthenticationContext);
947 if (auth_handle == NULL) {
948 CFDataRef acm_context = CFDictionaryGetValue(auth_params->dictionary, kSecUseCredentialReference);
949 require_quiet(auth_handle = LACreateNewContextWithACMContext(acm_context, error), out);
950 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseAuthenticationContext, auth_handle);
951 CFRelease(auth_handle);
952 if (acm_context == NULL) {
953 require_quiet(acm_context = LACopyACMContext(auth_handle, error), out);
954 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseCredentialReference, acm_context);
955 CFRelease(acm_context);
956 }
957 }
958
959 // Throttle max authentication attempts. This is mainly protection against exceptional states, not ordinary
960 // user retry limit.
961 require_action(i < 20, out, SecItemAuthMaxAttemptsReached(ac_pairs, error));
962
963 // Prepare auth options dictionary.
964 if (auth_options.dictionary == NULL) {
965 CFStringRef operation_prompt = CFDictionaryGetValue(auth_params->dictionary, kSecUseOperationPrompt);
966 if (operation_prompt != NULL) {
967 CFNumberRef key = CFNumberCreateWithCFIndex(NULL, kLAOptionAuthenticationReason);
968 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options), key, operation_prompt);
969 CFRelease(key);
970 }
971
972 CFTypeRef auth_ui = CFDictionaryGetValue(auth_params->dictionary, kSecUseAuthenticationUI);
973 if (CFEqualSafe(auth_ui, kSecUseAuthenticationUIFail)) {
974 CFNumberRef key = CFNumberCreateWithCFIndex(NULL, kLAOptionNotInteractive);
975 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&auth_options), key, kCFBooleanTrue);
976 CFRelease(key);
977 }
978 }
979
980 // Go through all access_control-operation pairs and evaluate them.
981 CFArrayRef ac_pair;
982 CFArrayForEachC(ac_pairs, ac_pair) {
983 CFDataRef updated_acl = NULL;
984 require_quiet(LAEvaluateAndUpdateACL(auth_handle,
985 CFArrayGetValueAtIndex(ac_pair, 0), CFArrayGetValueAtIndex(ac_pair, 1),
986 auth_options.dictionary, &updated_acl, error), out);
987
988 if (updated_acl || CFEqual(CFArrayGetValueAtIndex(ac_pair, 1), CFSTR(""))) {
989 // we assume that only one ACL can be modified during ItemAdd or ItemUpdate
990 SecAccessControlRef ac = NULL;
991 require(ac = SecAccessControlCreateFromData(kCFAllocatorDefault,
992 updated_acl ? updated_acl : CFArrayGetValueAtIndex(ac_pair, 0), error), out);
993 SecAccessControlSetBound(ac, true);
994 CFAssignRetained(updated_acl, SecAccessControlCopyData(ac));
995 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecAttrAccessControl, updated_acl);
996 CFRelease(updated_acl);
997 CFRelease(ac);
998 }
999 }
1000 }
1001
1002 ok = true;
1003
1004 out:
1005 CFReleaseSafe(auth_options.mutable_dictionary);
1006 CFReleaseSafe(ac_pairs);
1007 return ok;
1008 }
1009
1010 void SecItemAuthCopyParams(SecCFDictionaryCOW *auth_params, SecCFDictionaryCOW *query) {
1011 // Store operation prompt.
1012 CFStringRef operation_prompt = CFDictionaryGetValue(query->dictionary, kSecUseOperationPrompt);
1013 if (operation_prompt != NULL) {
1014 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseOperationPrompt, operation_prompt);
1015 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecUseOperationPrompt);
1016 }
1017
1018 // Find out whether we are allowed to pop up a UI.
1019 CFTypeRef auth_ui = CFDictionaryGetValue(query->dictionary, kSecUseAuthenticationUI) ?:
1020 (CFEqualSafe(CFDictionaryGetValue(query->dictionary, kSecUseNoAuthenticationUI), kCFBooleanTrue) ?
1021 kSecUseAuthenticationUIFail : kSecUseAuthenticationUIAllow);
1022 if (!CFEqual(auth_ui, kSecUseAuthenticationUISkip) || CFDictionaryGetValue(query->dictionary, kSecUseNoAuthenticationUI)) {
1023 if (CFDictionaryContainsKey(query->dictionary, kSecUseNoAuthenticationUI))
1024 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecUseNoAuthenticationUI);
1025 if (!CFEqualSafe(auth_ui, kSecUseAuthenticationUISkip) && CFDictionaryContainsKey(query->dictionary, kSecUseAuthenticationUI))
1026 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecUseAuthenticationUI);
1027 }
1028
1029 if (!CFEqual(auth_ui, kSecUseAuthenticationUIAllow)) {
1030 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseAuthenticationUI, auth_ui);
1031 }
1032
1033 CFDataRef acm_context = CFDictionaryGetValue(query->dictionary, kSecUseCredentialReference);
1034 if (acm_context != NULL) {
1035 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseCredentialReference, acm_context);
1036 }
1037 }
1038
1039 static SecItemAuthResult SecItemCreatePairsFromError(CFErrorRef *error, CFArrayRef *ac_pairs)
1040 {
1041 if (error && CFErrorGetCode(*error) == errSecAuthNeeded && CFEqualSafe(CFErrorGetDomain(*error), kSecErrorDomain)) {
1042 // Extract ACLs to be verified from the error.
1043 CFDictionaryRef user_info = CFErrorCopyUserInfo(*error);
1044 CFNumberRef key = CFNumberCreateWithCFIndex(NULL, errSecAuthNeeded);
1045 CFRetainAssign(*ac_pairs, CFDictionaryGetValue(user_info, key));
1046 if (*ac_pairs == NULL)
1047 CFAssignRetained(*ac_pairs, CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
1048
1049 CFRelease(key);
1050 CFRelease(user_info);
1051 CFReleaseNull(*error);
1052 return kSecItemAuthResultNeedAuth;
1053 }
1054 return kSecItemAuthResultError;
1055 }
1056
1057 // Wrapper to handle automatic authentication and token/secd case switching.
1058 static bool SecItemAuthDoQuery(SecCFDictionaryCOW *query, SecCFDictionaryCOW *attributes, const void *secItemOperation, CFErrorRef *error,
1059 bool (^perform)(TKTokenRef token, CFDictionaryRef query, CFDictionaryRef attributes, CFDictionaryRef auth_params, CFErrorRef *error)) {
1060 bool ok = false;
1061 SecCFDictionaryCOW auth_params = { NULL };
1062 SecAccessControlRef access_control = NULL;
1063 __block TKTokenRef token = NULL;
1064
1065 if (secItemOperation == SecItemAdd || secItemOperation == SecItemUpdate) {
1066 CFDictionaryRef dict = attributes ? attributes->dictionary : query->dictionary;
1067 access_control = (SecAccessControlRef)CFDictionaryGetValue(dict, kSecAttrAccessControl);
1068 if (access_control && SecAccessControlGetConstraints(access_control) &&
1069 CFEqualSafe(CFDictionaryGetValue(dict, kSecAttrSynchronizable), kCFBooleanTrue))
1070 require_quiet(SecError(errSecParam, error, CFSTR("item with kSecAttrAccessControl is not synchronizable")), out);
1071 }
1072
1073 // Perform initial surgery on query/attributes (resolve LAContext to serialized ACM handle, resolve
1074 // SecAccessControlRef to serialized forms, expand kSecValueRef etc.)
1075 require_quiet(SecItemAttributesPrepare(query, error), out);
1076 if (attributes != NULL)
1077 require_quiet(SecItemAttributesPrepare(attributes, error), out);
1078
1079 // Populate auth_params dictionary according to initial query contents.
1080 SecItemAuthCopyParams(&auth_params, query);
1081
1082 if (secItemOperation != SecItemCopyMatching) {
1083 // UISkip is allowed only for CopyMatching.
1084 require_action_quiet(!CFEqualSafe(CFDictionaryGetValue(query->dictionary, kSecUseAuthenticationUI), kSecUseAuthenticationUISkip), out,
1085 SecError(errSecParam, error,
1086 CFSTR("kSecUseAuthenticationUISkip is allowed only for SecItemCopyMatching")));
1087 }
1088
1089 ok = SecItemAuthDo(&auth_params, error, ^SecItemAuthResult(CFDictionaryRef auth_params, CFArrayRef *ac_pairs, CFErrorRef *error) {
1090 SecItemAuthResult result = kSecItemAuthResultError;
1091
1092 // Propagate actual credential reference to the query.
1093 if (auth_params != NULL) {
1094 CFDataRef acm_context = CFDictionaryGetValue(auth_params, kSecUseCredentialReference);
1095 if (acm_context != NULL) {
1096 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query), kSecUseCredentialReference, acm_context);
1097 }
1098
1099 CFDataRef acl_data_ref = CFDictionaryGetValue(auth_params, kSecAttrAccessControl);
1100 if (acl_data_ref != NULL) {
1101 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes ?: query), kSecAttrAccessControl, acl_data_ref);
1102 }
1103 }
1104
1105 // Prepare connection to target token if it is present.
1106 CFStringRef token_id = CFDictionaryGetValue(query->dictionary, kSecAttrTokenID);
1107 if (secItemOperation != SecItemCopyMatching && token_id != NULL) {
1108 require_quiet(CFAssignRetained(token, SecTokenCreate(token_id, auth_params, error)), out);
1109 }
1110
1111 CFDictionaryRef attrs = (attributes != NULL) ? attributes->dictionary : NULL;
1112 if(!perform(token, query->dictionary, attrs, auth_params, error)) {
1113 require_quiet((result = SecItemCreatePairsFromError(error, ac_pairs)) == kSecItemAuthResultOK, out);
1114 }
1115
1116 result = kSecItemAuthResultOK;
1117
1118 out:
1119 return result;
1120 });
1121 require_quiet(ok, out);
1122
1123 ok = true;
1124
1125 out:
1126 CFReleaseSafe(token);
1127 CFReleaseSafe(auth_params.mutable_dictionary);
1128 return ok;
1129 }
1130
1131 #if SECITEM_SHIM_OSX
1132 /* TODO: Should be in some header */
1133 OSStatus SecItemAdd_ios(CFDictionaryRef attributes, CFTypeRef *result);
1134 OSStatus SecItemCopyMatching_ios(CFDictionaryRef query, CFTypeRef *result);
1135 OSStatus SecItemUpdate_ios(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
1136 OSStatus SecItemDelete_ios(CFDictionaryRef query);
1137 #endif
1138
1139 static bool cftype_to_bool_cftype_error_request(enum SecXPCOperation op, CFTypeRef attributes, CFTypeRef *result, CFErrorRef *error)
1140 {
1141 return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
1142 return SecXPCDictionarySetPList(message, kSecXPCKeyQuery, attributes, error);
1143 }, ^bool(xpc_object_t response, CFErrorRef *error) {
1144 if (result) {
1145 return SecXPCDictionaryCopyPListOptional(response, kSecXPCKeyResult, result, error);
1146 }
1147 return true;
1148 });
1149 }
1150
1151 static CFArrayRef dict_to_array_error_request(enum SecXPCOperation op, CFDictionaryRef attributes, CFErrorRef *error)
1152 {
1153 CFArrayRef result = NULL;
1154 bool success = cftype_to_bool_cftype_error_request(op, attributes, (CFTypeRef*)&result, error);
1155 if(success && !isArray(result)){
1156 SecError(errSecUnimplemented, error, CFSTR("Unexpected nonarray returned: %@"), result);
1157 CFReleaseNull(result);
1158 }
1159 return result;
1160 }
1161
1162 bool cftype_ag_to_bool_cftype_error_request(enum SecXPCOperation op, CFTypeRef attributes, __unused CFArrayRef accessGroups, CFTypeRef *result, CFErrorRef *error) {
1163 return cftype_to_bool_cftype_error_request(op, attributes, result, error);
1164 }
1165
1166 static bool dict_to_error_request(enum SecXPCOperation op, CFDictionaryRef query, CFErrorRef *error)
1167 {
1168 return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
1169 return SecXPCDictionarySetPList(message, kSecXPCKeyQuery, query, error);
1170 }, NULL);
1171 }
1172
1173 static bool dict_ag_to_error_request(enum SecXPCOperation op, CFDictionaryRef query, __unused CFArrayRef accessGroups, CFErrorRef *error)
1174 {
1175 return dict_to_error_request(op, query, error);
1176 }
1177
1178 static bool SecTokenCreateAccessControlError(CFStringRef operation, CFDataRef access_control, CFErrorRef *error) {
1179 CFArrayRef ac_pair = CFArrayCreateForCFTypes(NULL, access_control, operation, NULL);
1180 const void *ac_pairs[] = { CFArrayCreateForCFTypes(NULL, ac_pair, NULL) };
1181 const void *keys[] = { CFNumberCreateWithCFIndex(NULL, errSecAuthNeeded) };
1182 CFAssignRetained(*error, CFErrorCreateWithUserInfoKeysAndValues(NULL, kSecErrorDomain, errSecAuthNeeded,
1183 keys, ac_pairs, 1));
1184 CFRelease(keys[0]);
1185 CFRelease(ac_pairs[0]);
1186 CFRelease(ac_pair);
1187 return false;
1188 }
1189
1190 static bool SecTokenProcessError(CFStringRef operation, TKTokenRef token, CFTypeRef object_or_attrs, CFErrorRef *error) {
1191 if (CFEqualSafe(CFErrorGetDomain(*error), CFSTR(kTKErrorDomain)) &&
1192 CFErrorGetCode(*error) == kTKErrorCodeAuthenticationFailed) {
1193 // Replace error with the one which is augmented with access control and operation which failed,
1194 // which will cause SecItemDoWithAuth to throw UI.
1195 // Create array containing tuple (array) with error and requested operation.
1196 CFDataRef access_control = (CFGetTypeID(object_or_attrs) == CFDataGetTypeID()) ?
1197 TKTokenCopyObjectAccessControl(token, object_or_attrs, error) :
1198 TKTokenCopyObjectCreationAccessControl(token, object_or_attrs, error);
1199 if (access_control != NULL) {
1200 SecTokenCreateAccessControlError(operation, access_control, error);
1201 CFRelease(access_control);
1202 }
1203 }
1204 return false;
1205 }
1206
1207 static CFTypeRef SecTokenCopyUpdatedObjectID(TKTokenRef token, CFDataRef object_id, CFMutableDictionaryRef attributes, CFErrorRef *error) {
1208 CFDataRef access_control = NULL, db_value = NULL, new_object_id = NULL;
1209 SecAccessControlRef ac = NULL;
1210
1211 // Make sure that ACL is bound - if not, generate an error which will trigger binding.
1212 CFDataRef ac_data = CFDictionaryGetValue(attributes, kSecAttrAccessControl);
1213 if (ac_data != NULL) {
1214 require_quiet(ac = SecAccessControlCreateFromData(NULL, ac_data, error), out);
1215 require_action_quiet(SecAccessControlIsBound(ac), out,
1216 SecTokenCreateAccessControlError(CFSTR(""), ac_data, error));
1217 }
1218
1219 // Create or update the object on the token.
1220 require_action_quiet(new_object_id = TKTokenCreateOrUpdateObject(token, object_id, attributes, error), out,
1221 SecTokenProcessError(kAKSKeyOpEncrypt, token, object_id ?: (CFTypeRef)attributes, error));
1222
1223 // Prepare kSecValueData field for the item to be stored into the keychain DB.
1224 require_quiet(access_control = TKTokenCopyObjectAccessControl(token, new_object_id, error), out);
1225 require_quiet(db_value = SecTokenItemValueCreate(new_object_id, access_control,
1226 CFDictionaryGetValue(attributes, kSecValueData), error), out);
1227 CFDictionarySetValue(attributes, kSecValueData, db_value);
1228
1229 // kSecAttrAccessControl is handled directly by the token and stored inside data field.
1230 CFDictionaryRemoveValue(attributes, kSecAttrAccessControl);
1231
1232 out:
1233 CFReleaseSafe(ac);
1234 CFReleaseSafe(access_control);
1235 CFReleaseSafe(db_value);
1236 return new_object_id;
1237 }
1238
1239 static bool SecTokenItemAdd(TKTokenRef token, CFDictionaryRef attributes, CFDictionaryRef auth_params,
1240 CFTypeRef *result, CFErrorRef *error) {
1241 bool ok = false;
1242 CFTypeRef object_id = NULL, ref = NULL;
1243 CFDictionaryRef ref_attrs = NULL;
1244 CFTypeRef db_result = NULL;
1245
1246 CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, attributes);
1247 require_quiet(object_id = SecTokenCopyUpdatedObjectID(token, NULL, attrs, error), out);
1248
1249 #ifndef SECITEM_SHIM_OSX
1250 // Augment attributes from default attributes of the related ref (SecKeyRef, SecCertificateRef). This is best done
1251 // by creating ref and getting back its attributes.
1252 require_quiet(SecTokenItemCreateFromAttributes(attrs, auth_params, token, object_id, &ref, error), out);
1253 if (ref != NULL) {
1254 if ((ref_attrs = SecItemCopyAttributeDictionary(ref)) != NULL) {
1255 CFDictionaryForEach(ref_attrs, ^(const void *key, const void *value) {
1256 if (!CFEqual(key, kSecValueData)) {
1257 CFDictionaryAddValue(attrs, key, value);
1258 }
1259 });
1260 }
1261 }
1262 #endif
1263
1264 // Make sure that both attributes and data are returned.
1265 CFDictionarySetValue(attrs, kSecReturnAttributes, kCFBooleanTrue);
1266 CFDictionarySetValue(attrs, kSecReturnData, kCFBooleanTrue);
1267
1268 if (!CFEqualSafe(CFDictionaryGetValue(attrs, kSecAttrIsPermanent), kCFBooleanFalse)) {
1269 // IsPermanent is not present or is true, so add item to the db.
1270 require_quiet(SECURITYD_XPC(sec_item_add, cftype_ag_to_bool_cftype_error_request, attrs,
1271 SecAccessGroupsGetCurrent(), &db_result, error), out);
1272 } else {
1273 // Process directly result of token call.
1274 db_result = CFRetain(attrs);
1275 }
1276 require_quiet(SecItemResultProcess(attributes, auth_params, token, db_result, result, error), out);
1277
1278 ok = true;
1279
1280 out:
1281 CFReleaseSafe(db_result);
1282 CFReleaseSafe(attrs);
1283 CFReleaseSafe(ref_attrs);
1284 CFReleaseSafe(object_id);
1285 CFReleaseSafe(ref);
1286 return ok;
1287 }
1288
1289 OSStatus
1290 #if SECITEM_SHIM_OSX
1291 SecItemAdd_ios(CFDictionaryRef attributes, CFTypeRef *result)
1292 #else
1293 SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result)
1294 #endif // *** END SECITEM_SHIM_OSX ***
1295 {
1296 __block SecCFDictionaryCOW attrs = { attributes };
1297 OSStatus status;
1298
1299 os_activity_t trace_activity = os_activity_start("SecItemAdd_ios", OS_ACTIVITY_FLAG_DEFAULT);
1300
1301 #ifndef SECITEM_SHIM_OSX
1302 require_quiet(!explode_identity(attrs.dictionary, (secitem_operation)SecItemAdd, &status, result), errOut);
1303 infer_cert_label(&attrs);
1304 #endif // *** END SECITEM_SHIM_OSX ***
1305
1306 status = SecOSStatusWith(^bool(CFErrorRef *error) {
1307 return SecItemAuthDoQuery(&attrs, NULL, SecItemAdd, error, ^bool(TKTokenRef token, CFDictionaryRef attributes, CFDictionaryRef unused, CFDictionaryRef auth_params, CFErrorRef *error) {
1308 if (token == NULL) {
1309 CFTypeRef raw_result = NULL;
1310 if (!SECURITYD_XPC(sec_item_add, cftype_ag_to_bool_cftype_error_request, attributes, SecAccessGroupsGetCurrent(), &raw_result, error))
1311 return false;
1312
1313 bool ok = SecItemResultProcess(attributes, auth_params, token, raw_result, result, error);
1314 CFReleaseSafe(raw_result);
1315 return ok;
1316 } else {
1317 // Send request to an appropriate token instead of secd.
1318 return SecTokenItemAdd(token, attributes, auth_params, result, error);
1319 }
1320 });
1321 });
1322
1323 #ifndef SECITEM_SHIM_OSX
1324 errOut:
1325 #endif
1326 CFReleaseSafe(attrs.mutable_dictionary);
1327
1328 os_activity_end(trace_activity);
1329 return status;
1330 }
1331
1332
1333 OSStatus
1334 #if SECITEM_SHIM_OSX
1335 SecItemCopyMatching_ios(CFDictionaryRef inQuery, CFTypeRef *result)
1336 #else
1337 SecItemCopyMatching(CFDictionaryRef inQuery, CFTypeRef *result)
1338 #endif // *** END SECITEM_SHIM_OSX ***
1339 {
1340 OSStatus status;
1341 __block SecCFDictionaryCOW query = { inQuery };
1342
1343 os_activity_t trace_activity = os_activity_start("SecItemCopyMatching_ios", OS_ACTIVITY_FLAG_DEFAULT);
1344
1345 #ifndef SECITEM_SHIM_OSX
1346 require_quiet(!explode_identity(query.dictionary, (secitem_operation)SecItemCopyMatching, &status, result), errOut);
1347 #endif // *** END SECITEM_SHIM_OSX ***
1348
1349 bool wants_data = cf_bool_value(CFDictionaryGetValue(query.dictionary, kSecReturnData));
1350 bool wants_attributes = cf_bool_value(CFDictionaryGetValue(query.dictionary, kSecReturnAttributes));
1351 if ((wants_data && !wants_attributes) || (!wants_data && wants_attributes)) {
1352 // When either attributes or data are requested, we need to query both, because for token based items,
1353 // both are needed in order to generate proper data and/or attributes results.
1354 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query), kSecReturnAttributes, kCFBooleanTrue);
1355 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&query), kSecReturnData, kCFBooleanTrue);
1356 }
1357
1358 status = SecOSStatusWith(^bool(CFErrorRef *error) {
1359 return SecItemAuthDoQuery(&query, NULL, SecItemCopyMatching, error, ^bool(TKTokenRef token, CFDictionaryRef query, CFDictionaryRef attributes, CFDictionaryRef auth_params, CFErrorRef *error) {
1360 CFTypeRef raw_result = NULL;
1361 if (!SECURITYD_XPC(sec_item_copy_matching, cftype_ag_to_bool_cftype_error_request, query, SecAccessGroupsGetCurrent(), &raw_result, error))
1362 return false;
1363
1364 // We intentionally pass NULL as token argument, because we want to be able to decide about token on which the item lives
1365 // on per-record basis, not wholesale. Logic inside SecItemResultCopyPrepared will open proper token according
1366 // to currently processed item.
1367 bool ok = SecItemResultProcess(inQuery, auth_params, NULL, raw_result, result, error);
1368 CFReleaseSafe(raw_result);
1369 return ok;
1370 });
1371 });
1372
1373 #ifndef SECITEM_SHIM_OSX
1374 errOut:
1375 #endif
1376
1377 CFReleaseSafe(query.mutable_dictionary);
1378 os_activity_end(trace_activity);
1379 return status;
1380 }
1381
1382 // Invokes token-object handler for each item matching specified query.
1383 static bool SecTokenItemForEachMatching(CFDictionaryRef query, CFErrorRef *error,
1384 bool (^perform)(CFDictionaryRef item_value, CFDictionaryRef item_query,
1385 CFErrorRef *error)) {
1386 bool ok = false;
1387 CFMutableDictionaryRef list_query = NULL;
1388 CFTypeRef items = NULL;
1389 CFArrayRef ref_array = NULL;
1390 CFDictionaryRef item_query = NULL, item_data = NULL;
1391
1392 // Query all items with data and persistent_ref, so that we can extract objectIDs and also identify originating
1393 // items in the keychain.
1394 list_query = CFDictionaryCreateMutableCopy(NULL, 0, query);
1395 if (CFDictionaryGetValue(list_query, kSecMatchLimit) == NULL) {
1396 CFDictionarySetValue(list_query, kSecMatchLimit, kSecMatchLimitAll);
1397 }
1398 CFDictionarySetValue(list_query, kSecReturnData, kCFBooleanTrue);
1399 CFDictionarySetValue(list_query, kSecReturnPersistentRef, kCFBooleanTrue);
1400 require_quiet(SECURITYD_XPC(sec_item_copy_matching, cftype_ag_to_bool_cftype_error_request, list_query,
1401 SecAccessGroupsGetCurrent(), &items, error), out);
1402 if (CFGetTypeID(items) != CFArrayGetTypeID()) {
1403 // Wrap single returned item into the array.
1404 CFArrayRef item_array = CFArrayCreateForCFTypes(NULL, items, NULL);
1405 CFAssignRetained(items, item_array);
1406 }
1407
1408 CFTypeRef item;
1409 CFArrayForEachC(items, item) {
1410 CFDataRef data = CFDictionaryGetValue(item, kSecValueData);
1411 require_action_quiet(data != NULL, out, SecError(errSecInternal, error, CFSTR("value not present for token item")));
1412
1413 CFAssignRetained(item_data, SecTokenItemValueCopy(data, error));
1414 require_quiet(item_data, out);
1415
1416 CFAssignRetained(item_query,
1417 CFDictionaryCreateForCFTypes(NULL,
1418 kSecValuePersistentRef, CFDictionaryGetValue(item, kSecValuePersistentRef),
1419 NULL));
1420 require_quiet(perform(item_data, item_query, error), out);
1421 }
1422
1423 ok = true;
1424
1425 out:
1426 CFReleaseSafe(list_query);
1427 CFReleaseSafe(items);
1428 CFReleaseSafe(item_data);
1429 CFReleaseSafe(ref_array);
1430 CFReleaseSafe(item_query);
1431 return ok;
1432 }
1433
1434 static bool SecItemRawUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate, CFErrorRef *error) {
1435 bool ok = false;
1436 if (gSecurityd) {
1437 // Ensure the dictionary passed to securityd has proper kCFTypeDictionaryKeyCallBacks.
1438 CFMutableDictionaryRef tmp = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
1439 CFDictionaryForEach(attributesToUpdate, ^(const void *key, const void *value) { CFDictionaryAddValue(tmp, key, value); });
1440 ok = gSecurityd->sec_item_update(query, tmp, SecAccessGroupsGetCurrent(), error);
1441 CFRelease(tmp);
1442 } else {
1443 xpc_object_t message = securityd_create_message(sec_item_update_id, error);
1444 if (message) {
1445 if (SecXPCDictionarySetPList(message, kSecXPCKeyQuery, query, error) &&
1446 SecXPCDictionarySetPList(message, kSecXPCKeyAttributesToUpdate, attributesToUpdate, error)) {
1447 xpc_object_t reply = securityd_message_with_reply_sync(message, error);
1448 if (reply) {
1449 ok = securityd_message_no_error(reply, error);
1450 xpc_release(reply);
1451 }
1452 }
1453 xpc_release(message);
1454 }
1455 }
1456 return ok;
1457 }
1458
1459 static bool SecTokenItemUpdate(TKTokenRef token, CFDictionaryRef query, CFDictionaryRef attributesToUpdate, CFErrorRef *error) {
1460 return SecTokenItemForEachMatching(query, error, ^bool(CFDictionaryRef object_data, CFDictionaryRef item_query, CFErrorRef *error) {
1461 bool ok = false;
1462 CFDataRef object_id = NULL;
1463 CFMutableDictionaryRef db_value = NULL;
1464
1465 // Update attributes on the token.
1466 CFMutableDictionaryRef attributes = CFDictionaryCreateMutableCopy(NULL, 0, attributesToUpdate);
1467 require_quiet(object_id = SecTokenCopyUpdatedObjectID(token, CFDictionaryGetValue(object_data, kSecTokenValueObjectIDKey),
1468 attributes, error), out);
1469
1470 // Update attributes in the database.
1471 require_quiet(SecItemRawUpdate(item_query, attributes, error), out);
1472
1473 ok = true;
1474
1475 out:
1476 CFReleaseSafe(object_id);
1477 CFReleaseSafe(attributes);
1478 CFReleaseSafe(db_value);
1479 return ok;
1480 });
1481 }
1482
1483 OSStatus
1484 #if SECITEM_SHIM_OSX
1485 SecItemUpdate_ios(CFDictionaryRef inQuery, CFDictionaryRef inAttributesToUpdate)
1486 #else
1487 SecItemUpdate(CFDictionaryRef inQuery, CFDictionaryRef inAttributesToUpdate)
1488 #endif // *** END SECITEM_SHIM_OSX ***
1489 {
1490 OSStatus status;
1491 __block SecCFDictionaryCOW query = { inQuery };
1492 __block SecCFDictionaryCOW attributesToUpdate = { inAttributesToUpdate };
1493
1494 status = SecOSStatusWith(^bool(CFErrorRef *error) {
1495 return SecItemAuthDoQuery(&query, &attributesToUpdate, SecItemUpdate, error, ^bool(TKTokenRef token, CFDictionaryRef query, CFDictionaryRef attributes, CFDictionaryRef auth_params, CFErrorRef *error) {
1496 if (token == NULL) {
1497 return SecItemRawUpdate(query, attributes, error);
1498 } else {
1499 return SecTokenItemUpdate(token, query, attributes, error);
1500 }
1501 });
1502 });
1503
1504 CFReleaseSafe(query.mutable_dictionary);
1505 CFReleaseSafe(attributesToUpdate.mutable_dictionary);
1506 return status;
1507 }
1508
1509 #ifndef SECITEM_SHIM_OSX
1510 static OSStatus explode_persistent_identity_ref(SecCFDictionaryCOW *query)
1511 {
1512 OSStatus status = errSecSuccess;
1513 CFTypeRef persist = CFDictionaryGetValue(query->dictionary, kSecValuePersistentRef);
1514 CFStringRef class;
1515 if (persist && _SecItemParsePersistentRef(persist, &class, NULL)
1516 && CFEqual(class, kSecClassIdentity)) {
1517 const void *keys[] = { kSecReturnRef, kSecValuePersistentRef };
1518 const void *vals[] = { kCFBooleanTrue, persist };
1519 CFDictionaryRef persistent_query = CFDictionaryCreate(NULL, keys,
1520 vals, (array_size(keys)), NULL, NULL);
1521 CFTypeRef item_query = NULL;
1522 status = SecItemCopyMatching(persistent_query, &item_query);
1523 CFReleaseNull(persistent_query);
1524 if (status)
1525 return status;
1526
1527 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecValuePersistentRef);
1528 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query), kSecValueRef, item_query);
1529 CFRelease(item_query);
1530 }
1531
1532 return status;
1533 }
1534 #endif
1535
1536 OSStatus
1537 #if SECITEM_SHIM_OSX
1538 SecItemDelete_ios(CFDictionaryRef inQuery)
1539 #else
1540 SecItemDelete(CFDictionaryRef inQuery)
1541 #endif // *** END SECITEM_SHIM_OSX ***
1542 {
1543 OSStatus status;
1544 __block SecCFDictionaryCOW query = { inQuery };
1545
1546 os_activity_t trace_activity = os_activity_start("SecItemDelete_ios", OS_ACTIVITY_FLAG_DEFAULT);
1547
1548 #ifndef SECITEM_SHIM_OSX
1549 require_noerr_quiet(status = explode_persistent_identity_ref(&query), errOut);
1550 require_quiet(!explode_identity(query.dictionary, (secitem_operation)SecItemDelete, &status, NULL), errOut);
1551 #endif // *** END SECITEM_SHIM_OSX ***
1552
1553 status = SecOSStatusWith(^bool(CFErrorRef *error) {
1554 return SecItemAuthDoQuery(&query, NULL, SecItemDelete, error, ^bool(TKTokenRef token, CFDictionaryRef query, CFDictionaryRef attributes, CFDictionaryRef auth_params, CFErrorRef *error) {
1555 if (token == NULL) {
1556 return SECURITYD_XPC(sec_item_delete, dict_ag_to_error_request, query, SecAccessGroupsGetCurrent(), error);
1557 } else {
1558 return SecTokenItemForEachMatching(query, error, ^bool(CFDictionaryRef object_data, CFDictionaryRef item_query, CFErrorRef *error) {
1559 bool ok = false;
1560
1561 // Delete item from the token.
1562 CFDataRef object_id = CFDictionaryGetValue(object_data, kSecTokenValueObjectIDKey);
1563 require_action_quiet(TKTokenDeleteObject(token, object_id, error), out,
1564 SecTokenProcessError(kAKSKeyOpDelete, token, object_id, error));
1565
1566 // Delete the item from the keychain.
1567 require_quiet(SECURITYD_XPC(sec_item_delete, dict_ag_to_error_request, item_query,
1568 SecAccessGroupsGetCurrent(), error), out);
1569 ok = true;
1570
1571 out:
1572 return ok;
1573 });
1574 }
1575 });
1576 });
1577
1578 #ifndef SECITEM_SHIM_OSX
1579 errOut:
1580 #endif
1581
1582 CFReleaseSafe(query.mutable_dictionary);
1583
1584 os_activity_end(trace_activity);
1585
1586 return status;
1587 }
1588
1589 OSStatus
1590 SecItemDeleteAll(void)
1591 {
1592 return SecOSStatusWith(^bool (CFErrorRef *error) {
1593 bool ok;
1594 if (gSecurityd) {
1595 ok = true;
1596 #ifndef SECITEM_SHIM_OSX
1597 SecTrustStoreRef ts = SecTrustStoreForDomain(kSecTrustStoreDomainUser);
1598 if (!gSecurityd->sec_truststore_remove_all(ts, error))
1599 ok = SecError(errSecInternal, error, CFSTR("sec_truststore_remove_all is NULL"));
1600 #endif // *** END SECITEM_SHIM_OSX ***
1601 if (!gSecurityd->sec_item_delete_all(error))
1602 ok = SecError(errSecInternal, error, CFSTR("sec_item_delete_all is NULL"));
1603 } else {
1604 ok = securityd_send_sync_and_do(sec_delete_all_id, error, NULL, NULL);
1605 }
1606 return ok;
1607 });
1608 }
1609
1610
1611 CFArrayRef _SecKeychainSyncUpdateMessage(CFDictionaryRef updates, CFErrorRef *error) {
1612 __block CFArrayRef result;
1613 os_activity_initiate("_SecKeychainSyncUpdateMessage", OS_ACTIVITY_FLAG_DEFAULT, ^{
1614 result = SECURITYD_XPC(sec_keychain_sync_update_message, dict_to_array_error_request, updates, error);
1615 });
1616 return result;
1617 }
1618
1619 #ifndef SECITEM_SHIM_OSX
1620 OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement);
1621
1622 OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement)
1623 {
1624 return -1; /* this is only on OS X currently */
1625 }
1626
1627 #else
1628
1629 extern OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement);
1630
1631 #endif
1632
1633 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
1634
1635 bool _SecKeychainRollKeys(bool force, CFErrorRef *error)
1636 {
1637 do_if_registered(sec_roll_keys, force, error);
1638
1639 __block bool result = false;
1640
1641 secdebug("secitem","enter - %s", __FUNCTION__);
1642 securityd_send_sync_and_do(kSecXPCOpRollKeys, error,
1643 ^bool(xpc_object_t message, CFErrorRef *error) {
1644 xpc_dictionary_set_bool(message, "force", force);
1645 return true;
1646 },
1647 ^bool(xpc_object_t response, __unused CFErrorRef *error) {
1648 result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
1649 return result;
1650 });
1651 return result;
1652 }
1653
1654