]> git.saurik.com Git - apple/security.git/blob - keychain/securityd/SecItemDb.c
Security-59754.41.1.tar.gz
[apple/security.git] / keychain / securityd / SecItemDb.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 * SecItemDb.c - CoreFoundation-based constants and functions for
26 access to Security items (certificates, keys, identities, and
27 passwords.)
28 */
29
30 #if TARGET_DARWINOS
31 #undef OCTAGON
32 #undef SECUREOBJECTSYNC
33 #undef SHAREDWEBCREDENTIALS
34 #endif
35
36 #include "keychain/securityd/SecItemDb.h"
37 #include <utilities/SecAKSWrappers.h>
38
39 #include "keychain/securityd/SecDbKeychainItem.h"
40 #include "keychain/securityd/SecItemSchema.h"
41 #include "keychain/securityd/SecItemServer.h"
42 #include <Security/SecAccessControlPriv.h>
43 #include <Security/SecBasePriv.h>
44 #include <Security/SecItem.h>
45 #include <Security/SecSignpost.h>
46 #include <Security/SecItemPriv.h>
47 #include <Security/SecItemInternal.h>
48 #include "keychain/securityd/SOSCloudCircleServer.h"
49 #include <utilities/array_size.h>
50 #include <utilities/SecIOFormat.h>
51 #include <utilities/SecCFCCWrappers.h>
52 #include <uuid/uuid.h>
53
54 #include "utilities/SecABC.h"
55 #include "utilities/sec_action.h"
56
57 #include "keychain/ckks/CKKS.h"
58
59 #define kSecBackupKeybagUUIDKey CFSTR("keybag-uuid")
60
61 const SecDbAttr *SecDbAttrWithKey(const SecDbClass *c,
62 CFTypeRef key,
63 CFErrorRef *error) {
64 /* Special case: identites can have all attributes of either cert
65 or keys. */
66 if (c == identity_class()) {
67 const SecDbAttr *desc;
68 if (!(desc = SecDbAttrWithKey(cert_class(), key, 0)))
69 desc = SecDbAttrWithKey(keys_class(), key, error);
70 return desc;
71 }
72
73 if (isString(key)) {
74 SecDbForEachAttr(c, a) {
75 if (CFEqual(a->name, key))
76 return a;
77 }
78 if (CFEqual(kSecUseDataProtectionKeychain, key)) {
79 return NULL; /* results in no ops for this attribute */
80 }
81 }
82
83 SecError(errSecNoSuchAttr, error, CFSTR("attribute %@ not found in class %@"), key, c->name);
84
85 return NULL;
86 }
87
88 bool kc_transaction(SecDbConnectionRef dbt, CFErrorRef *error, bool(^perform)(void)) {
89 return kc_transaction_type(dbt, kSecDbExclusiveTransactionType, error, perform);
90 }
91
92 bool kc_transaction_type(SecDbConnectionRef dbt, SecDbTransactionType type, CFErrorRef *error, bool(^perform)(void)) {
93 __block bool ok = true;
94 return ok && SecDbTransaction(dbt, type, error, ^(bool *commit) {
95 ok = *commit = perform();
96 });
97 }
98
99 static CFStringRef SecDbGetKindSQL(SecDbAttrKind kind) {
100 switch (kind) {
101 case kSecDbBlobAttr:
102 case kSecDbDataAttr:
103 case kSecDbUUIDAttr:
104 case kSecDbSHA1Attr:
105 case kSecDbPrimaryKeyAttr:
106 case kSecDbEncryptedDataAttr:
107 return CFSTR("BLOB");
108 case kSecDbAccessAttr:
109 case kSecDbStringAttr:
110 return CFSTR("TEXT");
111 case kSecDbNumberAttr:
112 case kSecDbSyncAttr:
113 case kSecDbTombAttr:
114 return CFSTR("INTEGER");
115 case kSecDbDateAttr:
116 case kSecDbCreationDateAttr:
117 case kSecDbModificationDateAttr:
118 return CFSTR("REAL");
119 case kSecDbRowIdAttr:
120 return CFSTR("INTEGER PRIMARY KEY AUTOINCREMENT");
121 case kSecDbAccessControlAttr:
122 case kSecDbUTombAttr:
123 /* This attribute does not exist in the DB. */
124 return NULL;
125 }
126 }
127
128 static void SecDbAppendUnique(CFMutableStringRef sql, CFStringRef value, bool *haveUnique) {
129 assert(haveUnique);
130 if (!*haveUnique)
131 CFStringAppend(sql, CFSTR("UNIQUE("));
132
133 SecDbAppendElement(sql, value, haveUnique);
134 }
135
136 static void SecDbAppendCreateTableWithClass(CFMutableStringRef sql, const SecDbClass *c) {
137 CFStringAppendFormat(sql, 0, CFSTR("CREATE TABLE %@("), c->name);
138 SecDbForEachAttrWithMask(c,desc,kSecDbInFlag) {
139 CFStringAppendFormat(sql, 0, CFSTR("%@ %@"), desc->name, SecDbGetKindSQL(desc->kind));
140 if (desc->flags & kSecDbNotNullFlag)
141 CFStringAppend(sql, CFSTR(" NOT NULL"));
142 if (desc->flags & kSecDbDefault0Flag)
143 CFStringAppend(sql, CFSTR(" DEFAULT 0"));
144 if (desc->flags & kSecDbDefaultEmptyFlag)
145 CFStringAppend(sql, CFSTR(" DEFAULT ''"));
146 CFStringAppend(sql, CFSTR(","));
147 }
148
149 bool haveUnique = false;
150 SecDbForEachAttrWithMask(c,desc,kSecDbPrimaryKeyFlag | kSecDbInFlag) {
151 SecDbAppendUnique(sql, desc->name, &haveUnique);
152 }
153 if (haveUnique)
154 CFStringAppend(sql, CFSTR(")"));
155
156 CFStringAppend(sql, CFSTR(");"));
157
158 // Create indices
159 SecDbForEachAttrWithMask(c,desc, kSecDbIndexFlag | kSecDbInFlag) {
160 if (desc->kind == kSecDbSyncAttr) {
161 CFStringAppendFormat(sql, 0, CFSTR("CREATE INDEX %@%@0 ON %@(%@) WHERE %@=0;"), c->name, desc->name, c->name, desc->name, desc->name);
162 } else {
163 CFStringAppendFormat(sql, 0, CFSTR("CREATE INDEX %@%@ ON %@(%@);"), c->name, desc->name, c->name, desc->name);
164 }
165 }
166 }
167
168 static void SecDbAppendDropTableWithClass(CFMutableStringRef sql, const SecDbClass *c) {
169 CFStringAppendFormat(sql, 0, CFSTR("DROP TABLE %@;"), c->name);
170 }
171
172 static CFDataRef SecPersistentRefCreateWithItem(SecDbItemRef item, CFErrorRef *error) {
173 sqlite3_int64 row_id = SecDbItemGetRowId(item, error);
174 if (row_id)
175 return _SecItemCreatePersistentRef(SecDbItemGetClass(item)->name, row_id, item->attributes);
176 return NULL;
177 }
178
179 bool SecItemDbCreateSchema(SecDbConnectionRef dbt, const SecDbSchema *schema, CFArrayRef classIndexesForNewTables, bool includeVersion, CFErrorRef *error)
180 {
181 __block bool ok = true;
182 CFMutableStringRef sql = CFStringCreateMutable(kCFAllocatorDefault, 0);
183
184 if (classIndexesForNewTables) {
185 CFArrayForEach(classIndexesForNewTables, ^(const void* index) {
186 const SecDbClass* class = schema->classes[(int)index];
187 SecDbAppendCreateTableWithClass(sql, class);
188 });
189 }
190 else {
191 for (const SecDbClass * const *pclass = schema->classes; *pclass; ++pclass) {
192 SecDbAppendCreateTableWithClass(sql, *pclass);
193 }
194 }
195
196 if (includeVersion) {
197 CFStringAppendFormat(sql, NULL, CFSTR("INSERT INTO tversion(version,minor) VALUES(%d, %d);"),
198 schema->majorVersion, schema->minorVersion);
199 }
200 CFStringPerformWithCString(sql, ^(const char *sql_string) {
201 ok = SecDbErrorWithDb(sqlite3_exec(SecDbHandle(dbt), sql_string, NULL, NULL, NULL),
202 SecDbHandle(dbt), error, CFSTR("sqlite3_exec: %s"), sql_string);
203 });
204 CFReleaseSafe(sql);
205 return ok;
206 }
207
208 bool SecItemDbDeleteSchema(SecDbConnectionRef dbt, const SecDbSchema *schema, CFErrorRef *error)
209 {
210 __block bool ok = true;
211 CFMutableStringRef sql = CFStringCreateMutable(kCFAllocatorDefault, 0);
212 for (const SecDbClass * const *pclass = schema->classes; *pclass; ++pclass) {
213 SecDbAppendDropTableWithClass(sql, *pclass);
214 }
215 CFStringPerformWithCString(sql, ^(const char *sql_string) {
216 ok = SecDbErrorWithDb(sqlite3_exec(SecDbHandle(dbt), sql_string, NULL, NULL, NULL),
217 SecDbHandle(dbt), error, CFSTR("sqlite3_exec: %s"), sql_string);
218 });
219 CFReleaseSafe(sql);
220 return ok;
221 }
222
223 CFTypeRef SecDbItemCopyResult(SecDbItemRef item, ReturnTypeMask return_type, CFErrorRef *error) {
224 CFTypeRef a_result;
225
226 if (return_type == 0) {
227 /* Caller isn't interested in any results at all. */
228 a_result = kCFNull;
229 } else if (return_type == kSecReturnDataMask) {
230 a_result = SecDbItemGetCachedValueWithName(item, kSecValueData);
231 if (a_result) {
232 CFRetainSafe(a_result);
233 } else {
234 a_result = CFDataCreate(kCFAllocatorDefault, NULL, 0);
235 }
236 } else if (return_type == kSecReturnPersistentRefMask) {
237 a_result = SecPersistentRefCreateWithItem(item, error);
238 } else {
239 CFMutableDictionaryRef dict = CFDictionaryCreateMutableForCFTypes(CFGetAllocator(item));
240 /* We need to return more than one value. */
241 if (return_type & kSecReturnRefMask) {
242 CFDictionarySetValue(dict, kSecClass, SecDbItemGetClass(item)->name);
243 }
244 CFOptionFlags mask = (((return_type & kSecReturnDataMask || return_type & kSecReturnRefMask) ? kSecDbReturnDataFlag : 0) |
245 ((return_type & kSecReturnAttributesMask || return_type & kSecReturnRefMask) ? kSecDbReturnAttrFlag : 0));
246 SecDbForEachAttr(SecDbItemGetClass(item), desc) {
247 if ((desc->flags & mask) != 0) {
248 CFTypeRef value = SecDbItemGetValue(item, desc, error);
249 if (value && !CFEqual(kCFNull, value)) {
250 CFDictionarySetValue(dict, desc->name, value);
251 } else if (value == NULL) {
252 CFReleaseNull(dict);
253 break;
254 }
255 }
256 }
257 CFDictionaryRemoveValue(dict, kSecAttrUUID);
258
259 if (return_type & kSecReturnPersistentRefMask) {
260 CFDataRef pref = SecPersistentRefCreateWithItem(item, error);
261 CFDictionarySetValue(dict, kSecValuePersistentRef, pref);
262 CFReleaseSafe(pref);
263 }
264
265 a_result = dict;
266 }
267
268 return a_result;
269 }
270
271 /* AUDIT[securityd](done):
272 attributes (ok) is a caller provided dictionary, only its cf type has
273 been checked.
274 */
275 bool
276 s3dl_query_add(SecDbConnectionRef dbt, Query *q, CFTypeRef *result, CFErrorRef *error)
277 {
278 if (query_match_count(q) != 0)
279 return errSecItemMatchUnsupported;
280
281 /* Add requires a class to be specified unless we are adding a ref. */
282 if (q->q_use_item_list)
283 return errSecUseItemListUnsupported;
284
285 /* Actual work here. */
286 SecDbItemRef item = SecDbItemCreateWithAttributes(kCFAllocatorDefault, q->q_class, q->q_item, KEYBAG_DEVICE, error);
287 if (!item)
288 return false;
289 if (SecDbItemIsTombstone(item))
290 SecDbItemSetValue(item, &v7utomb, q->q_use_tomb ? q->q_use_tomb : kCFBooleanTrue, NULL);
291
292 bool ok = true;
293 if (q->q_data)
294 ok = SecDbItemSetValueWithName(item, CFSTR("v_Data"), q->q_data, error);
295 if (q->q_row_id)
296 ok = SecDbItemSetRowId(item, q->q_row_id, error);
297 if (q->q_musrView)
298 ok = SecDbItemSetValueWithName(item, CFSTR("musr"), q->q_musrView, error);
299 SecDbItemSetCredHandle(item, q->q_use_cred_handle);
300
301 #if OCTAGON
302 if(SecCKKSIsEnabled() && !SecCKKSTestDisableAutomaticUUID()) {
303 s3dl_item_make_new_uuid(item, q->q_uuid_from_primary_key, error);
304
305 if(q->q_add_sync_callback) {
306 CFTypeRef uuid = SecDbItemGetValue(item, &v10itemuuid, error);
307 if(uuid) {
308 CKKSRegisterSyncStatusCallback(uuid, q->q_add_sync_callback);
309 } else {
310 secerror("Couldn't fetch UUID from item; can't call callback");
311 }
312 }
313 }
314 #endif
315
316 if (ok) {
317 // We care about the new item's UUID only when we just made it from the primary key.
318 // Otherwise, we just made a random one, and don't mind if it changes.
319 ok = SecDbItemInsert(item, dbt, q->q_uuid_from_primary_key, error);
320 }
321
322 if (ok) {
323 if (result && q->q_return_type) {
324 *result = SecDbItemCopyResult(item, q->q_return_type, error);
325 }
326 }
327 if (!ok && error && *error) {
328 if (CFEqual(CFErrorGetDomain(*error), kSecDbErrorDomain) && CFErrorGetCode(*error) == SQLITE_CONSTRAINT) {
329 CFReleaseNull(*error);
330 SecError(errSecDuplicateItem, error, CFSTR("duplicate item %@"), item);
331 } else if (CFEqual(CFErrorGetDomain(*error), kSecErrorDomain) && CFErrorGetCode(*error) == errSecDecode) { //handle situation when item have pdmn=akpu but passcode is not set
332 CFTypeRef value = SecDbItemGetValue(item, SecDbClassAttrWithKind(item->class, kSecDbAccessAttr, error), error);
333 if (value && CFEqual(value, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly)) {
334 CFReleaseNull(*error);
335 SecError(errSecAuthFailed, error, CFSTR("authentication failed"));
336 }
337 }
338 }
339
340 if (ok) {
341 q->q_changed = true;
342 if (SecDbItemIsSyncable(item))
343 q->q_sync_changed = true;
344 }
345
346 secdebug("dbitem", "inserting item %@%s%@", item, ok ? "" : "failed: ", ok || error == NULL ? (CFErrorRef)CFSTR("") : *error);
347
348 CFRelease(item);
349
350 return ok;
351 }
352
353 bool s3dl_item_make_new_uuid(SecDbItemRef item, bool uuid_from_primary_key, CFErrorRef* error) {
354 if(!item) {
355 return false;
356 }
357
358 // Set the item UUID.
359 CFUUIDRef uuid = NULL;
360 // Were we asked to make the UUID static?
361 if (uuid_from_primary_key) {
362 // This UUID isn't used in any security mechanism, so we can
363 // just use the first bits of the SHA256 hash.
364 CFDataRef pkhash = SecDbKeychainItemCopySHA256PrimaryKey(item, error);
365 if(CFDataGetLength(pkhash) >= 16) {
366 UInt8 uuidBytes[16];
367 CFRange range = CFRangeMake(0, 16);
368 CFDataGetBytes(pkhash, range, uuidBytes);
369
370 uuid = CFUUIDCreateWithBytes(NULL,
371 uuidBytes[ 0],
372 uuidBytes[ 1],
373 uuidBytes[ 2],
374 uuidBytes[ 3],
375 uuidBytes[ 4],
376 uuidBytes[ 5],
377 uuidBytes[ 6],
378 uuidBytes[ 7],
379 uuidBytes[ 8],
380 uuidBytes[ 9],
381 uuidBytes[10],
382 uuidBytes[11],
383 uuidBytes[12],
384 uuidBytes[13],
385 uuidBytes[14],
386 uuidBytes[15]);
387 }
388 CFReleaseNull(pkhash);
389 }
390 if(uuid == NULL) {
391 uuid = CFUUIDCreate(NULL);
392 }
393 SecDbItemSetValueWithName(item, kSecAttrUUID, uuid, error);
394 CFReleaseNull(uuid);
395 return true;
396 }
397
398 typedef void (*s3dl_handle_row)(sqlite3_stmt *stmt, void *context);
399
400 static CFDataRef
401 s3dl_copy_data_from_col(sqlite3_stmt *stmt, int col, CFErrorRef *error) {
402 return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt, col),
403 sqlite3_column_bytes(stmt, col),
404 kCFAllocatorNull);
405 }
406
407 static bool
408 s3dl_item_from_col(sqlite3_stmt *stmt, Query *q, int col, CFArrayRef accessGroups,
409 CFMutableDictionaryRef *item, SecAccessControlRef *access_control, keyclass_t* keyclass, CFErrorRef *error) {
410 CFDataRef edata = NULL;
411 bool ok = false;
412 require(edata = s3dl_copy_data_from_col(stmt, col, error), out);
413 ok = s3dl_item_from_data(edata, q, accessGroups, item, access_control, keyclass, error);
414
415 out:
416 CFReleaseSafe(edata);
417 return ok;
418 }
419
420 struct s3dl_query_ctx {
421 Query *q;
422 CFArrayRef accessGroups;
423 SecDbConnectionRef dbt;
424 CFTypeRef result;
425 int found;
426 };
427
428 /* Return whatever the caller requested based on the value of q->q_return_type.
429 keys and values must be 3 larger than attr_count in size to accomadate the
430 optional data, class and persistent ref results. This is so we can use
431 the CFDictionaryCreate() api here rather than appending to a
432 mutable dictionary. */
433 static CF_RETURNS_RETAINED CFTypeRef
434 handle_result(Query *q,
435 CFMutableDictionaryRef item,
436 sqlite_int64 rowid)
437 {
438 CFTypeRef a_result;
439 CFDataRef data;
440 data = CFDictionaryGetValue(item, kSecValueData);
441 CFDataRef pref = NULL;
442 if (q->q_return_type & kSecReturnPersistentRefMask) {
443 pref = _SecItemCreatePersistentRef(q->q_class->name, rowid, item);
444 }
445 if (q->q_return_type == 0) {
446 /* Caller isn't interested in any results at all. */
447 a_result = kCFNull;
448 } else if (q->q_return_type == kSecReturnDataMask) {
449 if (data) {
450 a_result = data;
451 CFRetain(a_result);
452 } else {
453 a_result = CFDataCreate(kCFAllocatorDefault, NULL, 0);
454 }
455 } else if (q->q_return_type == kSecReturnPersistentRefMask) {
456 a_result = _SecItemCreatePersistentRef(q->q_class->name, rowid, item);
457 } else {
458 /* We need to return more than one value. */
459 if (q->q_return_type & kSecReturnRefMask) {
460 CFDictionarySetValue(item, kSecClass, q->q_class->name);
461 } else if ((q->q_return_type & kSecReturnAttributesMask)) {
462 if (!(q->q_return_type & kSecReturnDataMask)) {
463 CFDictionaryRemoveValue(item, kSecValueData);
464 }
465
466 // Add any attributes which are supposed to be returned, are not present in the decrypted blob,
467 // and have a way to generate themselves.
468 SecDbItemRef itemRef = NULL;
469 SecDbForEachAttrWithMask(q->q_class, attr, kSecDbReturnAttrFlag) {
470 if(!CFDictionaryGetValue(item, attr->name) && attr->copyValue) {
471 CFErrorRef cferror = NULL;
472 if(!itemRef) {
473 itemRef = SecDbItemCreateWithAttributes(NULL, q->q_class, item, KEYBAG_DEVICE, &cferror);
474 }
475 if(!cferror && itemRef) {
476 if (attr->kind != kSecDbSHA1Attr || (q->q_return_type & kSecReturnDataMask)) { // we'll skip returning the sha1 attribute unless the client has also asked us to return data, because without data our sha1 could be invalid
477 CFTypeRef attrValue = attr->copyValue(itemRef, attr, &cferror);
478 if (!cferror && attrValue) {
479 CFDictionarySetValue(item, attr->name, attrValue);
480 }
481 CFReleaseNull(attrValue);
482 }
483 }
484 CFReleaseNull(cferror);
485 }
486 }
487 CFReleaseNull(itemRef);
488
489 CFDictionaryRemoveValue(item, kSecAttrUUID);
490 } else {
491 CFRetainSafe(data);
492 CFDictionaryRemoveAllValues(item);
493 if ((q->q_return_type & kSecReturnDataMask) && data) {
494 CFDictionarySetValue(item, kSecValueData, data);
495 }
496 CFReleaseSafe(data);
497 }
498 if (q->q_return_type & kSecReturnPersistentRefMask && pref != NULL) {
499 CFDictionarySetValue(item, kSecValuePersistentRef, pref);
500 }
501
502 a_result = item;
503 CFRetain(item);
504 }
505 CFReleaseSafe(pref);
506 return a_result;
507 }
508
509 static void s3dl_merge_into_dict(const void *key, const void *value, void *context) {
510 CFDictionarySetValue(context, key, value);
511 }
512
513 static bool checkTokenObjectID(CFDataRef token_object_id, CFDataRef value_data) {
514 bool equalOID = false;
515 require_quiet(value_data, out);
516 CFDictionaryRef itemValue = SecTokenItemValueCopy(value_data, NULL);
517 require_quiet(itemValue, out);
518 CFDataRef oID = CFDictionaryGetValue(itemValue, kSecTokenValueObjectIDKey);
519 equalOID = CFEqualSafe(token_object_id, oID);
520 CFRelease(itemValue);
521 out:
522 return equalOID;
523 }
524
525 static void s3dl_query_row(sqlite3_stmt *stmt, void *context) {
526 struct s3dl_query_ctx *c = context;
527 Query *q = c->q;
528 ReturnTypeMask saved_mask = q->q_return_type;
529
530 sqlite_int64 rowid = sqlite3_column_int64(stmt, 0);
531 CFMutableDictionaryRef item = NULL;
532 bool ok;
533
534 decode:
535 ok = s3dl_item_from_col(stmt, q, 1, c->accessGroups, &item, NULL, NULL, &q->q_error);
536 if (!ok) {
537 OSStatus status = SecErrorGetOSStatus(q->q_error);
538 // errSecDecode means the item is corrupted, stash it for delete.
539 if (status == errSecDecode) {
540 secwarning("ignoring corrupt %@,rowid=%" PRId64 " %@", q->q_class->name, rowid, q->q_error);
541
542 // Can't get rid of this item on the read path. Let's come back from elsewhere.
543 CFStringRef tablename = CFStringCreateCopy(kCFAllocatorDefault, q->q_class->name);
544 deleteCorruptedItemAsync(c->dbt, tablename, rowid);
545 CFReleaseNull(tablename);
546
547 // provide helpful logging statement
548 CFDataRef edata = s3dl_copy_data_from_col(stmt, 1, NULL);
549 CFMutableStringRef edatastring = CFStringCreateMutable(kCFAllocatorDefault, 0);
550 if(edatastring) {
551 CFStringAppendEncryptedData(edatastring, edata);
552 secnotice("item", "corrupted edata=%@", edatastring);
553 }
554 CFReleaseSafe(edata);
555 CFReleaseSafe(edatastring);
556
557 CFReleaseNull(q->q_error); // This item was never here, keep going
558 } else if (status == errSecAuthNeeded) {
559 secwarning("Authentication is needed for %@,rowid=%" PRId64 " (%" PRIdOSStatus "): %@", q->q_class->name, rowid, status, q->q_error);
560 } else if (status == errSecInteractionNotAllowed) {
561 static dispatch_once_t kclockedtoken;
562 static sec_action_t kclockedaction;
563 dispatch_once(&kclockedtoken, ^{
564 kclockedaction = sec_action_create("ratelimiterdisabledlogevent", 1);
565 sec_action_set_handler(kclockedaction, ^{
566 secerror("decode item failed, keychain is locked (%d)", (int)errSecInteractionNotAllowed);
567 });
568 });
569 sec_action_perform(kclockedaction);
570 } else if (status == errSecMissingEntitlement) {
571 // That's fine, let's pretend the item never existed for this query.
572 // We may find other, better items for the caller!
573 CFReleaseNull(q->q_error);
574 } else {
575 secerror("decode %@,rowid=%" PRId64 " failed (%" PRIdOSStatus "): %@", q->q_class->name, rowid, status, q->q_error);
576 }
577 // q->q_error will be released appropriately by a call to query_error
578 return;
579 }
580
581 if (!item)
582 goto out;
583
584 if (CFDictionaryContainsKey(item, kSecAttrTokenID) && (q->q_return_type & kSecReturnDataMask) == 0) {
585 // For token-based items, to get really meaningful set of attributes we must provide also data field, so augment mask
586 // and restart item decoding cycle.
587 q->q_return_type |= kSecReturnDataMask;
588 CFReleaseNull(item);
589 goto decode;
590 }
591
592 if (q->q_token_object_id != NULL && !checkTokenObjectID(q->q_token_object_id, CFDictionaryGetValue(item, kSecValueData)))
593 goto out;
594
595 if (q->q_class == identity_class()) {
596 // TODO: Use col 2 for key rowid and use both rowids in persistent ref.
597
598 CFMutableDictionaryRef key;
599 /* TODO : if there is a errSecDecode error here, we should cleanup */
600 if (!s3dl_item_from_col(stmt, q, 3, c->accessGroups, &key, NULL, NULL, &q->q_error) || !key)
601 goto out;
602
603 CFDataRef certData = CFDictionaryGetValue(item, kSecValueData);
604 if (certData) {
605 CFDictionarySetValue(key, kSecAttrIdentityCertificateData, certData);
606 CFDictionaryRemoveValue(item, kSecValueData);
607 }
608
609 CFDataRef certTokenID = CFDictionaryGetValue(item, kSecAttrTokenID);
610 if (certTokenID) {
611 CFDictionarySetValue(key, kSecAttrIdentityCertificateTokenID, certTokenID);
612 CFDictionaryRemoveValue(item, kSecAttrTokenID);
613 }
614 CFDictionaryApplyFunction(item, s3dl_merge_into_dict, key);
615 CFRelease(item);
616 item = key;
617 }
618
619 if (!match_item(c->dbt, q, c->accessGroups, item))
620 goto out;
621
622 CFTypeRef a_result = handle_result(q, item, rowid);
623 if (a_result) {
624 if (a_result == kCFNull) {
625 /* Caller wasn't interested in a result, but we still
626 count this row as found. */
627 CFRelease(a_result); // Help shut up clang
628 } else if (q->q_limit == 1) {
629 c->result = a_result;
630 } else {
631 CFArrayAppendValue((CFMutableArrayRef)c->result, a_result);
632 CFRelease(a_result);
633 }
634 c->found++;
635 }
636
637 out:
638 q->q_return_type = saved_mask;
639 CFReleaseSafe(item);
640 }
641
642 static void
643 SecDbAppendWhereROWID(CFMutableStringRef sql,
644 CFStringRef col, sqlite_int64 row_id,
645 bool *needWhere) {
646 if (row_id > 0) {
647 SecDbAppendWhereOrAnd(sql, needWhere);
648 CFStringAppendFormat(sql, NULL, CFSTR("%@=%lld"), col, row_id);
649 }
650 }
651
652 static void
653 SecDbAppendWhereAttrs(CFMutableStringRef sql, const Query *q, bool *needWhere) {
654 CFIndex ix, attr_count = query_attr_count(q);
655 for (ix = 0; ix < attr_count; ++ix) {
656 SecDbAppendWhereOrAndEquals(sql, query_attr_at(q, ix).key, needWhere);
657 }
658 }
659
660 static void
661 SecDbAppendWhereAccessGroups(CFMutableStringRef sql,
662 CFStringRef col,
663 CFArrayRef accessGroups,
664 bool *needWhere) {
665 CFIndex ix, ag_count;
666 if (!accessGroups || 0 == (ag_count = CFArrayGetCount(accessGroups))) {
667 return;
668 }
669
670 SecDbAppendWhereOrAnd(sql, needWhere);
671 CFStringAppend(sql, col);
672 CFStringAppend(sql, CFSTR(" IN (?"));
673 for (ix = 1; ix < ag_count; ++ix) {
674 CFStringAppend(sql, CFSTR(",?"));
675 }
676 CFStringAppend(sql, CFSTR(")"));
677 }
678
679 static bool
680 isQueryOverAllMUSRViews(CFTypeRef musrView)
681 {
682 return SecMUSRIsViewAllViews(musrView);
683 }
684
685 static bool
686 isQueryOverSingleUserView(CFTypeRef musrView)
687 {
688 return isNull(musrView);
689 }
690
691 #if TARGET_OS_IPHONE
692 static bool
693 isQueryOverBothUserAndSystem(CFTypeRef musrView, uid_t *uid)
694 {
695 return SecMUSRGetBothUserAndSystemUUID(musrView, uid);
696 }
697 #endif
698
699 static void
700 SecDbAppendWhereMusr(CFMutableStringRef sql,
701 const Query *q,
702 bool *needWhere)
703 {
704 SecDbAppendWhereOrAnd(sql, needWhere);
705
706 #if TARGET_OS_IPHONE
707 if (isQueryOverBothUserAndSystem(q->q_musrView, NULL)) {
708 CFStringAppend(sql, CFSTR("(musr = ? OR musr = ?)"));
709 } else
710 #endif
711 if (isQueryOverAllMUSRViews(q->q_musrView)) {
712 /* query over all items, regardless of view */
713 } else if (isQueryOverSingleUserView(q->q_musrView)) {
714 CFStringAppend(sql, CFSTR("musr = ?"));
715 } else {
716 CFStringAppend(sql, CFSTR("musr = ?"));
717 }
718 }
719
720 static void
721 SecDbAppendWhereAppClip(CFMutableStringRef sql,
722 const Query* q,
723 bool* needWhere)
724 {
725 if (!q->q_skip_app_clip_items) {
726 return;
727 }
728
729 SecDbAppendWhereOrAnd(sql, needWhere);
730 CFStringAppend(sql, CFSTR("clip = 0"));
731 }
732
733 static void SecDbAppendWhereClause(CFMutableStringRef sql, const Query *q,
734 CFArrayRef accessGroups) {
735 bool needWhere = true;
736 SecDbAppendWhereROWID(sql, CFSTR("ROWID"), q->q_row_id, &needWhere);
737 SecDbAppendWhereAttrs(sql, q, &needWhere);
738 SecDbAppendWhereMusr(sql, q, &needWhere);
739 SecDbAppendWhereAccessGroups(sql, CFSTR("agrp"), accessGroups, &needWhere);
740 SecDbAppendWhereAppClip(sql, q, &needWhere);
741 }
742
743 static void SecDbAppendLimit(CFMutableStringRef sql, CFIndex limit) {
744 if (limit != kSecMatchUnlimited)
745 CFStringAppendFormat(sql, NULL, CFSTR(" LIMIT %" PRIdCFIndex), limit);
746 }
747
748 static CFStringRef s3dl_create_select_sql(Query *q, CFArrayRef accessGroups) {
749 CFMutableStringRef sql = CFStringCreateMutable(NULL, 0);
750 if (q->q_class == identity_class()) {
751 CFStringAppend(sql, CFSTR("SELECT crowid, certdata"
752 ", rowid,data FROM "
753 "(SELECT cert.rowid AS crowid, cert.labl AS labl,"
754 " cert.issr AS issr, cert.slnr AS slnr, cert.skid AS skid,"
755 " cert.tkid AS tkid,"
756 " keys.*,cert.data AS certdata"
757 " FROM keys, cert"
758 " WHERE keys.priv == 1 AND cert.pkhh == keys.klbl"));
759 SecDbAppendWhereAccessGroups(sql, CFSTR("cert.agrp"), accessGroups, 0);
760 /* The next 3 SecDbAppendWhere calls are in the same order as in
761 SecDbAppendWhereClause(). This makes sqlBindWhereClause() work,
762 as long as we do an extra sqlBindAccessGroups first. */
763 SecDbAppendWhereROWID(sql, CFSTR("crowid"), q->q_row_id, 0);
764 CFStringAppend(sql, CFSTR(")"));
765 bool needWhere = true;
766 SecDbAppendWhereAttrs(sql, q, &needWhere);
767 SecDbAppendWhereMusr(sql, q, &needWhere);
768 SecDbAppendWhereAccessGroups(sql, CFSTR("agrp"), accessGroups, &needWhere);
769 SecDbAppendWhereAppClip(sql, q, &needWhere);
770 } else {
771 // Most of the time we don't need agrp, but if an item fails to decode and we want to know more then this is helpful
772 CFStringAppend(sql, CFSTR("SELECT rowid, data, agrp FROM "));
773 CFStringAppend(sql, q->q_class->name);
774 SecDbAppendWhereClause(sql, q, accessGroups);
775 }
776 //do not append limit for all queries which needs filtering
777 if (q->q_match_issuer == NULL && q->q_match_policy == NULL && q->q_match_valid_on_date == NULL && q->q_match_trusted_only == NULL && q->q_token_object_id == NULL) {
778 SecDbAppendLimit(sql, q->q_limit);
779 }
780
781 return sql;
782 }
783
784 static bool sqlBindMusr(sqlite3_stmt *stmt, const Query *q, int *pParam, CFErrorRef *error) {
785 int param = *pParam;
786 bool result = true;
787 #if TARGET_OS_IPHONE
788 uid_t uid;
789
790 if (isQueryOverBothUserAndSystem(q->q_musrView, &uid)) {
791 /* network extensions are special and get to query both user and system views */
792 CFDataRef systemUUID = SecMUSRGetSystemKeychainUUID();
793 result = SecDbBindObject(stmt, param++, systemUUID, error);
794 if (result) {
795 CFDataRef activeUser = SecMUSRCreateActiveUserUUID(uid);
796 result = SecDbBindObject(stmt, param++, activeUser, error);
797 CFReleaseNull(activeUser);
798 }
799 } else
800 #endif
801 if (isQueryOverAllMUSRViews(q->q_musrView)) {
802 /* query over all items, regardless of view */
803 } else if (isQueryOverSingleUserView(q->q_musrView)) {
804 CFDataRef singleUUID = SecMUSRGetSingleUserKeychainUUID();
805 result = SecDbBindObject(stmt, param++, singleUUID, error);
806 } else {
807 result = SecDbBindObject(stmt, param++, q->q_musrView, error);
808 }
809
810 *pParam = param;
811 return result;
812 }
813
814
815 static bool sqlBindAccessGroups(sqlite3_stmt *stmt, CFArrayRef accessGroups,
816 int *pParam, CFErrorRef *error) {
817 bool result = true;
818 int param = *pParam;
819 CFIndex ix, count = accessGroups ? CFArrayGetCount(accessGroups) : 0;
820 for (ix = 0; ix < count; ++ix) {
821 result = SecDbBindObject(stmt, param++,
822 CFArrayGetValueAtIndex(accessGroups, ix),
823 error);
824 if (!result)
825 break;
826 }
827 *pParam = param;
828 return result;
829 }
830
831 static bool sqlBindWhereClause(sqlite3_stmt *stmt, const Query *q,
832 CFArrayRef accessGroups, int *pParam, CFErrorRef *error) {
833 bool result = true;
834 int param = *pParam;
835 CFIndex ix, attr_count = query_attr_count(q);
836 for (ix = 0; ix < attr_count; ++ix) {
837 result = SecDbBindObject(stmt, param++, query_attr_at(q, ix).value, error);
838 if (!result)
839 break;
840 }
841
842 if (result) {
843 result = sqlBindMusr(stmt, q, &param, error);
844 }
845
846 /* Bind the access group to the sql. */
847 if (result) {
848 result = sqlBindAccessGroups(stmt, accessGroups, &param, error);
849 }
850
851 *pParam = param;
852 return result;
853 }
854
855 bool SecDbItemQuery(SecDbQueryRef query, CFArrayRef accessGroups, SecDbConnectionRef dbconn, CFErrorRef *error,
856 void (^handle_row)(SecDbItemRef item, bool *stop)) {
857 __block bool ok = true;
858 /* Sanity check the query. */
859 if (query->q_ref)
860 return SecError(errSecValueRefUnsupported, error, CFSTR("value ref not supported by queries"));
861
862 bool (^return_attr)(const SecDbAttr *attr) = ^bool (const SecDbAttr * attr) {
863 // The attributes here must match field list hardcoded in s3dl_select_sql used below, which is
864 // "rowid, data"
865 return attr->kind == kSecDbRowIdAttr || attr->kind == kSecDbEncryptedDataAttr;
866 };
867
868 CFStringRef sql = s3dl_create_select_sql(query, accessGroups);
869 ok = sql;
870 if (sql) {
871 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) {
872 /* Bind the values being searched for to the SELECT statement. */
873 int param = 1;
874 if (query->q_class == identity_class()) {
875 /* Bind the access groups to cert.agrp. */
876 ok &= sqlBindAccessGroups(stmt, accessGroups, &param, error);
877 }
878 if (ok)
879 ok &= sqlBindWhereClause(stmt, query, accessGroups, &param, error);
880 if (ok) {
881 SecDbStep(dbconn, stmt, error, ^(bool *stop) {
882 SecDbItemRef itemFromStatement = SecDbItemCreateWithStatement(kCFAllocatorDefault, query->q_class, stmt, query->q_keybag, error, return_attr);
883 if (itemFromStatement) {
884 CFTransferRetained(itemFromStatement->credHandle, query->q_use_cred_handle);
885 if (match_item(dbconn, query, accessGroups, itemFromStatement->attributes))
886 handle_row(itemFromStatement, stop);
887 CFReleaseNull(itemFromStatement);
888 } else {
889 secerror("failed to create item from stmt: %@", error ? *error : (CFErrorRef)"no error");
890 if (error) {
891 CFReleaseNull(*error);
892 }
893 //*stop = true;
894 //ok = false;
895 }
896 });
897 }
898 });
899 CFRelease(sql);
900 }
901
902 return ok;
903 }
904
905 static bool
906 s3dl_query(s3dl_handle_row handle_row,
907 void *context, CFErrorRef *error)
908 {
909 struct s3dl_query_ctx *c = context;
910 SecDbConnectionRef dbt = c->dbt;
911 Query *q = c->q;
912 CFArrayRef accessGroups = c->accessGroups;
913
914 /* Sanity check the query. */
915 if (q->q_ref)
916 return SecError(errSecValueRefUnsupported, error, CFSTR("value ref not supported by queries"));
917
918 /* Actual work here. */
919 if (q->q_limit == 1) {
920 c->result = NULL;
921 } else {
922 c->result = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
923 }
924 CFStringRef sql = s3dl_create_select_sql(q, accessGroups);
925 bool ok = SecDbWithSQL(dbt, sql, error, ^(sqlite3_stmt *stmt) {
926 bool sql_ok = true;
927 /* Bind the values being searched for to the SELECT statement. */
928 int param = 1;
929 if (q->q_class == identity_class()) {
930 /* Bind the access groups to cert.agrp. */
931 sql_ok = sqlBindAccessGroups(stmt, accessGroups, &param, error);
932 }
933 if (sql_ok)
934 sql_ok = sqlBindWhereClause(stmt, q, accessGroups, &param, error);
935 if (sql_ok) {
936 SecDbForEach(dbt, stmt, error, ^bool (int row_index) {
937 handle_row(stmt, context);
938
939 bool needs_auth = q->q_error && CFErrorGetCode(q->q_error) == errSecAuthNeeded;
940 if (q->q_skip_acl_items && needs_auth)
941 // Skip items needing authentication if we are told to do so.
942 CFReleaseNull(q->q_error);
943
944 bool stop = q->q_limit != kSecMatchUnlimited && c->found >= q->q_limit;
945 stop = stop || (q->q_error && !needs_auth);
946 return !stop;
947 });
948 }
949 return sql_ok;
950 });
951
952 CFRelease(sql);
953
954 // First get the error from the query, since errSecDuplicateItem from an
955 // update query should superceed the errSecItemNotFound below.
956 if (!query_error(q, error))
957 ok = false;
958 if (ok && c->found == 0) {
959 ok = SecError(errSecItemNotFound, error, CFSTR("no matching items found"));
960 if (q->q_spindump_on_failure) {
961 __security_stackshotreport(CFSTR("ItemNotFound"), __sec_exception_code_LostInMist);
962 }
963 }
964
965 return ok;
966 }
967
968 bool
969 s3dl_copy_matching(SecDbConnectionRef dbt, Query *q, CFTypeRef *result,
970 CFArrayRef accessGroups, CFErrorRef *error)
971 {
972 struct s3dl_query_ctx ctx = {
973 .q = q, .accessGroups = accessGroups, .dbt = dbt,
974 };
975 if (q->q_row_id && query_attr_count(q))
976 return SecError(errSecItemIllegalQuery, error,
977 CFSTR("attributes to query illegal; both row_id and other attributes can't be searched at the same time"));
978 if (q->q_token_object_id && query_attr_count(q) != 1)
979 return SecError(errSecItemIllegalQuery, error,
980 CFSTR("attributes to query illegal; both token persistent ref and other attributes can't be searched at the same time"));
981
982 // Only copy things that aren't tombstones unless the client explicitly asks otherwise.
983 if (!CFDictionaryContainsKey(q->q_item, kSecAttrTombstone))
984 query_add_attribute(kSecAttrTombstone, kCFBooleanFalse, q);
985 bool ok = s3dl_query(s3dl_query_row, &ctx, error);
986 if (ok && result)
987 *result = ctx.result;
988 else
989 CFReleaseSafe(ctx.result);
990
991 return ok;
992 }
993
994 typedef void (^s3dl_item_digest_callback)(CFDataRef persistantReference, CFDataRef encryptedData);
995
996 struct s3dl_digest_ctx {
997 Query *q;
998 SecDbConnectionRef dbt;
999 s3dl_item_digest_callback item_callback;
1000 };
1001
1002 static void s3dl_query_row_digest(sqlite3_stmt *stmt, void *context) {
1003 struct s3dl_query_ctx *c = context;
1004 Query *q = c->q;
1005
1006 sqlite_int64 rowid = sqlite3_column_int64(stmt, 0);
1007 CFDataRef edata = s3dl_copy_data_from_col(stmt, 1, NULL);
1008 CFDataRef persistant_reference = _SecItemCreatePersistentRef(q->q_class->name, rowid, NULL);
1009 CFDataRef digest = NULL;
1010
1011 if (edata) {
1012 digest = CFDataCopySHA256Digest(edata, NULL);
1013 }
1014
1015 if (digest && persistant_reference) {
1016 CFDictionaryRef item = CFDictionaryCreateForCFTypes(NULL,
1017 kSecValuePersistentRef, persistant_reference,
1018 kSecValueData, digest,
1019 NULL);
1020 if (item)
1021 CFArrayAppendValue((CFMutableArrayRef)c->result, item);
1022 CFReleaseNull(item);
1023 c->found++;
1024 } else {
1025 secinfo("item", "rowid %lu in %@ failed to create pref/digest", (unsigned long)rowid, q->q_class->name);
1026 }
1027 CFReleaseNull(digest);
1028 CFReleaseNull(edata);
1029 CFReleaseNull(persistant_reference);
1030 }
1031
1032
1033 bool
1034 s3dl_copy_digest(SecDbConnectionRef dbt, Query *q, CFArrayRef *result, CFArrayRef accessGroups, CFErrorRef *error)
1035 {
1036 struct s3dl_query_ctx ctx = {
1037 .q = q, .dbt = dbt, .accessGroups = accessGroups,
1038 };
1039 // Force to always return an array
1040 q->q_limit = kSecMatchUnlimited;
1041 // This interface only queries live data
1042 query_add_attribute(kSecAttrTombstone, kCFBooleanFalse, q);
1043 bool ok = s3dl_query(s3dl_query_row_digest, &ctx, error);
1044 if (ok && result)
1045 *result = (CFArrayRef)ctx.result;
1046 else
1047 CFReleaseSafe(ctx.result);
1048
1049 return ok;
1050 }
1051
1052 /* First remove key from q->q_pairs if it's present, then add the attribute again. */
1053 static void query_set_attribute_with_desc(const SecDbAttr *desc, const void *value, Query *q) {
1054 if (CFDictionaryContainsKey(q->q_item, desc->name)) {
1055 CFIndex ix;
1056 for (ix = 0; ix < q->q_attr_end; ++ix) {
1057 if (CFEqual(desc->name, q->q_pairs[ix].key)) {
1058 CFReleaseSafe(q->q_pairs[ix].value);
1059 --q->q_attr_end;
1060 for (; ix < q->q_attr_end; ++ix) {
1061 q->q_pairs[ix] = q->q_pairs[ix + 1];
1062 }
1063 CFDictionaryRemoveValue(q->q_item, desc->name);
1064 break;
1065 }
1066 }
1067 }
1068 query_add_attribute_with_desc(desc, value, q);
1069 }
1070
1071 /* Update modification_date if needed. */
1072 static void query_pre_update(Query *q) {
1073 SecDbForEachAttr(q->q_class, desc) {
1074 if (desc->kind == kSecDbModificationDateAttr) {
1075 CFDateRef now = CFDateCreate(0, CFAbsoluteTimeGetCurrent());
1076 query_set_attribute_with_desc(desc, now, q);
1077 CFReleaseSafe(now);
1078 }
1079 }
1080 }
1081
1082 /* Make sure all attributes that are marked as not_null have a value. If
1083 force_date is false, only set mdat and cdat if they aren't already set. */
1084 void query_pre_add(Query *q, bool force_date) {
1085 CFDateRef now = CFDateCreate(0, CFAbsoluteTimeGetCurrent());
1086 SecDbForEachAttrWithMask(q->q_class, desc, kSecDbInFlag) {
1087 if (desc->kind == kSecDbCreationDateAttr ||
1088 desc->kind == kSecDbModificationDateAttr) {
1089 if (force_date) {
1090 query_set_attribute_with_desc(desc, now, q);
1091 } else if (!CFDictionaryContainsKey(q->q_item, desc->name)) {
1092 query_add_attribute_with_desc(desc, now, q);
1093 }
1094 } else if ((desc->flags & kSecDbNotNullFlag) &&
1095 !CFDictionaryContainsKey(q->q_item, desc->name)) {
1096 CFTypeRef value = NULL;
1097 if (desc->flags & kSecDbDefault0Flag) {
1098 if (desc->kind == kSecDbDateAttr)
1099 value = CFDateCreate(kCFAllocatorDefault, 0.0);
1100 else {
1101 SInt32 vzero = 0;
1102 value = CFNumberCreate(0, kCFNumberSInt32Type, &vzero);
1103 }
1104 } else if (desc->flags & kSecDbDefaultEmptyFlag) {
1105 if (desc->kind == kSecDbDataAttr || desc->kind == kSecDbUUIDAttr)
1106 value = CFDataCreate(kCFAllocatorDefault, NULL, 0);
1107 else {
1108 value = CFSTR("");
1109 CFRetain(value);
1110 }
1111 }
1112 if (value) {
1113 /* Safe to use query_add_attribute here since the attr wasn't
1114 set yet. */
1115 query_add_attribute_with_desc(desc, value, q);
1116 CFRelease(value);
1117 }
1118 }
1119 }
1120 CFReleaseSafe(now);
1121 }
1122
1123 // Return a tri state value false->never make a tombstone, true->always make a
1124 // tombstone, NULL->make a tombstone, but delete it if the tombstone itself is not currently being synced.
1125 static CFBooleanRef s3dl_should_make_tombstone(Query *q, bool item_is_syncable, SecDbItemRef item) {
1126 if (q->q_use_tomb)
1127 return q->q_use_tomb;
1128 else if (item_is_syncable && !SecDbItemIsTombstone(item))
1129 return NULL;
1130 else
1131 return kCFBooleanFalse;
1132 }
1133
1134 bool
1135 s3dl_query_update(SecDbConnectionRef dbt, Query *q,
1136 CFDictionaryRef attributesToUpdate, CFArrayRef accessGroups, CFErrorRef *error)
1137 {
1138 /* Sanity check the query. */
1139 if (query_match_count(q) != 0)
1140 return SecError(errSecItemMatchUnsupported, error, CFSTR("match not supported in attributes to update"));
1141 if (q->q_ref)
1142 return SecError(errSecValueRefUnsupported, error, CFSTR("value ref not supported in attributes to update"));
1143 if (q->q_row_id && query_attr_count(q))
1144 return SecError(errSecItemIllegalQuery, error, CFSTR("attributes to update illegal; both row_id and other attributes can't be updated at the same time"));
1145 if (q->q_token_object_id && query_attr_count(q) != 1)
1146 return SecError(errSecItemIllegalQuery, error, CFSTR("attributes to update illegal; both token persistent ref and other attributes can't be updated at the same time"));
1147
1148 __block bool result = true;
1149 Query *u = query_create(q->q_class, NULL, attributesToUpdate, NULL, error);
1150 if (u == NULL) return false;
1151 require_action_quiet(query_update_parse(u, attributesToUpdate, error), errOut, result = false);
1152 query_pre_update(u);
1153 result &= SecDbTransaction(dbt, kSecDbExclusiveTransactionType, error, ^(bool *commit) {
1154 // Make sure we only update real items, not tombstones, unless the client explicitly asks otherwise.
1155 if (!CFDictionaryContainsKey(q->q_item, kSecAttrTombstone))
1156 query_add_attribute(kSecAttrTombstone, kCFBooleanFalse, q);
1157 result &= SecDbItemQuery(q, accessGroups, dbt, error, ^(SecDbItemRef item, bool *stop) {
1158 // We always need to know the error here.
1159 CFErrorRef localError = NULL;
1160 if (q->q_token_object_id) {
1161 const SecDbAttr *valueDataAttr = SecDbClassAttrWithKind(item->class, kSecDbDataAttr, NULL);
1162 CFDataRef valueData = SecDbItemGetValue(item, valueDataAttr, NULL);
1163 if (q->q_token_object_id != NULL && !checkTokenObjectID(q->q_token_object_id, valueData))
1164 return;
1165 }
1166 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1167 const SecDbAttr *sha1attr = SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, NULL);
1168 CFDataRef storedSHA1 = CFRetainSafe(SecDbItemGetValue(item, sha1attr, NULL));
1169 SecDbItemRef new_item = SecDbItemCopyWithUpdates(item, u->q_item, &localError);
1170 SecDbItemSetValue(item, sha1attr, storedSHA1, NULL);
1171 CFReleaseSafe(storedSHA1);
1172 if (SecErrorGetOSStatus(localError) == errSecDecode) {
1173 // We just ignore this, and treat as if item is not found.
1174 secwarning("deleting corrupt %@,rowid=%" PRId64 " %@", q->q_class->name, SecDbItemGetRowId(item, NULL), localError);
1175 CFReleaseNull(localError);
1176 if (!SecDbItemDelete(item, dbt, false, false, &localError)) {
1177 secerror("failed to delete corrupt %@,rowid=%" PRId64 " %@", q->q_class->name, SecDbItemGetRowId(item, NULL), localError);
1178 CFReleaseNull(localError);
1179 }
1180 CFReleaseNull(new_item);
1181 return;
1182 }
1183 if (new_item != NULL && u->q_access_control != NULL)
1184 SecDbItemSetAccessControl(new_item, u->q_access_control, &localError);
1185 result = SecErrorPropagate(localError, error) && new_item;
1186 if (new_item) {
1187 bool item_is_sync = SecDbItemIsSyncable(item);
1188 result = SecDbItemUpdate(item, new_item, dbt, s3dl_should_make_tombstone(q, item_is_sync, item), q->q_uuid_from_primary_key, error);
1189 if (result) {
1190 q->q_changed = true;
1191 if (item_is_sync || SecDbItemIsSyncable(new_item))
1192 q->q_sync_changed = true;
1193 }
1194 CFRelease(new_item);
1195 }
1196 });
1197 if (!result)
1198 *commit = false;
1199 });
1200 if (result && !q->q_changed)
1201 result = SecError(errSecItemNotFound, error, CFSTR("No items updated"));
1202 errOut:
1203 if (!query_destroy(u, error))
1204 result = false;
1205 return result;
1206 }
1207
1208 static bool SecDbItemNeedAuth(SecDbItemRef item, CFErrorRef *error)
1209 {
1210 CFErrorRef localError = NULL;
1211 if (!SecDbItemEnsureDecrypted(item, true, &localError) && localError && CFErrorGetCode(localError) == errSecAuthNeeded) {
1212 if (error)
1213 *error = localError;
1214 return true;
1215 }
1216
1217 CFReleaseSafe(localError);
1218 return false;
1219 }
1220
1221 bool
1222 s3dl_query_delete(SecDbConnectionRef dbt, Query *q, CFArrayRef accessGroups, CFErrorRef *error)
1223 {
1224 __block bool ok = true;
1225 __block bool needAuth = false;
1226 // Only delete things that aren't tombstones, unless the client explicitly asks otherwise.
1227 if (!CFDictionaryContainsKey(q->q_item, kSecAttrTombstone))
1228 query_add_attribute(kSecAttrTombstone, kCFBooleanFalse, q);
1229 ok &= SecDbItemSelect(q, dbt, error, NULL, ^bool(const SecDbAttr *attr) {
1230 return false;
1231 },^bool(CFMutableStringRef sql, bool *needWhere) {
1232 SecDbAppendWhereClause(sql, q, accessGroups);
1233 return true;
1234 },^bool(sqlite3_stmt * stmt, int col) {
1235 return sqlBindWhereClause(stmt, q, accessGroups, &col, error);
1236 }, ^(SecDbItemRef item, bool *stop) {
1237 // Check if item for token persitence ref
1238 if (q->q_token_object_id) {
1239 const SecDbAttr *valueDataAttr = SecDbClassAttrWithKind(item->class, kSecDbDataAttr, NULL);
1240 CFDataRef valueData = SecDbItemGetValue(item, valueDataAttr, NULL);
1241 if (q->q_token_object_id != NULL && !checkTokenObjectID(q->q_token_object_id, valueData))
1242 return;
1243 }
1244 // Check if item need to be authenticated by LocalAuthentication
1245 item->cryptoOp = kAKSKeyOpDelete;
1246 if (SecDbItemNeedAuth(item, error)) {
1247 needAuth = true;
1248 return;
1249 }
1250 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1251 const SecDbAttr *sha1attr = SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, NULL);
1252 CFDataRef storedSHA1 = CFRetainSafe(SecDbItemGetValue(item, sha1attr, NULL));
1253 bool item_is_sync = SecDbItemIsSyncable(item);
1254 SecDbItemSetValue(item, sha1attr, storedSHA1, NULL);
1255 CFReleaseSafe(storedSHA1);
1256 ok = SecDbItemDelete(item, dbt, s3dl_should_make_tombstone(q, item_is_sync, item), q->q_tombstone_use_mdat_from_item, error);
1257 if (ok) {
1258 q->q_changed = true;
1259 if (item_is_sync)
1260 q->q_sync_changed = true;
1261 }
1262 });
1263 if (ok && !q->q_changed && !needAuth) {
1264 ok = SecError(errSecItemNotFound, error, CFSTR("Delete failed to delete anything"));
1265 }
1266 return ok && !needAuth;
1267 }
1268
1269 static bool
1270 matchAnyString(CFStringRef needle, CFStringRef *haystack)
1271 {
1272 while (*haystack) {
1273 if (CFEqual(needle, *haystack))
1274 return true;
1275 haystack++;
1276 }
1277 return false;
1278 }
1279
1280 /* Return true iff the item in question should not be backed up, nor restored,
1281 but when restoring a backup the original version of the item should be
1282 added back to the keychain again after the restore completes. */
1283 bool SecItemIsSystemBound(CFDictionaryRef item, const SecDbClass *cls, bool multiUser) {
1284 CFNumberRef sysb = CFDictionaryGetValue(item, kSecAttrSysBound);
1285 if (isNumber(sysb)) {
1286 int32_t num = 0;
1287 if (!CFNumberGetValue(sysb, kCFNumberSInt32Type, &num))
1288 return false;
1289 if (num == kSecSecAttrSysBoundNot) {
1290 return false;
1291 } else if (num == kSecSecAttrSysBoundPreserveDuringRestore) {
1292 return true;
1293 }
1294 return true;
1295 }
1296
1297 CFStringRef agrp = CFDictionaryGetValue(item, kSecAttrAccessGroup);
1298 if (!isString(agrp))
1299 return false;
1300
1301 if (CFEqualSafe(agrp, kSOSInternalAccessGroup)) {
1302 secdebug("backup", "found sysbound item: %@", item);
1303 return true;
1304 }
1305
1306 if (CFEqual(agrp, CFSTR("lockdown-identities"))) {
1307 secdebug("backup", "found sys_bound item: %@", item);
1308 return true;
1309 }
1310
1311 if (CFEqual(agrp, CFSTR("apple")) && cls == genp_class()) {
1312 CFStringRef service = CFDictionaryGetValue(item, kSecAttrService);
1313 CFStringRef account = CFDictionaryGetValue(item, kSecAttrAccount);
1314
1315 if (isString(service) && isString(account)) {
1316 static CFStringRef mcAccounts[] = {
1317 CFSTR("Public"),
1318 CFSTR("Private"),
1319 NULL,
1320 };
1321
1322 if (CFEqual(service, CFSTR("com.apple.managedconfiguration"))
1323 && matchAnyString(account, mcAccounts))
1324 {
1325 secdebug("backup", "found sys_bound item: %@", item);
1326 return true;
1327 }
1328 }
1329
1330 if (isString(service) && CFEqual(service, CFSTR("com.apple.account.CloudKit.token"))) {
1331 secdebug("backup", "found sys_bound item: %@", item);
1332 return true;
1333 }
1334
1335 if (isString(service) && CFEqual(service, CFSTR("com.apple.account.idms.continuation-key"))) {
1336 secdebug("backup", "found sys_bound item: %@", item);
1337 return true;
1338 }
1339 }
1340
1341 if (multiUser && CFEqual(agrp, CFSTR("com.apple.apsd")) && cls == genp_class()) {
1342 static CFStringRef pushServices[] = {
1343 CFSTR("push.apple.com"),
1344 CFSTR("push.apple.com,PerAppToken.v0"),
1345 NULL
1346 };
1347 CFStringRef service = CFDictionaryGetValue(item, kSecAttrService);
1348
1349 if (isString(service) && matchAnyString(service, pushServices)) {
1350 secdebug("backup", "found sys_bound item: %@", item);
1351 return true;
1352 }
1353 }
1354
1355 if (multiUser && CFEqual(agrp, CFSTR("appleaccount")) && cls == genp_class()) {
1356 static CFStringRef accountServices[] = {
1357 CFSTR("com.apple.appleaccount.fmf.token"), /* temporary tokens while accout is being setup */
1358 CFSTR("com.apple.appleaccount.fmf.apptoken"),
1359 CFSTR("com.apple.appleaccount.fmip.siritoken"),
1360 CFSTR("com.apple.appleaccount.cloudkit.token"),
1361 NULL
1362 };
1363 CFStringRef service = CFDictionaryGetValue(item, kSecAttrService);
1364
1365 if (isString(service) && matchAnyString(service, accountServices)) {
1366 secdebug("backup", "found exact sys_bound item: %@", item);
1367 return true;
1368 }
1369 }
1370
1371 if (multiUser && CFEqual(agrp, CFSTR("apple")) && cls == genp_class()) {
1372 static CFStringRef accountServices[] = {
1373 /* accounts, remove with rdar://37595482 */
1374 CFSTR("com.apple.account.AppleAccount.token"),
1375 CFSTR("com.apple.account.AppleAccount.password"),
1376 CFSTR("com.apple.account.AppleAccount.rpassword"),
1377 CFSTR("com.apple.account.idms.token"),
1378 CFSTR("com.apple.account.idms.heartbeat-token"),
1379 CFSTR("com.apple.account.idms.continuation-key"),
1380 CFSTR("com.apple.account.CloudKit.token"),
1381 CFSTR("com.apple.account.IdentityServices.password"), /* accountsd for ids */
1382 CFSTR("com.apple.account.IdentityServices.rpassword"),
1383 CFSTR("com.apple.account.IdentityServices.token"),
1384 /* IDS stuff */
1385 CFSTR("BackupIDSAccountToken"),
1386 CFSTR("com.apple.ids"),
1387 CFSTR("ids"),
1388 CFSTR("IDS"),
1389 NULL
1390 };
1391 CFStringRef service = CFDictionaryGetValue(item, kSecAttrService);
1392
1393 if (isString(service) && matchAnyString(service, accountServices)) {
1394 secdebug("backup", "found exact sys_bound item: %@", item);
1395 return true;
1396 }
1397 if (isString(service) && CFStringHasPrefix(service, CFSTR("com.apple.gs."))) {
1398 secdebug("backup", "found exact sys_bound item: %@", item);
1399 return true;
1400 }
1401 if (isString(service) && CFEqual(service, CFSTR("com.apple.facetime"))) {
1402 CFStringRef account = CFDictionaryGetValue(item, kSecAttrAccount);
1403 if (isString(account) && CFEqual(account, CFSTR("registrationV1"))) {
1404 secdebug("backup", "found exact sys_bound item: %@", item);
1405 return true;
1406 }
1407 }
1408 }
1409
1410 /* accounts, remove with rdar://37595482 */
1411 if (multiUser && CFEqual(agrp, CFSTR("com.apple.ind")) && cls == genp_class()) {
1412 CFStringRef service = CFDictionaryGetValue(item, kSecAttrService);
1413 if (isString(service) && CFEqual(service, CFSTR("com.apple.ind.registration"))) {
1414 secdebug("backup", "found exact sys_bound item: %@", item);
1415 return true;
1416 }
1417 }
1418
1419 if (multiUser && CFEqual(agrp, CFSTR("ichat")) && cls == genp_class()) {
1420 static CFStringRef accountServices[] = {
1421 CFSTR("ids"),
1422 NULL
1423 };
1424 CFStringRef service = CFDictionaryGetValue(item, kSecAttrService);
1425
1426 if (isString(service) && matchAnyString(service, accountServices)) {
1427 secdebug("backup", "found exact sys_bound item: %@", item);
1428 return true;
1429 }
1430 }
1431
1432 if (multiUser && CFEqual(agrp, CFSTR("ichat")) && cls == keys_class()) {
1433 static CFStringRef exactMatchingLabel[] = {
1434 CFSTR("iMessage Encryption Key"),
1435 CFSTR("iMessage Signing Key"),
1436 };
1437 CFStringRef label = CFDictionaryGetValue(item, kSecAttrLabel);
1438 if (isString(label)) {
1439 if (matchAnyString(label, exactMatchingLabel)) {
1440 secdebug("backup", "found exact sys_bound item: %@", item);
1441 return true;
1442 }
1443 }
1444 }
1445
1446 if (multiUser && CFEqual(agrp, CFSTR("com.apple.rapport")) && cls == genp_class()) {
1447 secdebug("backup", "found exact sys_bound item: %@", item);
1448 return true;
1449 }
1450
1451 secdebug("backup", "found non sys_bound item: %@", item);
1452 return false;
1453 }
1454
1455 /* Delete all items from the current keychain. If this is not an in
1456 place upgrade we don't delete items in the 'lockdown-identities'
1457 access group, this ensures that an import or restore of a backup
1458 will never overwrite an existing activation record. */
1459 static bool SecServerDeleteAll(SecDbConnectionRef dbt, CFErrorRef *error) {
1460 secwarning("SecServerDeleteAll");
1461
1462 return kc_transaction(dbt, error, ^{
1463
1464 bool ok = (SecDbExec(dbt, CFSTR("DELETE from genp;"), error) &&
1465 SecDbExec(dbt, CFSTR("DELETE from inet;"), error) &&
1466 SecDbExec(dbt, CFSTR("DELETE from cert;"), error) &&
1467 SecDbExec(dbt, CFSTR("DELETE from keys;"), error));
1468 return ok;
1469 });
1470 }
1471
1472 #if TARGET_OS_IPHONE
1473
1474 static bool DeleteAllFromTableForMUSRView(SecDbConnectionRef dbt,
1475 CFStringRef sql,
1476 CFDataRef musr,
1477 bool keepU,
1478 CFErrorRef *error)
1479 {
1480 sqlite3_stmt *stmt = NULL;
1481 CFStringRef sql2 = NULL;
1482 bool ok = false;
1483
1484 if (keepU) {
1485 sql2 = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ AND pdmn NOT IN ('aku','akpu','cku','dku')"), sql);
1486 } else {
1487 sql2 = CFRetain(sql);
1488 }
1489 require(sql2, fail);
1490
1491 stmt = SecDbCopyStmt(dbt, sql2, NULL, error);
1492 require(stmt, fail);
1493
1494 ok = SecDbBindObject(stmt, 1, musr, error);
1495 require(ok, fail);
1496
1497 ok = SecDbStep(dbt, stmt, error, ^(bool *stop) { });
1498 require(ok, fail);
1499
1500 fail:
1501 if (stmt) {
1502 ok = SecDbFinalize(stmt, error);
1503 }
1504 if (!ok) {
1505 secwarning("DeleteAllFromTableForMUSRView failed for %@ for musr: %@: %@", sql2, musr, error ? *error : NULL);
1506 }
1507
1508 CFReleaseNull(sql2);
1509
1510 return ok;
1511 }
1512
1513 bool SecServerDeleteAllForUser(SecDbConnectionRef dbt, CFDataRef musrView, bool keepU, CFErrorRef *error) {
1514 secwarning("SecServerDeleteAllForUser for user: %@ keepU %s", musrView, keepU ? "yes" : "no");
1515
1516 return kc_transaction(dbt, error, ^{
1517 bool ok;
1518
1519 ok = (DeleteAllFromTableForMUSRView(dbt, CFSTR("DELETE FROM genp WHERE musr = ?"), musrView, keepU, error) &&
1520 DeleteAllFromTableForMUSRView(dbt, CFSTR("DELETE FROM inet WHERE musr = ?"), musrView, keepU, error) &&
1521 DeleteAllFromTableForMUSRView(dbt, CFSTR("DELETE FROM cert WHERE musr = ?"), musrView, keepU, error) &&
1522 DeleteAllFromTableForMUSRView(dbt, CFSTR("DELETE FROM keys WHERE musr = ?"), musrView, keepU, error));
1523
1524 return ok;
1525 });
1526 }
1527 #endif
1528
1529 OSStatus SecServerDeleteForAppClipApplicationIdentifier(CFStringRef identifier) {
1530 secnotice("item", "Request to delete app clip keychain items for identifier '%@'", identifier);
1531
1532 __block CFErrorRef cfError = NULL;
1533 __block bool ok = true;
1534 ok &= kc_with_dbt(true, &cfError, ^bool(SecDbConnectionRef dbt) {
1535 return kc_transaction(dbt, &cfError, ^bool{
1536 const SecDbSchema* schema = current_schema();
1537 for (const SecDbClass *const * class = schema->classes; *class != NULL; ++class) {
1538 if ((*class)->itemclass) {
1539 CFStringRef sqlStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("DELETE FROM %@ WHERE agrp = ? AND clip = 1"), (*class)->name);
1540 ok &= SecDbPrepare(dbt, sqlStr, &cfError, ^(sqlite3_stmt *stmt) {
1541 ok &= SecDbBindObject(stmt, 1, identifier, &cfError);
1542 ok &= SecDbStep(dbt, stmt, &cfError, NULL);
1543 });
1544 CFReleaseNull(sqlStr);
1545 }
1546 }
1547 return ok;
1548 });
1549 });
1550
1551 OSStatus status = ok ? errSecSuccess : errSecInternal;
1552 secnotice("item", "Finished request to delete app clip keychain items for identifier '%@' with status %i: %@", identifier, (int)status, cfError);
1553 CFReleaseNull(cfError);
1554
1555 return status;
1556 }
1557
1558 struct s3dl_export_row_ctx {
1559 struct s3dl_query_ctx qc;
1560 keybag_handle_t dest_keybag;
1561 enum SecItemFilter filter;
1562 bool multiUser;
1563 };
1564
1565 static void s3dl_export_row(sqlite3_stmt *stmt, void *context) {
1566 struct s3dl_export_row_ctx *c = context;
1567 Query *q = c->qc.q;
1568 SecAccessControlRef access_control = NULL;
1569 CFErrorRef localError = NULL;
1570
1571 sqlite_int64 rowid = sqlite3_column_int64(stmt, 0);
1572 CFMutableDictionaryRef allAttributes = NULL;
1573 CFMutableDictionaryRef metadataAttributes = NULL;
1574 CFMutableDictionaryRef secretStuff = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1575 keyclass_t keyclass = 0;
1576 bool ok = s3dl_item_from_col(stmt, q, 1, c->qc.accessGroups, &allAttributes, &access_control, &keyclass, &localError);
1577
1578 if (ok) {
1579 metadataAttributes = CFDictionaryCreateMutableCopy(NULL, 0, allAttributes);
1580 SecDbForEachAttrWithMask(q->q_class, desc, kSecDbReturnDataFlag) {
1581 CFTypeRef value = CFDictionaryGetValue(metadataAttributes, desc->name);
1582 if (value) {
1583 CFDictionarySetValue(secretStuff, desc->name, value);
1584 CFDictionaryRemoveValue(metadataAttributes, desc->name);
1585 }
1586 }
1587 }
1588
1589 /* Skip akpu items when backing up, those are intentionally lost across restores. The same applies to SEP-based keys */
1590 bool is_akpu = access_control ? CFEqualSafe(SecAccessControlGetProtection(access_control), kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly)
1591 // Mask generation, only look at class per se
1592 : (keyclass & key_class_last) == key_class_akpu;
1593 bool is_token = (ok && allAttributes != NULL) ? CFDictionaryContainsKey(allAttributes, kSecAttrTokenID) : false;
1594 bool skip_akpu_or_token = (is_akpu || is_token) && c->filter == kSecBackupableItemFilter;
1595
1596 if (ok && allAttributes && !skip_akpu_or_token) {
1597 /* Only export sysbound items if do_sys_bound is true, only export non sysbound items otherwise. */
1598 bool do_sys_bound = c->filter == kSecSysBoundItemFilter;
1599 if (c->filter == kSecNoItemFilter ||
1600 SecItemIsSystemBound(allAttributes, q->q_class, c->multiUser) == do_sys_bound) {
1601 /* Re-encode the item. */
1602 secdebug("item", "export rowid %llu item: %@", rowid, allAttributes);
1603 /* The code below could be moved into handle_row. */
1604 CFDataRef pref = _SecItemCreatePersistentRef(q->q_class->name, rowid, allAttributes);
1605 if (pref) {
1606 if (c->dest_keybag != KEYBAG_NONE) {
1607 CFMutableDictionaryRef auth_attribs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1608 SecDbForEachAttrWithMask(q->q_class, desc, kSecDbInAuthenticatedDataFlag) {
1609 CFTypeRef value = CFDictionaryGetValue(metadataAttributes, desc->name);
1610 if(value) {
1611 CFDictionaryAddValue(auth_attribs, desc->name, value);
1612 CFDictionaryRemoveValue(metadataAttributes, desc->name);
1613 }
1614 }
1615
1616 /* Encode and encrypt the item to the specified keybag. */
1617 CFDataRef edata = NULL;
1618 bool encrypted = ks_encrypt_data(c->dest_keybag, access_control, q->q_use_cred_handle, secretStuff, metadataAttributes, auth_attribs, &edata, false, &q->q_error);
1619 CFDictionaryRemoveAllValues(allAttributes);
1620 CFRelease(auth_attribs);
1621 if (encrypted) {
1622 CFDictionarySetValue(allAttributes, kSecValueData, edata);
1623 CFReleaseSafe(edata);
1624 } else {
1625 seccritical("ks_encrypt_data %@,rowid=%" PRId64 ": failed: %@", q->q_class->name, rowid, q->q_error);
1626 CFReleaseNull(q->q_error);
1627 }
1628 }
1629 if (CFDictionaryGetCount(allAttributes)) {
1630 CFDictionarySetValue(allAttributes, kSecValuePersistentRef, pref);
1631 CFArrayAppendValue((CFMutableArrayRef)c->qc.result, allAttributes);
1632 c->qc.found++;
1633 }
1634 CFReleaseSafe(pref);
1635 }
1636 }
1637 } else if (!ok || !allAttributes) {
1638 OSStatus status = SecErrorGetOSStatus(localError);
1639
1640 if (status == errSecInteractionNotAllowed && is_akpu) {
1641 if (skip_akpu_or_token) {
1642 secdebug("item", "Skipping akpu item for backup");
1643 } else { // Probably failed to decrypt sysbound item. Should never be an akpu item in backup.
1644 secerror("Encountered akpu item we cannot export (filter %d), skipping. %@", c->filter, localError);
1645 if (sqlite3_column_count(stmt) > 2) { // Should have rowid,data,agrp from s3dl_create_select_sql
1646 CFStringRef agrp = CFStringCreateWithCString(kCFAllocatorDefault, (const char*)sqlite3_column_text(stmt, 2), kCFStringEncodingUTF8);
1647 if (agrp) {
1648 CFDictionaryRef payload = CFDictionaryCreateForCFTypes(NULL, CFSTR("agrp"), agrp, NULL);
1649 SecABCTrigger(CFSTR("keychain"), CFSTR("invalid-akpu+sysbound"), NULL, payload);
1650 CFReleaseNull(payload);
1651 }
1652 CFReleaseNull(agrp);
1653 }
1654 }
1655 // We expect akpu items to be inaccessible when the device is locked.
1656 CFReleaseNull(localError);
1657 } else {
1658 /* This happens a lot when trying to migrate keychain before first unlock, so only a notice */
1659 /* If the error is "corrupted item" then we just ignore it, otherwise we save it in the query */
1660 secinfo("item","Could not export item for rowid %llu: %@", rowid, localError);
1661
1662 if (status == errSecDecode) {
1663 CFReleaseNull(localError);
1664 } else {
1665 CFReleaseSafe(q->q_error);
1666 q->q_error = localError;
1667 }
1668 }
1669 } else {
1670 secnotice("item", "export rowid %llu skipped. akpu/token: %i", rowid, skip_akpu_or_token);
1671 }
1672
1673 CFReleaseNull(access_control);
1674 CFReleaseNull(allAttributes);
1675 CFReleaseNull(metadataAttributes);
1676 CFReleaseNull(secretStuff);
1677 }
1678
1679 static CFStringRef
1680 SecCreateKeybagUUID(keybag_handle_t keybag)
1681 {
1682 #if !TARGET_HAS_KEYSTORE
1683 return NULL;
1684 #else
1685 char uuidstr[37];
1686 uuid_t uuid;
1687 if (aks_get_bag_uuid(keybag, uuid) != KERN_SUCCESS)
1688 return NULL;
1689 uuid_unparse_lower(uuid, uuidstr);
1690 return CFStringCreateWithCString(NULL, uuidstr, kCFStringEncodingUTF8);
1691 #endif
1692 }
1693
1694 CFDictionaryRef
1695 SecServerCopyKeychainPlist(SecDbConnectionRef dbt,
1696 SecurityClient *client,
1697 keybag_handle_t src_keybag,
1698 keybag_handle_t dest_keybag,
1699 enum SecItemFilter filter,
1700 CFErrorRef *error) {
1701 CFMutableDictionaryRef keychain;
1702 keychain = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1703 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1704 unsigned class_ix;
1705 bool inMultiUser = false;
1706 CFStringRef keybaguuid = NULL;
1707 Query q = { .q_keybag = src_keybag,
1708 .q_musrView = NULL
1709 };
1710
1711 if (!keychain) {
1712 if (error && !*error)
1713 SecError(errSecAllocate, error, CFSTR("Can't create keychain dictionary"));
1714 goto errOut;
1715 }
1716
1717 q.q_return_type =
1718 kSecReturnDataMask |
1719 kSecReturnAttributesMask |
1720 kSecReturnPersistentRefMask;
1721 q.q_limit = kSecMatchUnlimited;
1722 q.q_skip_acl_items = true;
1723 q.q_skip_app_clip_items = true;
1724
1725 #if TARGET_OS_IPHONE
1726 if (client && client->inMultiUser) {
1727 q.q_musrView = SecMUSRCreateActiveUserUUID(client->uid);
1728 inMultiUser = true;
1729 } else
1730 #endif
1731 {
1732 q.q_musrView = SecMUSRGetSingleUserKeychainUUID();
1733 CFRetain(q.q_musrView);
1734
1735 keybaguuid = SecCreateKeybagUUID(dest_keybag);
1736 if (keybaguuid) {
1737 CFDictionarySetValue(keychain, kSecBackupKeybagUUIDKey, keybaguuid);
1738 }
1739 }
1740
1741 /* Get rid of this duplicate. */
1742 const SecDbClass *SecDbClasses[] = {
1743 genp_class(),
1744 inet_class(),
1745 cert_class(),
1746 keys_class()
1747 };
1748
1749 for (class_ix = 0; class_ix < array_size(SecDbClasses);
1750 ++class_ix) {
1751 q.q_class = SecDbClasses[class_ix];
1752 struct s3dl_export_row_ctx ctx = {
1753 .qc = { .q = &q, .dbt = dbt },
1754 .dest_keybag = dest_keybag, .filter = filter,
1755 .multiUser = inMultiUser,
1756 };
1757
1758 secnotice("item", "exporting %ssysbound class '%@'", filter != kSecSysBoundItemFilter ? "non-" : "", q.q_class->name);
1759
1760 CFErrorRef localError = NULL;
1761 if (s3dl_query(s3dl_export_row, &ctx, &localError)) {
1762 secnotice("item", "exporting class '%@' complete", q.q_class->name);
1763 if (CFArrayGetCount(ctx.qc.result)) {
1764 SecSignpostBackupCount(SecSignpostImpulseBackupClassCount, q.q_class->name, CFArrayGetCount(ctx.qc.result), filter);
1765 CFDictionaryAddValue(keychain, q.q_class->name, ctx.qc.result);
1766 }
1767
1768 } else {
1769 OSStatus status = (OSStatus)CFErrorGetCode(localError);
1770 if (status == errSecItemNotFound) {
1771 secnotice("item", "exporting class '%@' complete (no items)", q.q_class->name);
1772 CFRelease(localError);
1773 } else {
1774 secerror("exporting class '%@' failed: %@", q.q_class->name, localError);
1775 if (error) {
1776 CFReleaseSafe(*error);
1777 *error = localError;
1778 } else {
1779 CFRelease(localError);
1780 }
1781 CFReleaseNull(keychain);
1782 CFReleaseNull(ctx.qc.result);
1783 break;
1784 }
1785 }
1786 CFReleaseNull(ctx.qc.result);
1787 }
1788
1789 errOut:
1790 CFReleaseNull(q.q_musrView);
1791 CFReleaseNull(keybaguuid);
1792
1793 return keychain;
1794 }
1795
1796 struct SecServerImportClassState {
1797 SecDbConnectionRef dbt;
1798 CFErrorRef error;
1799 keybag_handle_t src_keybag;
1800 keybag_handle_t dest_keybag;
1801 SecurityClient *client;
1802 enum SecItemFilter filter;
1803 };
1804
1805 struct SecServerImportItemState {
1806 const SecDbClass *class;
1807 struct SecServerImportClassState *s;
1808 };
1809
1810 static void
1811 SecServerImportItem(const void *value, void *context)
1812 {
1813 struct SecServerImportItemState *state = (struct SecServerImportItemState *)context;
1814 bool inMultiUser = false;
1815 #if TARGET_OS_IPHONE
1816 if (state->s->client->inMultiUser)
1817 inMultiUser = true;
1818 #endif
1819
1820 if (state->s->error)
1821 return;
1822
1823 if (!isDictionary(value)) {
1824 SecError(errSecParam, &state->s->error, CFSTR("value %@ is not a dictionary"), value);
1825 return;
1826 }
1827
1828 CFDictionaryRef dict = (CFDictionaryRef)value;
1829
1830 secdebug("item", "Import Item : %@", dict);
1831
1832 SecDbItemRef item = NULL;
1833
1834 /* This is sligthly confusing:
1835 - During upgrade all items are exported with KEYBAG_NONE.
1836 - During restore from backup, existing sys_bound items are exported with KEYBAG_NONE, and are exported as dictionary of attributes.
1837 - Item in the actual backup are export with a real keybag, and are exported as encrypted v_Data and v_PersistentRef
1838 */
1839 if (state->s->src_keybag == KEYBAG_NONE) {
1840 item = SecDbItemCreateWithAttributes(kCFAllocatorDefault, state->class, dict, state->s->dest_keybag, &state->s->error);
1841 } else {
1842 item = SecDbItemCreateWithBackupDictionary(state->class, dict, state->s->src_keybag, state->s->dest_keybag, &state->s->error);
1843 }
1844
1845 /* If item is NULL here, control flow ends up at the end where error is cleared. */
1846 if (item && !SecDbItemEnsureDecrypted(item, true, &state->s->error)) {
1847 secdebug("item", "Failed to import item because of decryption failure: %@", state->s->error);
1848 CFReleaseNull(item);
1849 /* No early return; as just above, go to the end where error is cleared. */
1850 }
1851
1852 /* We use the kSecSysBoundItemFilter to indicate that we don't
1853 * preserve rowid's during import.
1854 */
1855 if (item && item->attributes && state->s->filter == kSecBackupableItemFilter) {
1856 CFTypeRef pdmu;
1857
1858 /* We don't filter non sys_bound items during import since we know we
1859 * will never have any in this case.
1860 */
1861 if (SecItemIsSystemBound(item->attributes, state->class, inMultiUser)) {
1862 secdebug("item", "skipping backup of item: %@", dict);
1863 CFReleaseNull(item);
1864 return;
1865 }
1866
1867 /*
1868 * Don't bother with u items when in edu mode since our current backup system
1869 * don't keep track of items that blongs to the device (u) but rather just
1870 * merge them into one blob.
1871 */
1872 if (inMultiUser && (pdmu = CFDictionaryGetValue(item->attributes, kSecAttrAccessible))) {
1873 if (CFEqual(pdmu, kSecAttrAccessibleWhenUnlockedThisDeviceOnly) ||
1874 CFEqual(pdmu, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly) ||
1875 CFEqual(pdmu, kSecAttrAccessibleWhenUnlockedThisDeviceOnly) ||
1876 CFEqual(pdmu, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly))
1877 {
1878 secdebug("item", "Skipping KU item : %@", dict);
1879 CFReleaseNull(item);
1880 return;
1881 }
1882 }
1883
1884 /* Avoid importing token-based items. Although newer backups should not have them,
1885 * older (iOS9, iOS10.0) produced backups with token-based items.
1886 */
1887 if (CFDictionaryContainsKey(item->attributes, kSecAttrTokenID)) {
1888 secdebug("item", "Skipping token-based item : %@", dict);
1889 CFReleaseNull(item);
1890 return;
1891 }
1892 }
1893
1894 /*
1895 *
1896 */
1897
1898 if (item && item->attributes) {
1899 CFDataRef musr = NULL;
1900 CFDataRef musrBackup = CFDictionaryGetValue(item->attributes, kSecAttrMultiUser);
1901 CFDataRef systemKeychainUUID = SecMUSRGetSystemKeychainUUID();
1902 bool systemKeychain = CFEqualSafe(musrBackup, systemKeychainUUID);
1903
1904 #if TARGET_OS_IPHONE
1905 if (state->s->client && state->s->client->inMultiUser) {
1906 if (systemKeychain) {
1907 secwarning("system keychain not allowed in multi user mode for item: %@", item);
1908 } else {
1909 musr = SecMUSRCreateActiveUserUUID(state->s->client->uid);
1910 }
1911 } else
1912 #endif
1913 {
1914 if (systemKeychain) {
1915 musr = SecMUSRCopySystemKeychainUUID();
1916 } else {
1917 musr = SecMUSRGetSingleUserKeychainUUID();
1918 CFRetainSafe(musr);
1919 }
1920 }
1921 if (musr == NULL) {
1922 CFReleaseNull(item);
1923 } else {
1924 SecDbItemSetValueWithName(item, CFSTR("musr"), musr, &state->s->error);
1925 CFRelease(musr);
1926 }
1927 }
1928
1929 /*
1930 *
1931 */
1932
1933 if (item) {
1934 bool insertStatus;
1935
1936 if(state->s->filter != kSecSysBoundItemFilter) {
1937 SecDbItemExtractRowIdFromBackupDictionary(item, dict, &state->s->error);
1938 }
1939 SecDbItemInferSyncable(item, &state->s->error);
1940 insertStatus = SecDbItemInsert(item, state->s->dbt, false, &state->s->error);
1941 if (!insertStatus) {
1942 /*
1943 When running in EduMode, multiple users share the same
1944 keychain and unfortionaly the rowid is used a
1945 persistant reference and is part of the contraints (its
1946 UNIQUE), so lets clear the rowid and try to insert the
1947 entry again.
1948
1949 This even happens for normal operation because of
1950 SysBound entries, so in case of a failure, lets try
1951 again to insert the record.
1952 */
1953 SecDbItemClearRowId(item, NULL);
1954 SecDbItemInsert(item, state->s->dbt, false, &state->s->error);
1955 }
1956 }
1957
1958 /* Reset error if we had one, since we just skip the current item
1959 and continue importing what we can. */
1960 if (state->s->error) {
1961 secwarning("Failed to import an item (%@) of class '%@': %@ - ignoring error.",
1962 item, state->class->name, state->s->error);
1963 CFReleaseNull(state->s->error);
1964 }
1965
1966 CFReleaseSafe(item);
1967 }
1968
1969 static void SecServerImportClass(const void *key, const void *value,
1970 void *context) {
1971 struct SecServerImportClassState *state =
1972 (struct SecServerImportClassState *)context;
1973 if (state->error)
1974 return;
1975 if (!isString(key)) {
1976 SecError(errSecParam, &state->error, CFSTR("class name %@ is not a string"), key);
1977 return;
1978 }
1979 /* ignore the Keybag UUID */
1980 if (CFEqual(key, kSecBackupKeybagUUIDKey))
1981 return;
1982 const SecDbClass *class = kc_class_with_name(key);
1983 if (!class) {
1984 secwarning("Ignoring unknown key class '%@'", key);
1985 return;
1986 }
1987 if (class == identity_class()) {
1988 SecError(errSecParam, &state->error, CFSTR("attempt to import an identity"));
1989 return;
1990 }
1991 struct SecServerImportItemState item_state = {
1992 .class = class, .s = state,
1993 };
1994 if (isArray(value)) {
1995 CFArrayRef items = (CFArrayRef)value;
1996 secwarning("Import %ld items of class %@ (filter %d)", (long)CFArrayGetCount(items), key, state->filter);
1997 SecSignpostBackupCount(SecSignpostImpulseRestoreClassCount, class->name, CFArrayGetCount(items), state->filter);
1998 CFArrayApplyFunction(items, CFRangeMake(0, CFArrayGetCount(items)),
1999 SecServerImportItem, &item_state);
2000 } else if (isDictionary(value)) {
2001 CFDictionaryRef item = (CFDictionaryRef)value;
2002 secwarning("Import %ld items of class %@ (filter %d)", (long)1, key, state->filter);
2003 SecSignpostBackupCount(SecSignpostImpulseRestoreClassCount, class->name, 1, state->filter);
2004 SecServerImportItem(item, &item_state);
2005 } else {
2006 secwarning("Unknown value type for class %@ (filter %d)", key, state->filter);
2007 }
2008 }
2009
2010 bool SecServerImportKeychainInPlist(SecDbConnectionRef dbt, SecurityClient *client,
2011 keybag_handle_t src_keybag, keybag_handle_t dest_keybag,
2012 CFDictionaryRef keychain, enum SecItemFilter filter,
2013 bool removeKeychainContent, CFErrorRef *error) {
2014 CFStringRef keybaguuid = NULL;
2015 bool ok = true;
2016
2017 CFDictionaryRef sys_bound = NULL;
2018 if (filter == kSecBackupableItemFilter) {
2019 /* Grab a copy of all the items for which SecItemIsSystemBound()
2020 returns true. */
2021 require(sys_bound = SecServerCopyKeychainPlist(dbt, client, KEYBAG_DEVICE,
2022 KEYBAG_NONE, kSecSysBoundItemFilter,
2023 error), errOut);
2024 }
2025
2026 /*
2027 * Validate the uuid of the source keybag matches what we have in the backup
2028 */
2029 keybaguuid = SecCreateKeybagUUID(src_keybag);
2030 if (keybaguuid) {
2031 CFStringRef uuid = CFDictionaryGetValue(keychain, kSecBackupKeybagUUIDKey);
2032 if (isString(uuid)) {
2033 require_action(CFEqual(keybaguuid, uuid), errOut,
2034 SecError(errSecDecode, error, CFSTR("Keybag UUID (%@) mismatch with backup (%@)"),
2035 keybaguuid, uuid));
2036 }
2037 }
2038
2039 #if TARGET_OS_IOS
2040 /*
2041 * Shared iPad is very special, it always delete's the user keychain, and never merge content
2042 */
2043 if (client->inMultiUser) {
2044 CFDataRef musrView = SecMUSRCreateActiveUserUUID(client->uid);
2045 require_action(musrView, errOut, ok = false);
2046 require_action(ok = SecServerDeleteAllForUser(dbt, musrView, true, error), errOut, CFReleaseNull(musrView));
2047 CFReleaseNull(musrView);
2048 } else
2049 #endif
2050 {
2051 /*
2052 * Delete everything in the keychain.
2053 * We don't want this if we're restoring backups because we probably already synced stuff over
2054 */
2055 if (removeKeychainContent) {
2056 require(ok = SecServerDeleteAll(dbt, error), errOut);
2057 } else {
2058 // Custom hack to support bluetooth's workflow for 11.3. Should be removed in a future release.
2059 __block CFErrorRef btError = NULL;
2060 bool deletedBT = kc_transaction(dbt, &btError, ^bool{
2061
2062 #define EXCLUDE_AGRPS "'com.apple.security.sos', 'com.apple.security.sos-usercredential', 'com.apple.security.ckks', 'com.apple.security.egoIdentities', 'com.apple.security.octagon'"
2063
2064 bool tok = SecDbExec(dbt, CFSTR("DELETE FROM genp WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS ");"), &btError);
2065 tok &= SecDbExec(dbt, CFSTR("DELETE FROM inet WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS ");"), &btError);
2066 tok &= SecDbExec(dbt, CFSTR("DELETE FROM cert WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS ");"), &btError);
2067 tok &= SecDbExec(dbt, CFSTR("DELETE FROM keys WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS ");"), &btError);
2068
2069 #undef EXCLUDE_AGRPS
2070 return tok;
2071 });
2072 if (!deletedBT) {
2073 secerror("Unable to delete nonsyncable items prior to keychain restore: %@", btError);
2074 } else {
2075 secnotice("restore", "Successfully deleted nonsyncable items");
2076 }
2077 CFReleaseNull(btError);
2078 }
2079 }
2080
2081 struct SecServerImportClassState state = {
2082 .dbt = dbt,
2083 .src_keybag = src_keybag,
2084 .dest_keybag = dest_keybag,
2085 .client = client,
2086 .filter = filter,
2087 };
2088 /* Import the provided items, preserving rowids. */
2089 secwarning("Restoring backup items '%ld'", (long)CFDictionaryGetCount(keychain));
2090 CFDictionaryApplyFunction(keychain, SecServerImportClass, &state);
2091
2092 if (sys_bound) {
2093 state.src_keybag = KEYBAG_NONE;
2094 /* Import the items we preserved with random rowids. */
2095 state.filter = kSecSysBoundItemFilter;
2096 secwarning("Restoring sysbound items '%ld'", (long)CFDictionaryGetCount(sys_bound));
2097 CFDictionaryApplyFunction(sys_bound, SecServerImportClass, &state);
2098 }
2099 if (state.error) {
2100 if (error) {
2101 CFReleaseSafe(*error);
2102 *error = state.error;
2103 } else {
2104 CFRelease(state.error);
2105 }
2106 ok = false;
2107 }
2108
2109 // If CKKS had spun up, it's very likely that we just deleted its data.
2110 // Tell it to perform a local resync.
2111 #if OCTAGON
2112 SecCKKSPerformLocalResync();
2113 #endif
2114
2115 errOut:
2116 CFReleaseSafe(sys_bound);
2117 CFReleaseSafe(keybaguuid);
2118
2119 return ok;
2120 }
2121
2122 CFStringRef
2123 SecServerBackupGetKeybagUUID(CFDictionaryRef keychain, CFErrorRef *error)
2124 {
2125 CFStringRef uuid = CFDictionaryGetValue(keychain, kSecBackupKeybagUUIDKey);
2126 if (!isString(uuid)) {
2127 SecError(errSecDecode, error, CFSTR("Missing or invalid %@ in backup dictionary"), kSecBackupKeybagUUIDKey);
2128 return NULL;
2129 }
2130 return uuid;
2131 }
2132
2133 #pragma mark - key rolling support
2134 #if USE_KEYSTORE
2135
2136 struct check_generation_ctx {
2137 struct s3dl_query_ctx query_ctx;
2138 uint32_t current_generation;
2139 };
2140
2141 static void check_generation(sqlite3_stmt *stmt, void *context) {
2142 struct check_generation_ctx *c = context;
2143 CFDataRef blob = NULL;
2144 size_t blobLen = 0;
2145 const uint8_t *cursor = NULL;
2146 uint32_t version;
2147 keyclass_t keyclass;
2148 uint32_t current_generation = c->current_generation;
2149
2150 require(blob = s3dl_copy_data_from_col(stmt, 1, &c->query_ctx.q->q_error), out);
2151 blobLen = CFDataGetLength(blob);
2152 cursor = CFDataGetBytePtr(blob);
2153
2154 /* Check for underflow, ensuring we have at least one full AES block left. */
2155 if (blobLen < sizeof(version) + sizeof(keyclass)) {
2156 SecError(errSecDecode, &c->query_ctx.q->q_error, CFSTR("check_generation: Check for underflow"));
2157 goto out;
2158 }
2159
2160 version = *((uint32_t *)cursor);
2161 cursor += sizeof(version);
2162
2163 (void) version; // TODO: do something with the version number.
2164
2165 keyclass = *((keyclass_t *)cursor);
2166
2167 // TODO: export get_key_gen macro
2168 if (((keyclass & ~key_class_last) == 0) != (current_generation == 0)) {
2169 c->query_ctx.found++;
2170 }
2171
2172 CFReleaseSafe(blob);
2173 return;
2174
2175 out:
2176 c->query_ctx.found++;
2177 CFReleaseSafe(blob);
2178 }
2179
2180 bool s3dl_dbt_keys_current(SecDbConnectionRef dbt, uint32_t current_generation, CFErrorRef *error) {
2181 CFErrorRef localError = NULL;
2182 struct check_generation_ctx ctx = { .query_ctx = { .dbt = dbt }, .current_generation = current_generation };
2183
2184 const SecDbClass *classes[] = {
2185 genp_class(),
2186 inet_class(),
2187 keys_class(),
2188 cert_class(),
2189 };
2190
2191 for (size_t class_ix = 0; class_ix < array_size(classes); ++class_ix) {
2192 Query *q = query_create(classes[class_ix], NULL, NULL, NULL, &localError);
2193 if (!q)
2194 return false;
2195
2196 ctx.query_ctx.q = q;
2197 q->q_limit = kSecMatchUnlimited;
2198
2199 bool ok = s3dl_query(check_generation, &ctx, &localError);
2200 query_destroy(q, NULL);
2201 CFReleaseNull(ctx.query_ctx.result);
2202
2203 if (!ok && localError && (CFErrorGetCode(localError) == errSecItemNotFound)) {
2204 CFReleaseNull(localError);
2205 continue;
2206 }
2207 secerror("Class %@ not up to date", classes[class_ix]->name);
2208 return false;
2209 }
2210 return true;
2211 }
2212
2213 bool s3dl_dbt_update_keys(SecDbConnectionRef dbt, SecurityClient *client, CFErrorRef *error) {
2214 return SecDbTransaction(dbt, kSecDbExclusiveTransactionType, error, ^(bool *commit) {
2215 __block bool ok = false;
2216 uint32_t keystore_generation_status;
2217
2218 /* can we migrate to new class keys right now? */
2219 if (!aks_generation(KEYBAG_DEVICE, generation_noop, &keystore_generation_status) &&
2220 (keystore_generation_status & generation_change_in_progress)) {
2221
2222 /* take a lock assertion */
2223 bool operated_while_unlocked = SecAKSDoWithUserBagLockAssertion(error, ^{
2224 CFErrorRef localError = NULL;
2225 CFDictionaryRef backup = SecServerCopyKeychainPlist(dbt, NULL,
2226 KEYBAG_DEVICE, KEYBAG_NONE, kSecNoItemFilter, &localError);
2227 if (backup) {
2228 if (localError) {
2229 secerror("Ignoring export error: %@ during roll export", localError);
2230 CFReleaseNull(localError);
2231 }
2232 // 'true' argument: we're replacing everything with newly wrapped entries so remove the old stuff
2233 ok = SecServerImportKeychainInPlist(dbt, client, KEYBAG_NONE,
2234 KEYBAG_DEVICE, backup, kSecNoItemFilter, true, &localError);
2235 if (localError) {
2236 secerror("Ignoring export error: %@ during roll export", localError);
2237 CFReleaseNull(localError);
2238 }
2239 CFRelease(backup);
2240 }
2241 });
2242 if (!operated_while_unlocked)
2243 ok = false;
2244 } else {
2245 ok = SecError(errSecBadReq, error, CFSTR("No key roll in progress."));
2246 }
2247
2248 *commit = ok;
2249 });
2250 }
2251 #endif