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