]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecItem.c
112335be757d11a7024f3c9ab418b9bdc97566db
[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 case kSecIDSErrorNoPeersAvailable:
207 return errSecPeersNotAvailable;
208 default:
209 return errSecInternal;
210 }
211 }
212
213 static OSStatus osstatus_for_localauthentication_error(CFIndex laError) {
214 // Wrap LA error in Sec error.
215 switch (laError) {
216 case kLAErrorUserCancel:
217 return errSecUserCanceled;
218 case kLAErrorParameter:
219 return errSecParam;
220 case kLAErrorNotInteractive:
221 return errSecInteractionNotAllowed;
222 default:
223 return errSecAuthFailed;
224 }
225 }
226
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
231 #endif
232 #ifndef kTKErrorCodeCanceledByUser
233 #define kTKErrorCodeCanceledByUser -4
234 #endif
235
236 switch (ctkError) {
237 case kTKErrorCodeBadParameter:
238 return errSecParam;
239 case kTKErrorCodeNotImplemented:
240 return errSecUnimplemented;
241 case kTKErrorCodeCanceledByUser:
242 return errSecUserCanceled;
243 default:
244 return errSecInternal;
245 }
246 }
247
248 // Convert from securityd error codes to OSStatus for legacy API.
249 OSStatus SecErrorGetOSStatus(CFErrorRef error) {
250 OSStatus status;
251 if (error == NULL) {
252 status = errSecSuccess;
253 } else {
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));
276 } else {
277 secnotice("securityd", "unknown error domain: %@ for error: %@", domain, error);
278 status = errSecInternal;
279 }
280 }
281 return status;
282 }
283
284 // Wrapper to provide a CFErrorRef for legacy API.
285 OSStatus SecOSStatusWith(bool (^perform)(CFErrorRef *error)) {
286 CFErrorRef error = NULL;
287 OSStatus status;
288 if (perform(&error)) {
289 assert(error == NULL);
290 status = errSecSuccess;
291 } else {
292 assert(error);
293 status = SecErrorGetOSStatus(error);
294 if (status != errSecItemNotFound) // Occurs in normal operation, so exclude
295 secerror("error:[%" PRIdOSStatus "] %@", status, error);
296 CFReleaseNull(error);
297 }
298 return status;
299 }
300
301 /* IPC uses CFPropertyList to un/marshall input/output data and can handle:
302 CFData, CFString, CFArray, CFDictionary, CFDate, CFBoolean, and CFNumber
303
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
310 */
311
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()) {
320 refDictionary =
321 SecCertificateCopyAttributeDictionary((SecCertificateRef)ref);
322 } else if (typeID == SecIdentityGetTypeID()) {
323 assert(false);
324 SecIdentityRef identity = (SecIdentityRef)ref;
325 SecCertificateRef cert = NULL;
326 SecKeyRef key = NULL;
327 if (!SecIdentityCopyCertificate(identity, &cert) &&
328 !SecIdentityCopyPrivateKey(identity, &key))
329 {
330 CFDataRef data = SecCertificateCopyData(cert);
331 CFDictionaryRef key_dict = SecKeyCopyAttributeDictionary(key);
332
333 if (key_dict && data) {
334 refDictionary = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, key_dict);
335 CFDictionarySetValue((CFMutableDictionaryRef)refDictionary,
336 CFSTR(CERTIFICATE_DATA_COLUMN_LABEL), data);
337 }
338 CFReleaseNull(key_dict);
339 CFReleaseNull(data);
340 }
341 CFReleaseNull(cert);
342 CFReleaseNull(key);
343 } else {
344 refDictionary = NULL;
345 }
346 return refDictionary;
347 }
348
349 static CFTypeRef
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);
362 if (key && cert)
363 ref = SecIdentityCreate(allocator, cert, key);
364 CFReleaseSafe(cert);
365 CFReleaseSafe(key);
366 #if 0
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)) {
371 #endif
372 } else {
373 ref = NULL;
374 }
375 return ref;
376 }
377 #else
378
379 extern CFTypeRef SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes);
380
381 #endif
382
383 OSStatus
384 SecItemCopyDisplayNames(CFArrayRef items, CFArrayRef *displayNames)
385 {
386 // @@@ TBI
387 return -1 /* errSecUnimplemented */;
388 }
389
390 #ifndef SECITEM_SHIM_OSX
391 typedef OSStatus (*secitem_operation)(CFDictionaryRef attributes, CFTypeRef *result);
392
393 static bool explode_identity(CFDictionaryRef attributes, secitem_operation operation,
394 OSStatus *return_status, CFTypeRef *return_result)
395 {
396 bool handled = false;
397 CFTypeRef value = CFDictionaryGetValue(attributes, kSecValueRef);
398 if (value) {
399 CFTypeID typeID = CFGetTypeID(value);
400 if (typeID == SecIdentityGetTypeID()) {
401 handled = true;
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))
408 {
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;
421 }
422
423 if (!status || status == errSecItemNotFound) {
424 bool skip_key_operation = false;
425
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;
431
432 key_dict = SecKeyCopyAttributeDictionary(key);
433 if (key_dict)
434 pkhh = (CFDataRef)CFDictionaryGetValue(key_dict, kSecAttrApplicationLabel);
435 const void *keys[] = { kSecClass, kSecAttrPublicKeyHash };
436 const void *vals[] = { kSecClassCertificate, pkhh };
437 if (pkhh)
438 query_dict = CFDictionaryCreate(NULL, keys,
439 vals, (array_size(keys)),
440 NULL, NULL);
441 if (query_dict)
442 if (errSecSuccess == SecItemCopyMatching(query_dict, NULL))
443 skip_key_operation = true;
444 CFReleaseSafe(query_dict);
445 CFReleaseSafe(key_dict);
446 }
447
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) &&
455 !duplicate_cert)
456 status = errSecSuccess;
457 }
458
459 /* add and copy matching for an identityref have a persistent ref result */
460 if (result) {
461 if (!status) {
462 /* result is a persistent ref to a cert */
463 sqlite_int64 rowid;
464 if (_SecItemParsePersistentRef(result, NULL, &rowid)) {
465 *return_result = _SecItemMakePersistentRef(kSecClassIdentity, rowid);
466 }
467 }
468 CFRelease(result);
469 }
470 }
471 CFReleaseNull(partial_query);
472 }
473 else
474 status = errSecInvalidItemRef;
475
476 CFReleaseNull(cert);
477 CFReleaseNull(key);
478 *return_status = status;
479 }
480 } else {
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);
488 if (!status) {
489 CFDictionarySetValue(dict, kSecClass, kSecClassKey);
490 status = SecItemDelete(dict);
491 }
492 CFRelease(dict);
493 *return_status = status;
494 handled = true;
495 }
496 }
497 return handled;
498 }
499
500 static void infer_cert_label(SecCFDictionaryCOW *attributes)
501 {
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);
507 if (label) {
508 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes), kSecAttrLabel, label);
509 CFReleaseNull(label);
510 }
511 }
512 }
513 }
514
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)
517 {
518 uint8_t bytes[sizeof(sqlite_int64) + 4];
519 if (rowid < 0)
520 return NULL;
521 if (CFStringGetCString(class, (char *)bytes, 4 + 1 /*null-term*/,
522 kCFStringEncodingUTF8))
523 {
524 OSWriteBigInt64(bytes + 4, 0, rowid);
525 return CFDataCreate(NULL, bytes, sizeof(bytes));
526 }
527 return NULL;
528 }
529
530 /* AUDIT[securityd](done):
531 persistent_ref (ok) is a caller provided, non NULL CFTypeRef.
532 */
533 bool _SecItemParsePersistentRef(CFDataRef persistent_ref, CFStringRef *return_class, sqlite_int64 *return_rowid)
534 {
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);
540
541 CFStringRef class = CFStringCreateWithBytes(kCFAllocatorDefault,
542 bytes, CFStringGetLength(kSecClassGenericPassword),
543 kCFStringEncodingUTF8, true);
544 const void *valid_classes[] = { kSecClassGenericPassword,
545 kSecClassInternetPassword,
546 kSecClassAppleSharePassword,
547 kSecClassCertificate,
548 kSecClassKey,
549 kSecClassIdentity };
550
551 unsigned i;
552 for (i=0; i< array_size(valid_classes); i++) {
553 if (CFEqual(valid_classes[i], class)) {
554 if (return_class)
555 *return_class = valid_classes[i];
556 if (return_rowid)
557 *return_rowid = rowid;
558 valid_ref = true;
559 break;
560 }
561 }
562 CFRelease(class);
563 }
564 return valid_ref;
565 }
566
567 #endif // *** END SECITEM_SHIM_OSX ***
568
569 static bool cf_bool_value(CFTypeRef cf_bool)
570 {
571 return (cf_bool && CFEqual(kCFBooleanTrue, cf_bool));
572 }
573
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);
580 });
581 }
582 cow_dictionary->dictionary = cow_dictionary->mutable_dictionary;
583 }
584
585 return cow_dictionary->mutable_dictionary;
586 }
587
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");
592
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,
600 NULL);
601 if (object_value != NULL) {
602 CFDictionarySetValue(value, kSecTokenValueDataKey, object_value);
603 }
604
605 CFDataRef value_data = CFPropertyListCreateDERData(NULL, value, error);
606 CFRelease(value);
607 return value_data;
608 }
609
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")));
618
619 out:
620 return plist;
621 }
622
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);
630
631 CFDictionaryRemoveValue(token_attrs, kSecUseAuthenticationContext);
632 token = TKTokenCreate(token_attrs, error);
633
634 CFReleaseSafe(token_attrs);
635 return token;
636 }
637
638 static bool SecTokenItemCreateFromAttributes(CFDictionaryRef attributes, CFDictionaryRef auth_params,
639 TKTokenRef token, CFDataRef object_id, CFTypeRef *ref, CFErrorRef *error) {
640 bool ok = false;
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);
646 }
647
648 if (auth_params != NULL) {
649 CFDictionaryForEach(auth_params, ^(const void *key, const void *value) {
650 CFDictionarySetValue(attrs, key, value);
651 });
652 }
653 CFDictionarySetValue(attrs, kSecUseToken, token);
654 CFDictionarySetValue(attrs, kSecUseTokenObjectID, object_id);
655 CFRelease(token);
656 }
657 *ref = SecItemCreateFromAttributeDictionary(attrs);
658 ok = true;
659
660 out:
661 CFReleaseSafe(attrs);
662 return ok;
663 }
664
665
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) {
671 bool ok = false;
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;
678
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));
683
684 // Get token value if not provided by the caller.
685 bool token_item = false;
686 if (token == NULL) {
687 if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID()) {
688 token_id = CFDictionaryGetValue(raw_result, kSecAttrTokenID);
689 token_item = (token_id != NULL);
690 }
691 } else {
692 token_item = true;
693 CFRetain(token);
694 }
695
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));
700 else
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.
713 if (token == NULL) {
714 require_quiet(token = SecTokenCreate(token_id, auth_params, error), out);
715 }
716 require_quiet(object_value = TKTokenCopyObjectData(token, object_id, error), out);
717 if (CFEqual(object_value, kCFNull))
718 CFReleaseNull(object_value);
719 }
720 CFAssignRetained(value, object_value);
721 }
722
723 // If only thing requested is data, return them directly.
724 if (!(wants_attributes || wants_persistent_ref || wants_ref)) {
725 *result = CFRetainSafe(value);
726 ok = true;
727 goto out;
728 }
729 }
730
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));
735 else
736 persistent_ref = CFRetainSafe(raw_result);
737
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);
741 ok = true;
742 goto out;
743 }
744 }
745
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);
750 else
751 *result = CFDictionaryCreateForCFTypes(NULL, NULL);
752 CFMutableDictionaryRef output = (CFMutableDictionaryRef)*result;
753
754 if ((wants_data || wants_ref) && value != NULL)
755 CFDictionarySetValue(output, kSecValueData, value);
756 else
757 CFDictionaryRemoveValue(output, kSecValueData);
758
759 if (wants_persistent_ref && persistent_ref != NULL)
760 CFDictionarySetValue(output, kSecValuePersistentRef, persistent_ref);
761 else
762 CFDictionaryRemoveValue(output, kSecValuePersistentRef);
763
764 if (wants_ref) {
765 CFTypeRef ref;
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);
771 CFRelease(ref);
772 if (!wants_data) {
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);
776 }
777 }
778 }
779
780 if (wants_attributes) {
781 // Convert serialized form of access control to object form.
782 if (!token_item) {
783 CFRetainAssign(ac_data, CFDictionaryGetValue(output, kSecAttrAccessControl));
784 }
785
786 if (ac_data != NULL) {
787 SecAccessControlRef ac;
788 require_quiet(ac = SecAccessControlCreateFromData(kCFAllocatorDefault, ac_data, error), out);
789 CFDictionarySetValue(output, kSecAttrAccessControl, ac);
790 CFRelease(ac);
791 }
792 }
793 } else {
794 *result = NULL;
795 }
796
797 ok = true;
798
799 out:
800 CFReleaseSafe(ac_data);
801 CFReleaseSafe(value);
802 CFReleaseSafe(persistent_ref);
803 CFReleaseSafe(object_id);
804 CFReleaseSafe(attrs);
805 CFReleaseSafe(token);
806 return ok;
807 }
808
809 static bool SecItemResultProcess(CFDictionaryRef query, CFDictionaryRef auth_params, TKTokenRef token,
810 CFTypeRef raw_result, CFTypeRef *result, CFErrorRef *error) {
811 bool ok = false;
812 require_action_quiet(raw_result != NULL, out, ok = true);
813 require_action_quiet(result != NULL, out, ok = true);
814
815 if (CFGetTypeID(raw_result) == CFArrayGetTypeID()) {
816 CFIndex i, count = CFArrayGetCount(raw_result);
817 *result = CFArrayCreateMutableForCFTypes(NULL);
818 for (i = 0; i < count; i++) {
819 CFTypeRef ref;
820 require_quiet(SecItemResultCopyPrepared(CFArrayGetValueAtIndex(raw_result, i),
821 token, query, auth_params, &ref, error), out);
822 if (ref != NULL) {
823 CFArrayAppendValue((CFMutableArrayRef)*result, ref);
824 CFRelease(ref);
825 }
826 }
827 } else {
828 require_quiet(SecItemResultCopyPrepared(raw_result, token, query, auth_params, result, error), out);
829 }
830
831 ok = true;
832
833 out:
834 return ok;
835 }
836
837 static bool SecItemAttributesPrepare(SecCFDictionaryCOW *attrs, CFErrorRef *error) {
838 bool ok = false;
839 CFDataRef ac_data = NULL, acm_context = NULL;
840 void *la_lib = NULL;
841
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);
846 }
847
848 const CFTypeRef la_context = CFDictionaryGetValue(attrs->dictionary, kSecUseAuthenticationContext);
849 if (la_context) {
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);
861 }
862
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);
866 if (value) {
867 CFDictionaryRef ref_attributes;
868 require_action_quiet(ref_attributes = SecItemCopyAttributeDictionary(value), out,
869 SecError(errSecValueRefUnsupported, error, CFSTR("unsupported kSecValueRef in query")));
870
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);
880 });
881 CFAssignRetained(attrs->mutable_dictionary, new_query);
882 attrs->dictionary = attrs->mutable_dictionary;
883 }
884 value = CFDictionaryGetValue(attrs->dictionary, kSecAttrIssuer);
885 if (value) {
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))
891 {
892 CFDataRef canonical_issuer = createNormalizedX501Name(kCFAllocatorDefault, &content.content);
893 if (canonical_issuer) {
894 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecAttrIssuer, canonical_issuer);
895 CFRelease(canonical_issuer);
896 }
897 }
898 }
899 #endif
900
901 ok = true;
902
903 out:
904 if (la_lib != NULL) {
905 dlclose(la_lib);
906 }
907 CFReleaseSafe(ac_data);
908 CFReleaseSafe(acm_context);
909 return ok;
910 }
911
912 static bool SecItemAuthMaxAttemptsReached(CFArrayRef ac_pairs, CFErrorRef *error)
913 {
914 CFMutableStringRef log_string = CFStringCreateMutable(kCFAllocatorDefault, 0);
915 CFArrayRef ac_pair;
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);
921 CFRelease(str);
922 }
923
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);
927
928 CFRelease(reason);
929 CFRelease(log_string);
930 return false;
931 }
932
933 bool SecItemAuthDo(SecCFDictionaryCOW *auth_params, CFErrorRef *error, SecItemAuthResult (^perform)(CFDictionaryRef auth_params, CFArrayRef *ac_pairs, CFErrorRef *error)) {
934 bool ok = false;
935 CFArrayRef ac_pairs = NULL;
936 SecCFDictionaryCOW auth_options = { NULL };
937
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);
943
944 // If auth_params were not created up to now, do create them because we will definitely need them.
945 SecCFDictionaryCOWGetMutable(auth_params);
946
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);
958 }
959 }
960
961 // Throttle max authentication attempts. This is mainly protection against exceptional states, not ordinary
962 // user retry limit.
963 require_action(i < 20, out, SecItemAuthMaxAttemptsReached(ac_pairs, error));
964
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);
971 CFRelease(key);
972 }
973
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);
978 CFRelease(key);
979 }
980 }
981
982 // Go through all access_control-operation pairs and evaluate them.
983 CFArrayRef ac_pair;
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);
989
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);
999 CFRelease(ac);
1000 }
1001 }
1002 }
1003
1004 ok = true;
1005
1006 out:
1007 CFReleaseSafe(auth_options.mutable_dictionary);
1008 CFReleaseSafe(ac_pairs);
1009 return ok;
1010 }
1011
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);
1018 }
1019
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);
1029 }
1030
1031 if (!CFEqual(auth_ui, kSecUseAuthenticationUIAllow)) {
1032 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseAuthenticationUI, auth_ui);
1033 }
1034
1035 CFDataRef acm_context = CFDictionaryGetValue(query->dictionary, kSecUseCredentialReference);
1036 if (acm_context != NULL) {
1037 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(auth_params), kSecUseCredentialReference, acm_context);
1038 }
1039 }
1040
1041 static SecItemAuthResult SecItemCreatePairsFromError(CFErrorRef *error, CFArrayRef *ac_pairs)
1042 {
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));
1050
1051 CFRelease(key);
1052 CFRelease(user_info);
1053 CFReleaseNull(*error);
1054 return kSecItemAuthResultNeedAuth;
1055 }
1056 return kSecItemAuthResultError;
1057 }
1058
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)) {
1062 bool ok = false;
1063 SecCFDictionaryCOW auth_params = { NULL };
1064 SecAccessControlRef access_control = NULL;
1065 __block TKTokenRef token = NULL;
1066
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);
1073 }
1074
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);
1080
1081 // Populate auth_params dictionary according to initial query contents.
1082 SecItemAuthCopyParams(&auth_params, query);
1083
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")));
1089 }
1090
1091 ok = SecItemAuthDo(&auth_params, error, ^SecItemAuthResult(CFDictionaryRef auth_params, CFArrayRef *ac_pairs, CFErrorRef *error) {
1092 SecItemAuthResult result = kSecItemAuthResultError;
1093
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);
1099 }
1100
1101 CFDataRef acl_data_ref = CFDictionaryGetValue(auth_params, kSecAttrAccessControl);
1102 if (acl_data_ref != NULL) {
1103 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attributes ?: query), kSecAttrAccessControl, acl_data_ref);
1104 }
1105 }
1106
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);
1111 }
1112
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);
1116 }
1117
1118 result = kSecItemAuthResultOK;
1119
1120 out:
1121 return result;
1122 });
1123 require_quiet(ok, out);
1124
1125 ok = true;
1126
1127 out:
1128 CFReleaseSafe(token);
1129 CFReleaseSafe(auth_params.mutable_dictionary);
1130 return ok;
1131 }
1132
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);
1139 #endif
1140
1141 static bool cftype_to_bool_cftype_error_request(enum SecXPCOperation op, CFTypeRef attributes, CFTypeRef *result, CFErrorRef *error)
1142 {
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) {
1146 if (result) {
1147 return SecXPCDictionaryCopyPListOptional(response, kSecXPCKeyResult, result, error);
1148 }
1149 return true;
1150 });
1151 }
1152
1153 static CFArrayRef dict_to_array_error_request(enum SecXPCOperation op, CFDictionaryRef attributes, CFErrorRef *error)
1154 {
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);
1160 }
1161 return result;
1162 }
1163
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);
1166 }
1167
1168 static bool dict_to_error_request(enum SecXPCOperation op, CFDictionaryRef query, CFErrorRef *error)
1169 {
1170 return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
1171 return SecXPCDictionarySetPList(message, kSecXPCKeyQuery, query, error);
1172 }, NULL);
1173 }
1174
1175 static bool dict_ag_to_error_request(enum SecXPCOperation op, CFDictionaryRef query, __unused CFArrayRef accessGroups, CFErrorRef *error)
1176 {
1177 return dict_to_error_request(op, query, error);
1178 }
1179
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));
1186 CFRelease(keys[0]);
1187 CFRelease(ac_pairs[0]);
1188 CFRelease(ac_pair);
1189 return false;
1190 }
1191
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);
1204 }
1205 }
1206 return false;
1207 }
1208
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;
1212
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));
1219 }
1220
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));
1224
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);
1230
1231 // kSecAttrAccessControl is handled directly by the token and stored inside data field.
1232 CFDictionaryRemoveValue(attributes, kSecAttrAccessControl);
1233
1234 out:
1235 CFReleaseSafe(ac);
1236 CFReleaseSafe(access_control);
1237 CFReleaseSafe(db_value);
1238 return new_object_id;
1239 }
1240
1241 static bool SecTokenItemAdd(TKTokenRef token, CFDictionaryRef attributes, CFDictionaryRef auth_params,
1242 CFTypeRef *result, CFErrorRef *error) {
1243 bool ok = false;
1244 CFTypeRef object_id = NULL, ref = NULL;
1245 CFDictionaryRef ref_attrs = NULL;
1246 CFTypeRef db_result = NULL;
1247
1248 CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, attributes);
1249 require_quiet(object_id = SecTokenCopyUpdatedObjectID(token, NULL, attrs, error), out);
1250
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);
1255 if (ref != NULL) {
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);
1260 }
1261 });
1262 }
1263 }
1264 #endif
1265
1266 // Make sure that both attributes and data are returned.
1267 CFDictionarySetValue(attrs, kSecReturnAttributes, kCFBooleanTrue);
1268 CFDictionarySetValue(attrs, kSecReturnData, kCFBooleanTrue);
1269
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);
1274 } else {
1275 // Process directly result of token call.
1276 db_result = CFRetain(attrs);
1277 }
1278 require_quiet(SecItemResultProcess(attributes, auth_params, token, db_result, result, error), out);
1279
1280 ok = true;
1281
1282 out:
1283 CFReleaseSafe(db_result);
1284 CFReleaseSafe(attrs);
1285 CFReleaseSafe(ref_attrs);
1286 CFReleaseSafe(object_id);
1287 CFReleaseSafe(ref);
1288 return ok;
1289 }
1290
1291 OSStatus
1292 #if SECITEM_SHIM_OSX
1293 SecItemAdd_ios(CFDictionaryRef attributes, CFTypeRef *result)
1294 #else
1295 SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result)
1296 #endif // *** END SECITEM_SHIM_OSX ***
1297 {
1298 __block SecCFDictionaryCOW attrs = { attributes };
1299 OSStatus status;
1300
1301 os_activity_t trace_activity = os_activity_start("SecItemAdd_ios", OS_ACTIVITY_FLAG_DEFAULT);
1302
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 ***
1307
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))
1313 return false;
1314
1315 bool ok = SecItemResultProcess(attributes, auth_params, token, raw_result, result, error);
1316 CFReleaseSafe(raw_result);
1317 return ok;
1318 } else {
1319 // Send request to an appropriate token instead of secd.
1320 return SecTokenItemAdd(token, attributes, auth_params, result, error);
1321 }
1322 });
1323 });
1324
1325 #ifndef SECITEM_SHIM_OSX
1326 errOut:
1327 #endif
1328 CFReleaseSafe(attrs.mutable_dictionary);
1329
1330 os_activity_end(trace_activity);
1331 return status;
1332 }
1333
1334
1335 OSStatus
1336 #if SECITEM_SHIM_OSX
1337 SecItemCopyMatching_ios(CFDictionaryRef inQuery, CFTypeRef *result)
1338 #else
1339 SecItemCopyMatching(CFDictionaryRef inQuery, CFTypeRef *result)
1340 #endif // *** END SECITEM_SHIM_OSX ***
1341 {
1342 OSStatus status;
1343 __block SecCFDictionaryCOW query = { inQuery };
1344
1345 os_activity_t trace_activity = os_activity_start("SecItemCopyMatching_ios", OS_ACTIVITY_FLAG_DEFAULT);
1346
1347 #ifndef SECITEM_SHIM_OSX
1348 require_quiet(!explode_identity(query.dictionary, (secitem_operation)SecItemCopyMatching, &status, result), errOut);
1349 #endif // *** END SECITEM_SHIM_OSX ***
1350
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);
1358 }
1359
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))
1364 return false;
1365
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);
1371 return ok;
1372 });
1373 });
1374
1375 #ifndef SECITEM_SHIM_OSX
1376 errOut:
1377 #endif
1378
1379 CFReleaseSafe(query.mutable_dictionary);
1380 os_activity_end(trace_activity);
1381 return status;
1382 }
1383
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)) {
1388 bool ok = false;
1389 CFMutableDictionaryRef list_query = NULL;
1390 CFTypeRef items = NULL;
1391 CFArrayRef ref_array = NULL;
1392 CFDictionaryRef item_query = NULL, item_data = NULL;
1393
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);
1399 }
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);
1408 }
1409
1410 CFTypeRef item;
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")));
1414
1415 CFAssignRetained(item_data, SecTokenItemValueCopy(data, error));
1416 require_quiet(item_data, out);
1417
1418 CFAssignRetained(item_query,
1419 CFDictionaryCreateForCFTypes(NULL,
1420 kSecValuePersistentRef, CFDictionaryGetValue(item, kSecValuePersistentRef),
1421 NULL));
1422 require_quiet(perform(item_data, item_query, error), out);
1423 }
1424
1425 ok = true;
1426
1427 out:
1428 CFReleaseSafe(list_query);
1429 CFReleaseSafe(items);
1430 CFReleaseSafe(item_data);
1431 CFReleaseSafe(ref_array);
1432 CFReleaseSafe(item_query);
1433 return ok;
1434 }
1435
1436 static bool SecItemRawUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate, CFErrorRef *error) {
1437 bool ok = false;
1438 if (gSecurityd) {
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);
1443 CFRelease(tmp);
1444 } else {
1445 xpc_object_t message = securityd_create_message(sec_item_update_id, error);
1446 if (message) {
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);
1450 if (reply) {
1451 ok = securityd_message_no_error(reply, error);
1452 xpc_release(reply);
1453 }
1454 }
1455 xpc_release(message);
1456 }
1457 }
1458 return ok;
1459 }
1460
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) {
1463 bool ok = false;
1464 CFDataRef object_id = NULL;
1465 CFMutableDictionaryRef db_value = NULL;
1466
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);
1471
1472 // Update attributes in the database.
1473 require_quiet(SecItemRawUpdate(item_query, attributes, error), out);
1474
1475 ok = true;
1476
1477 out:
1478 CFReleaseSafe(object_id);
1479 CFReleaseSafe(attributes);
1480 CFReleaseSafe(db_value);
1481 return ok;
1482 });
1483 }
1484
1485 OSStatus
1486 #if SECITEM_SHIM_OSX
1487 SecItemUpdate_ios(CFDictionaryRef inQuery, CFDictionaryRef inAttributesToUpdate)
1488 #else
1489 SecItemUpdate(CFDictionaryRef inQuery, CFDictionaryRef inAttributesToUpdate)
1490 #endif // *** END SECITEM_SHIM_OSX ***
1491 {
1492 OSStatus status;
1493 __block SecCFDictionaryCOW query = { inQuery };
1494 __block SecCFDictionaryCOW attributesToUpdate = { inAttributesToUpdate };
1495
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);
1500 } else {
1501 return SecTokenItemUpdate(token, query, attributes, error);
1502 }
1503 });
1504 });
1505
1506 CFReleaseSafe(query.mutable_dictionary);
1507 CFReleaseSafe(attributesToUpdate.mutable_dictionary);
1508 return status;
1509 }
1510
1511 #ifndef SECITEM_SHIM_OSX
1512 static OSStatus explode_persistent_identity_ref(SecCFDictionaryCOW *query)
1513 {
1514 OSStatus status = errSecSuccess;
1515 CFTypeRef persist = CFDictionaryGetValue(query->dictionary, kSecValuePersistentRef);
1516 CFStringRef class;
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);
1526 if (status)
1527 return status;
1528
1529 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(query), kSecValuePersistentRef);
1530 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(query), kSecValueRef, item_query);
1531 CFRelease(item_query);
1532 }
1533
1534 return status;
1535 }
1536 #endif
1537
1538 OSStatus
1539 #if SECITEM_SHIM_OSX
1540 SecItemDelete_ios(CFDictionaryRef inQuery)
1541 #else
1542 SecItemDelete(CFDictionaryRef inQuery)
1543 #endif // *** END SECITEM_SHIM_OSX ***
1544 {
1545 OSStatus status;
1546 __block SecCFDictionaryCOW query = { inQuery };
1547
1548 os_activity_t trace_activity = os_activity_start("SecItemDelete_ios", OS_ACTIVITY_FLAG_DEFAULT);
1549
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 ***
1554
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);
1559 } else {
1560 return SecTokenItemForEachMatching(query, error, ^bool(CFDictionaryRef object_data, CFDictionaryRef item_query, CFErrorRef *error) {
1561 bool ok = false;
1562
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));
1567
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);
1571 ok = true;
1572
1573 out:
1574 return ok;
1575 });
1576 }
1577 });
1578 });
1579
1580 #ifndef SECITEM_SHIM_OSX
1581 errOut:
1582 #endif
1583
1584 CFReleaseSafe(query.mutable_dictionary);
1585
1586 os_activity_end(trace_activity);
1587
1588 return status;
1589 }
1590
1591 OSStatus
1592 SecItemDeleteAll(void)
1593 {
1594 return SecOSStatusWith(^bool (CFErrorRef *error) {
1595 bool ok;
1596 if (gSecurityd) {
1597 ok = true;
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"));
1605 } else {
1606 ok = securityd_send_sync_and_do(sec_delete_all_id, error, NULL, NULL);
1607 }
1608 return ok;
1609 });
1610 }
1611
1612
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);
1617 });
1618 return result;
1619 }
1620
1621 #ifndef SECITEM_SHIM_OSX
1622 OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement);
1623
1624 OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement)
1625 {
1626 return -1; /* this is only on OS X currently */
1627 }
1628
1629 #else
1630
1631 extern OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement);
1632
1633 #endif
1634
1635 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
1636
1637 bool _SecKeychainRollKeys(bool force, CFErrorRef *error)
1638 {
1639 do_if_registered(sec_roll_keys, force, error);
1640
1641 __block bool result = false;
1642
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);
1647 return true;
1648 },
1649 ^bool(xpc_object_t response, __unused CFErrorRef *error) {
1650 result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
1651 return result;
1652 });
1653 return result;
1654 }
1655
1656