]> git.saurik.com Git - apple/security.git/blob - Security/sec/securityd/SecItemDb.c
Security-57031.30.12.tar.gz
[apple/security.git] / Security / 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
32 #include <securityd/SecDbKeychainItem.h>
33 #include <securityd/SecItemSchema.h>
34 #include <securityd/SecItemServer.h>
35 #include <Security/SecBasePriv.h>
36 #include <Security/SecItem.h>
37 #include <Security/SecItemPriv.h>
38 #include <Security/SecItemInternal.h>
39 #include <securityd/SOSCloudCircleServer.h>
40 #include <utilities/array_size.h>
41 #include <utilities/SecIOFormat.h>
42
43 /* label when certificate data is joined with key data */
44 #define CERTIFICATE_DATA_COLUMN_LABEL "certdata"
45
46 const SecDbAttr *SecDbAttrWithKey(const SecDbClass *c,
47 CFTypeRef key,
48 CFErrorRef *error) {
49 /* Special case: identites can have all attributes of either cert
50 or keys. */
51 if (c == &identity_class) {
52 const SecDbAttr *desc;
53 if (!(desc = SecDbAttrWithKey(&cert_class, key, 0)))
54 desc = SecDbAttrWithKey(&keys_class, key, error);
55 return desc;
56 }
57
58 if (isString(key)) {
59 SecDbForEachAttr(c, a) {
60 if (CFEqual(a->name, key))
61 return a;
62 }
63 }
64
65 SecError(errSecNoSuchAttr, error, CFSTR("attribute %@ not found in class %@"), key, c->name);
66
67 return NULL;
68 }
69
70 bool kc_transaction(SecDbConnectionRef dbt, CFErrorRef *error, bool(^perform)()) {
71 __block bool ok = true;
72 return ok && SecDbTransaction(dbt, kSecDbExclusiveTransactionType, error, ^(bool *commit) {
73 ok = *commit = perform();
74 });
75 }
76
77 static CFStringRef SecDbGetKindSQL(SecDbAttrKind kind) {
78 switch (kind) {
79 case kSecDbBlobAttr:
80 case kSecDbDataAttr:
81 case kSecDbSHA1Attr:
82 case kSecDbPrimaryKeyAttr:
83 case kSecDbEncryptedDataAttr:
84 return CFSTR("BLOB");
85 case kSecDbAccessAttr:
86 case kSecDbStringAttr:
87 return CFSTR("TEXT");
88 case kSecDbNumberAttr:
89 case kSecDbSyncAttr:
90 case kSecDbTombAttr:
91 return CFSTR("INTEGER");
92 case kSecDbDateAttr:
93 case kSecDbCreationDateAttr:
94 case kSecDbModificationDateAttr:
95 return CFSTR("REAL");
96 case kSecDbRowIdAttr:
97 return CFSTR("INTEGER PRIMARY KEY AUTOINCREMENT");
98 case kSecDbAccessControlAttr:
99 /* This attribute does not exist in the DB. */
100 return NULL;
101 }
102 }
103
104 static void SecDbAppendUnique(CFMutableStringRef sql, CFStringRef value, bool *haveUnique) {
105 assert(haveUnique);
106 if (!*haveUnique)
107 CFStringAppend(sql, CFSTR("UNIQUE("));
108
109 SecDbAppendElement(sql, value, haveUnique);
110 }
111
112 void SecDbAppendCreateTableWithClass(CFMutableStringRef sql, const SecDbClass *c) {
113 CFStringAppendFormat(sql, 0, CFSTR("CREATE TABLE %@("), c->name);
114 SecDbForEachAttrWithMask(c,desc,kSecDbInFlag) {
115 CFStringAppendFormat(sql, 0, CFSTR("%@ %@"), desc->name, SecDbGetKindSQL(desc->kind));
116 if (desc->flags & kSecDbNotNullFlag)
117 CFStringAppend(sql, CFSTR(" NOT NULL"));
118 if (desc->flags & kSecDbDefault0Flag)
119 CFStringAppend(sql, CFSTR(" DEFAULT 0"));
120 if (desc->flags & kSecDbDefaultEmptyFlag)
121 CFStringAppend(sql, CFSTR(" DEFAULT ''"));
122 CFStringAppend(sql, CFSTR(","));
123 }
124
125 bool haveUnique = false;
126 SecDbForEachAttrWithMask(c,desc,kSecDbPrimaryKeyFlag | kSecDbInFlag) {
127 SecDbAppendUnique(sql, desc->name, &haveUnique);
128 }
129 if (haveUnique)
130 CFStringAppend(sql, CFSTR(")"));
131
132 CFStringAppend(sql, CFSTR(");"));
133 }
134
135 static CFDataRef SecPersistentRefCreateWithItem(SecDbItemRef item, CFErrorRef *error) {
136 sqlite3_int64 row_id = SecDbItemGetRowId(item, error);
137 if (row_id)
138 return _SecItemMakePersistentRef(SecDbItemGetClass(item)->name, row_id);
139 return NULL;
140 }
141
142 CFTypeRef SecDbItemCopyResult(SecDbItemRef item, ReturnTypeMask return_type, CFErrorRef *error) {
143 CFTypeRef a_result;
144
145 if (return_type == 0) {
146 /* Caller isn't interested in any results at all. */
147 a_result = kCFNull;
148 } else if (return_type == kSecReturnDataMask) {
149 a_result = SecDbItemGetCachedValueWithName(item, kSecValueData);
150 if (a_result) {
151 CFRetainSafe(a_result);
152 } else {
153 a_result = CFDataCreate(kCFAllocatorDefault, NULL, 0);
154 }
155 } else if (return_type == kSecReturnPersistentRefMask) {
156 a_result = SecPersistentRefCreateWithItem(item, error);
157 } else {
158 CFMutableDictionaryRef dict = CFDictionaryCreateMutableForCFTypes(CFGetAllocator(item));
159 /* We need to return more than one value. */
160 if (return_type & kSecReturnRefMask) {
161 CFDictionarySetValue(dict, kSecClass, SecDbItemGetClass(item)->name);
162 }
163 CFOptionFlags mask = (((return_type & kSecReturnDataMask || return_type & kSecReturnRefMask) ? kSecDbReturnDataFlag : 0) |
164 ((return_type & kSecReturnAttributesMask || return_type & kSecReturnRefMask) ? kSecDbReturnAttrFlag : 0));
165 SecDbForEachAttr(SecDbItemGetClass(item), desc) {
166 if ((desc->flags & mask) != 0) {
167 CFTypeRef value = SecDbItemGetValue(item, desc, error);
168 if (value && !CFEqual(kCFNull, value)) {
169 CFDictionarySetValue(dict, desc->name, value);
170 } else if (value == NULL) {
171 CFReleaseNull(dict);
172 break;
173 }
174 }
175 }
176 if (return_type & kSecReturnPersistentRefMask) {
177 CFDataRef pref = SecPersistentRefCreateWithItem(item, error);
178 CFDictionarySetValue(dict, kSecValuePersistentRef, pref);
179 CFReleaseSafe(pref);
180 }
181
182 a_result = dict;
183 }
184
185 return a_result;
186 }
187
188 /* AUDIT[securityd](done):
189 attributes (ok) is a caller provided dictionary, only its cf type has
190 been checked.
191 */
192 bool
193 s3dl_query_add(SecDbConnectionRef dbt, Query *q, CFTypeRef *result, CFErrorRef *error)
194 {
195 if (query_match_count(q) != 0)
196 return errSecItemMatchUnsupported;
197
198 /* Add requires a class to be specified unless we are adding a ref. */
199 if (q->q_use_item_list)
200 return errSecUseItemListUnsupported;
201
202 /* Actual work here. */
203 SecDbItemRef item = SecDbItemCreateWithAttributes(kCFAllocatorDefault, q->q_class, q->q_item, KEYBAG_DEVICE, error);
204 if (!item)
205 return false;
206
207 bool ok = true;
208 if (q->q_data)
209 ok = SecDbItemSetValueWithName(item, CFSTR("v_Data"), q->q_data, error);
210 if (q->q_row_id)
211 ok = SecDbItemSetRowId(item, q->q_row_id, error);
212 SecDbItemSetCredHandle(item, q->q_use_cred_handle);
213
214 if (ok) {
215 ok = SecDbItemInsert(item, dbt, q->q_required_access_controls, error);
216 CFRetainAssign(q->q_use_cred_handle, item->credHandle);
217 }
218 if (ok) {
219 if (result && q->q_return_type) {
220 *result = SecDbItemCopyResult(item, q->q_return_type, error);
221 }
222 }
223 if (!ok && error && *error) {
224 if (CFEqual(CFErrorGetDomain(*error), kSecDbErrorDomain) && CFErrorGetCode(*error) == SQLITE_CONSTRAINT) {
225 CFReleaseNull(*error);
226 SecError(errSecDuplicateItem, error, CFSTR("duplicate item %@"), item);
227 }
228 }
229
230 if (ok) {
231 q->q_changed = true;
232 if (SecDbItemIsSyncable(item))
233 q->q_sync_changed = true;
234 }
235
236 secdebug("dbitem", "inserting item %@%s%@", item, ok ? "" : "failed: ", ok || error == NULL ? (CFErrorRef)CFSTR("") : *error);
237
238 CFRelease(item);
239
240 return ok;
241 }
242
243 typedef void (*s3dl_handle_row)(sqlite3_stmt *stmt, void *context);
244
245 static CFDataRef
246 s3dl_copy_data_from_col(sqlite3_stmt *stmt, int col, CFErrorRef *error) {
247 return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt, col),
248 sqlite3_column_bytes(stmt, col),
249 kCFAllocatorNull);
250 }
251
252 static bool
253 s3dl_item_from_col(sqlite3_stmt *stmt, Query *q, int col, CFArrayRef accessGroups,
254 CFMutableDictionaryRef *item, SecAccessControlRef *access_control, CFErrorRef *error) {
255 CFDataRef edata = NULL;
256 bool ok = false;
257 require(edata = s3dl_copy_data_from_col(stmt, col, error), out);
258 ok = s3dl_item_from_data(edata, q, accessGroups, item, access_control, error);
259
260 out:
261 CFReleaseSafe(edata);
262 return ok;
263 }
264
265 struct s3dl_query_ctx {
266 Query *q;
267 CFArrayRef accessGroups;
268 SecDbConnectionRef dbt;
269 CFTypeRef result;
270 int found;
271 };
272
273 /* Return whatever the caller requested based on the value of q->q_return_type.
274 keys and values must be 3 larger than attr_count in size to accomadate the
275 optional data, class and persistent ref results. This is so we can use
276 the CFDictionaryCreate() api here rather than appending to a
277 mutable dictionary. */
278 static CF_RETURNS_RETAINED CFTypeRef handle_result(Query *q, CFMutableDictionaryRef item,
279 sqlite_int64 rowid) {
280 CFTypeRef a_result;
281 CFDataRef data;
282 data = CFDictionaryGetValue(item, kSecValueData);
283 if (q->q_return_type == 0) {
284 /* Caller isn't interested in any results at all. */
285 a_result = kCFNull;
286 } else if (q->q_return_type == kSecReturnDataMask) {
287 if (data) {
288 a_result = data;
289 CFRetain(a_result);
290 } else {
291 a_result = CFDataCreate(kCFAllocatorDefault, NULL, 0);
292 }
293 } else if (q->q_return_type == kSecReturnPersistentRefMask) {
294 a_result = _SecItemMakePersistentRef(q->q_class->name, rowid);
295 } else {
296 /* We need to return more than one value. */
297 if (q->q_return_type & kSecReturnRefMask) {
298 CFDictionarySetValue(item, kSecClass, q->q_class->name);
299 } else if ((q->q_return_type & kSecReturnAttributesMask)) {
300 if (!(q->q_return_type & kSecReturnDataMask)) {
301 CFDictionaryRemoveValue(item, kSecValueData);
302 }
303 } else {
304 CFRetainSafe(data);
305 CFDictionaryRemoveAllValues(item);
306 if ((q->q_return_type & kSecReturnDataMask) && data) {
307 CFDictionarySetValue(item, kSecValueData, data);
308 }
309 CFReleaseSafe(data);
310 }
311 if (q->q_return_type & kSecReturnPersistentRefMask) {
312 CFDataRef pref = _SecItemMakePersistentRef(q->q_class->name, rowid);
313 CFDictionarySetValue(item, kSecValuePersistentRef, pref);
314 CFRelease(pref);
315 }
316
317 a_result = item;
318 CFRetain(item);
319 }
320
321 return a_result;
322 }
323
324 static void s3dl_merge_into_dict(const void *key, const void *value, void *context) {
325 CFDictionarySetValue(context, key, value);
326 }
327
328 static void s3dl_query_row(sqlite3_stmt *stmt, void *context) {
329 struct s3dl_query_ctx *c = context;
330 Query *q = c->q;
331
332 sqlite_int64 rowid = sqlite3_column_int64(stmt, 0);
333 CFMutableDictionaryRef item;
334 bool ok = s3dl_item_from_col(stmt, q, 1, c->accessGroups, &item, NULL, &q->q_error);
335 if (!ok) {
336 OSStatus status = SecErrorGetOSStatus(q->q_error);
337 // errSecDecode means the item is corrupted, stash it for delete.
338 if (status == errSecDecode) {
339 secwarning("ignoring corrupt %@,rowid=%" PRId64 " %@", q->q_class->name, rowid, q->q_error);
340 {
341 CFDataRef edata = s3dl_copy_data_from_col(stmt, 1, NULL);
342 CFMutableStringRef edatastring = CFStringCreateMutable(kCFAllocatorDefault, 0);
343 if(edatastring) {
344 CFStringAppendEncryptedData(edatastring, edata);
345 secnotice("item", "corrupted edata=%@", edatastring);
346 }
347 CFReleaseSafe(edata);
348 CFReleaseSafe(edatastring);
349 }
350 CFReleaseNull(q->q_error);
351 } else {
352 secerror("decode %@,rowid=%" PRId64 " failed (%" PRIdOSStatus "): %@", q->q_class->name, rowid, status, q->q_error);
353 }
354 // q->q_error will be released appropriately by a call to query_error
355 return;
356 }
357
358 if (!item)
359 goto out;
360
361 if (q->q_class == &identity_class) {
362 // TODO: Use col 2 for key rowid and use both rowids in persistent ref.
363
364 CFMutableDictionaryRef key;
365 /* TODO : if there is a errSecDecode error here, we should cleanup */
366 if (!s3dl_item_from_col(stmt, q, 3, c->accessGroups, &key, NULL, &q->q_error) || !key)
367 goto out;
368
369 CFDataRef certData = CFDictionaryGetValue(item, kSecValueData);
370 if (certData) {
371 CFDictionarySetValue(key, CFSTR(CERTIFICATE_DATA_COLUMN_LABEL),
372 certData);
373 CFDictionaryRemoveValue(item, kSecValueData);
374 }
375 CFDictionaryApplyFunction(item, s3dl_merge_into_dict, key);
376 CFRelease(item);
377 item = key;
378 }
379
380 if (!match_item(c->dbt, q, c->accessGroups, item))
381 goto out;
382
383 CFTypeRef a_result = handle_result(q, item, rowid);
384 if (a_result) {
385 if (a_result == kCFNull) {
386 /* Caller wasn't interested in a result, but we still
387 count this row as found. */
388 CFRelease(a_result); // Help shut up clang
389 } else if (q->q_limit == 1) {
390 c->result = a_result;
391 } else {
392 CFArrayAppendValue((CFMutableArrayRef)c->result, a_result);
393 CFRelease(a_result);
394 }
395 c->found++;
396 }
397
398 out:
399 CFReleaseSafe(item);
400 }
401
402 static void
403 SecDbAppendWhereROWID(CFMutableStringRef sql,
404 CFStringRef col, sqlite_int64 row_id,
405 bool *needWhere) {
406 if (row_id > 0) {
407 SecDbAppendWhereOrAnd(sql, needWhere);
408 CFStringAppendFormat(sql, NULL, CFSTR("%@=%lld"), col, row_id);
409 }
410 }
411
412 static void
413 SecDbAppendWhereAttrs(CFMutableStringRef sql, const Query *q, bool *needWhere) {
414 CFIndex ix, attr_count = query_attr_count(q);
415 for (ix = 0; ix < attr_count; ++ix) {
416 SecDbAppendWhereOrAndEquals(sql, query_attr_at(q, ix).key, needWhere);
417 }
418 }
419
420 static void
421 SecDbAppendWhereAccessGroups(CFMutableStringRef sql,
422 CFStringRef col,
423 CFArrayRef accessGroups,
424 bool *needWhere) {
425 CFIndex ix, ag_count;
426 if (!accessGroups || 0 == (ag_count = CFArrayGetCount(accessGroups))) {
427 return;
428 }
429
430 SecDbAppendWhereOrAnd(sql, needWhere);
431 CFStringAppend(sql, col);
432 CFStringAppend(sql, CFSTR(" IN (?"));
433 for (ix = 1; ix < ag_count; ++ix) {
434 CFStringAppend(sql, CFSTR(",?"));
435 }
436 CFStringAppend(sql, CFSTR(")"));
437 }
438
439 static void SecDbAppendWhereClause(CFMutableStringRef sql, const Query *q,
440 CFArrayRef accessGroups) {
441 bool needWhere = true;
442 SecDbAppendWhereROWID(sql, CFSTR("ROWID"), q->q_row_id, &needWhere);
443 SecDbAppendWhereAttrs(sql, q, &needWhere);
444 SecDbAppendWhereAccessGroups(sql, CFSTR("agrp"), accessGroups, &needWhere);
445 }
446
447 static void SecDbAppendLimit(CFMutableStringRef sql, CFIndex limit) {
448 if (limit != kSecMatchUnlimited)
449 CFStringAppendFormat(sql, NULL, CFSTR(" LIMIT %" PRIdCFIndex), limit);
450 }
451
452 static CFStringRef s3dl_select_sql(Query *q, CFArrayRef accessGroups) {
453 CFMutableStringRef sql = CFStringCreateMutable(NULL, 0);
454 if (q->q_class == &identity_class) {
455 CFStringAppendFormat(sql, NULL, CFSTR("SELECT crowid, "
456 CERTIFICATE_DATA_COLUMN_LABEL ", rowid,data FROM "
457 "(SELECT cert.rowid AS crowid, cert.labl AS labl,"
458 " cert.issr AS issr, cert.slnr AS slnr, cert.skid AS skid,"
459 " keys.*,cert.data AS " CERTIFICATE_DATA_COLUMN_LABEL
460 " FROM keys, cert"
461 " WHERE keys.priv == 1 AND cert.pkhh == keys.klbl"));
462 SecDbAppendWhereAccessGroups(sql, CFSTR("cert.agrp"), accessGroups, 0);
463 /* The next 3 SecDbAppendWhere calls are in the same order as in
464 SecDbAppendWhereClause(). This makes sqlBindWhereClause() work,
465 as long as we do an extra sqlBindAccessGroups first. */
466 SecDbAppendWhereROWID(sql, CFSTR("crowid"), q->q_row_id, 0);
467 CFStringAppend(sql, CFSTR(")"));
468 bool needWhere = true;
469 SecDbAppendWhereAttrs(sql, q, &needWhere);
470 SecDbAppendWhereAccessGroups(sql, CFSTR("agrp"), accessGroups, &needWhere);
471 } else {
472 CFStringAppend(sql, CFSTR("SELECT rowid, data FROM "));
473 CFStringAppend(sql, q->q_class->name);
474 SecDbAppendWhereClause(sql, q, accessGroups);
475 }
476 SecDbAppendLimit(sql, q->q_limit);
477
478 return sql;
479 }
480
481 static bool sqlBindAccessGroups(sqlite3_stmt *stmt, CFArrayRef accessGroups,
482 int *pParam, CFErrorRef *error) {
483 bool result = true;
484 int param = *pParam;
485 CFIndex ix, count = accessGroups ? CFArrayGetCount(accessGroups) : 0;
486 for (ix = 0; ix < count; ++ix) {
487 result = SecDbBindObject(stmt, param++,
488 CFArrayGetValueAtIndex(accessGroups, ix),
489 error);
490 if (!result)
491 break;
492 }
493 *pParam = param;
494 return result;
495 }
496
497 static bool sqlBindWhereClause(sqlite3_stmt *stmt, const Query *q,
498 CFArrayRef accessGroups, int *pParam, CFErrorRef *error) {
499 bool result = true;
500 int param = *pParam;
501 CFIndex ix, attr_count = query_attr_count(q);
502 for (ix = 0; ix < attr_count; ++ix) {
503 result = SecDbBindObject(stmt, param++, query_attr_at(q, ix).value, error);
504 if (!result)
505 break;
506 }
507
508 /* Bind the access group to the sql. */
509 if (result) {
510 result = sqlBindAccessGroups(stmt, accessGroups, &param, error);
511 }
512
513 *pParam = param;
514 return result;
515 }
516
517 bool SecDbItemQuery(SecDbQueryRef query, CFArrayRef accessGroups, SecDbConnectionRef dbconn, CFErrorRef *error,
518 void (^handle_row)(SecDbItemRef item, bool *stop)) {
519 __block bool ok = true;
520 __block bool decrypted = true;
521 /* Sanity check the query. */
522 if (query->q_ref)
523 return SecError(errSecValueRefUnsupported, error, CFSTR("value ref not supported by queries"));
524
525 bool (^return_attr)(const SecDbAttr *attr) = ^bool (const SecDbAttr * attr) {
526 return attr->kind == kSecDbRowIdAttr || attr->kind == kSecDbEncryptedDataAttr || attr->kind == kSecDbSHA1Attr;
527 };
528
529 CFStringRef sql = s3dl_select_sql(query, accessGroups);
530 ok = sql;
531 if (sql) {
532 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) {
533 /* Bind the values being searched for to the SELECT statement. */
534 int param = 1;
535 if (query->q_class == &identity_class) {
536 /* Bind the access groups to cert.agrp. */
537 ok &= sqlBindAccessGroups(stmt, accessGroups, &param, error);
538 }
539 if (ok)
540 ok &= sqlBindWhereClause(stmt, query, accessGroups, &param, error);
541 if (ok) {
542 SecDbStep(dbconn, stmt, error, ^(bool *stop) {
543 SecDbItemRef item = SecDbItemCreateWithStatement(kCFAllocatorDefault, query->q_class, stmt, query->q_keybag, error, return_attr);
544 if (item) {
545 if (query->q_required_access_controls) {
546 CFDataRef authNeeded = NULL;
547 item->cryptoOp = query->q_crypto_op;
548 SecDbItemSetCallerAccessGroups(item, query->q_caller_access_groups);
549 decrypted = SecDbItemEnsureDecrypted(item, &authNeeded, &query->q_use_cred_handle, NULL);
550 if (decrypted && authNeeded) {
551 // Do not process the item right now, just remember that it will need
552 // authentication to properly process it.
553 CFArrayAppendValue(query->q_required_access_controls, authNeeded);
554 CFRelease(authNeeded);
555 CFRelease(item);
556 return;
557 }
558 CFReleaseSafe(authNeeded);
559 }
560 if (match_item(dbconn, query, accessGroups, item->attributes))
561 handle_row(item, stop);
562 CFRelease(item);
563 } else {
564 secerror("failed to create item from stmt: %@", error ? *error : (CFErrorRef)"no error");
565 if (error) {
566 CFReleaseNull(*error);
567 }
568 //*stop = true;
569 //ok = false;
570 }
571 });
572 }
573 });
574 CFRelease(sql);
575 }
576
577 return ok;
578 }
579
580 static bool
581 s3dl_query(s3dl_handle_row handle_row,
582 void *context, CFErrorRef *error)
583 {
584 struct s3dl_query_ctx *c = context;
585 SecDbConnectionRef dbt = c->dbt;
586 Query *q = c->q;
587 CFArrayRef accessGroups = c->accessGroups;
588
589 /* Sanity check the query. */
590 if (q->q_ref)
591 return SecError(errSecValueRefUnsupported, error, CFSTR("value ref not supported by queries"));
592
593 /* Actual work here. */
594 if (q->q_limit == 1) {
595 c->result = NULL;
596 } else {
597 c->result = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
598 }
599 CFStringRef sql = s3dl_select_sql(q, accessGroups);
600 bool ok = SecDbWithSQL(dbt, sql, error, ^(sqlite3_stmt *stmt) {
601 bool sql_ok = true;
602 /* Bind the values being searched for to the SELECT statement. */
603 int param = 1;
604 if (q->q_class == &identity_class) {
605 /* Bind the access groups to cert.agrp. */
606 sql_ok = sqlBindAccessGroups(stmt, accessGroups, &param, error);
607 }
608 if (sql_ok)
609 sql_ok = sqlBindWhereClause(stmt, q, accessGroups, &param, error);
610 if (sql_ok) {
611 SecDbForEach(stmt, error, ^bool (int row_index) {
612 handle_row(stmt, context);
613 return (!q->q_error) && (q->q_limit == kSecMatchUnlimited || c->found < q->q_limit);
614 });
615 }
616 return sql_ok;
617 });
618
619 CFRelease(sql);
620
621 // First get the error from the query, since errSecDuplicateItem from an
622 // update query should superceed the errSecItemNotFound below.
623 if (!query_error(q, error))
624 ok = false;
625 if (ok && c->found == 0)
626 ok = SecError(errSecItemNotFound, error, CFSTR("no matching items found"));
627
628 return ok;
629 }
630
631 bool
632 s3dl_copy_matching(SecDbConnectionRef dbt, Query *q, CFTypeRef *result,
633 CFArrayRef accessGroups, CFErrorRef *error)
634 {
635 struct s3dl_query_ctx ctx = {
636 .q = q, .accessGroups = accessGroups, .dbt = dbt,
637 };
638 if (q->q_row_id && query_attr_count(q))
639 return SecError(errSecItemIllegalQuery, error,
640 CFSTR("attributes to query illegal; both row_id and other attributes can't be searched at the same time"));
641
642 // Only copy things that aren't tombstones unless the client explicitly asks otherwise.
643 if (!CFDictionaryContainsKey(q->q_item, kSecAttrTombstone))
644 query_add_attribute(kSecAttrTombstone, kCFBooleanFalse, q);
645 bool ok = s3dl_query(s3dl_query_row, &ctx, error);
646 if (ok && result)
647 *result = ctx.result;
648 else
649 CFReleaseSafe(ctx.result);
650
651 return ok;
652 }
653
654 /* AUDIT[securityd](done):
655 attributesToUpdate (ok) is a caller provided dictionary,
656 only its cf types have been checked.
657 */
658 bool
659 s3dl_query_update(SecDbConnectionRef dbt, Query *q,
660 CFDictionaryRef attributesToUpdate, CFArrayRef accessGroups, CFErrorRef *error)
661 {
662 /* Sanity check the query. */
663 if (query_match_count(q) != 0)
664 return SecError(errSecItemMatchUnsupported, error, CFSTR("match not supported in attributes to update"));
665 if (q->q_ref)
666 return SecError(errSecValueRefUnsupported, error, CFSTR("value ref not supported in attributes to update"));
667 if (q->q_row_id && query_attr_count(q))
668 return SecError(errSecItemIllegalQuery, error, CFSTR("attributes to update illegal; both row_id and other attributes can't be updated at the same time"));
669
670 __block bool result = true;
671 Query *u = query_create(q->q_class, attributesToUpdate, error);
672 if (u == NULL) return false;
673 require_action_quiet(query_update_parse(u, attributesToUpdate, error), errOut, result = false);
674 query_pre_update(u);
675 result &= SecDbTransaction(dbt, kSecDbExclusiveTransactionType, error, ^(bool *commit) {
676 // Make sure we only update real items, not tombstones, unless the client explicitly asks otherwise.
677 if (!CFDictionaryContainsKey(q->q_item, kSecAttrTombstone))
678 query_add_attribute(kSecAttrTombstone, kCFBooleanFalse, q);
679 result &= SecDbItemQuery(q, accessGroups, dbt, error, ^(SecDbItemRef item, bool *stop) {
680 // We always need to know the error here.
681 CFErrorRef localError = NULL;
682 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
683 const SecDbAttr *sha1attr = SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, NULL);
684 CFDataRef storedSHA1 = CFRetainSafe(SecDbItemGetValue(item, sha1attr, NULL));
685 SecDbItemRef new_item = SecDbItemCopyWithUpdates(item, u->q_item, &localError);
686 SecDbItemSetValue(item, sha1attr, storedSHA1, NULL);
687 CFReleaseSafe(storedSHA1);
688 if (SecErrorGetOSStatus(localError) == errSecDecode) {
689 // We just ignore this, and treat as if item is not found.
690 secwarning("deleting corrupt %@,rowid=%" PRId64 " %@", q->q_class->name, SecDbItemGetRowId(item, NULL), localError);
691 CFReleaseNull(localError);
692 if (!SecDbItemDelete(item, dbt, false, &localError)) {
693 secerror("failed to delete corrupt %@,rowid=%" PRId64 " %@", q->q_class->name, SecDbItemGetRowId(item, NULL), localError);
694 CFReleaseNull(localError);
695 }
696 return;
697 }
698 if (new_item != NULL && u->q_access_control != NULL)
699 SecDbItemSetAccessControl(new_item, u->q_access_control, &localError);
700 result = CFErrorPropagate(localError, error) && new_item;
701 if (new_item) {
702 bool item_is_sync = SecDbItemIsSyncable(item);
703 bool makeTombstone = q->q_use_tomb ? CFBooleanGetValue(q->q_use_tomb) : (item_is_sync && !SecDbItemIsTombstone(item));
704 result = SecDbItemUpdate(item, new_item, dbt, makeTombstone, error);
705 if (result) {
706 q->q_changed = true;
707 if (item_is_sync || SecDbItemIsSyncable(new_item))
708 q->q_sync_changed = true;
709 }
710 CFRelease(new_item);
711 }
712 if (!result)
713 *stop = true;
714 });
715 if (!result)
716 *commit = false;
717 });
718 if (result && !q->q_changed)
719 result = SecError(errSecItemNotFound, error, CFSTR("No items updated"));
720 errOut:
721 if (!query_destroy(u, error))
722 result = false;
723 return result;
724 }
725
726 bool
727 s3dl_query_delete(SecDbConnectionRef dbt, Query *q, CFArrayRef accessGroups, CFErrorRef *error)
728 {
729 __block bool ok = true;
730 // Only delete things that aren't tombstones, unless the client explicitly asks otherwise.
731 if (!CFDictionaryContainsKey(q->q_item, kSecAttrTombstone))
732 query_add_attribute(kSecAttrTombstone, kCFBooleanFalse, q);
733 ok &= SecDbItemSelect(q, dbt, error, ^bool(const SecDbAttr *attr) {
734 return false;
735 },^bool(CFMutableStringRef sql, bool *needWhere) {
736 SecDbAppendWhereClause(sql, q, accessGroups);
737 return true;
738 },^bool(sqlite3_stmt * stmt, int col) {
739 return sqlBindWhereClause(stmt, q, accessGroups, &col, error);
740 }, ^(SecDbItemRef item, bool *stop) {
741 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
742 const SecDbAttr *sha1attr = SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, NULL);
743 CFDataRef storedSHA1 = CFRetainSafe(SecDbItemGetValue(item, sha1attr, NULL));
744 bool item_is_sync = SecDbItemIsSyncable(item);
745 SecDbItemSetValue(item, sha1attr, storedSHA1, NULL);
746 CFReleaseSafe(storedSHA1);
747 bool makeTombstone = q->q_use_tomb ? CFBooleanGetValue(q->q_use_tomb) : (item_is_sync && !SecDbItemIsTombstone(item));
748 item->cryptoOp = kSecKsDelete;
749 ok = SecDbItemDelete(item, dbt, makeTombstone, error);
750 if (ok) {
751 q->q_changed = true;
752 if (item_is_sync)
753 q->q_sync_changed = true;
754 }
755 });
756 if (ok && !q->q_changed) {
757 ok = SecError(errSecItemNotFound, error, CFSTR("Delete failed to delete anything"));
758 }
759 return ok;
760 }
761
762 /* Return true iff the item in question should not be backed up, nor restored,
763 but when restoring a backup the original version of the item should be
764 added back to the keychain again after the restore completes. */
765 static bool SecItemIsSystemBound(CFDictionaryRef item, const SecDbClass *class) {
766 CFStringRef agrp = CFDictionaryGetValue(item, kSecAttrAccessGroup);
767 if (!isString(agrp))
768 return false;
769
770 if (CFEqualSafe(agrp, kSOSInternalAccessGroup)) {
771 secdebug("backup", "found sysbound item: %@", item);
772 return true;
773 }
774
775 if (CFEqual(agrp, CFSTR("lockdown-identities"))) {
776 secdebug("backup", "found sys_bound item: %@", item);
777 return true;
778 }
779
780 if (CFEqual(agrp, CFSTR("apple")) && class == &genp_class) {
781 CFStringRef service = CFDictionaryGetValue(item, kSecAttrService);
782 CFStringRef account = CFDictionaryGetValue(item, kSecAttrAccount);
783 if (isString(service) && isString(account) &&
784 CFEqual(service, CFSTR("com.apple.managedconfiguration")) &&
785 (CFEqual(account, CFSTR("Public")) ||
786 CFEqual(account, CFSTR("Private")))) {
787 secdebug("backup", "found sys_bound item: %@", item);
788 return true;
789 }
790 }
791 secdebug("backup", "found non sys_bound item: %@", item);
792 return false;
793 }
794
795 /* Delete all items from the current keychain. If this is not an in
796 place upgrade we don't delete items in the 'lockdown-identities'
797 access group, this ensures that an import or restore of a backup
798 will never overwrite an existing activation record. */
799 static bool SecServerDeleteAll(SecDbConnectionRef dbt, CFErrorRef *error) {
800 return kc_transaction(dbt, error, ^{
801 bool ok = (SecDbExec(dbt, CFSTR("DELETE from genp;"), error) &&
802 SecDbExec(dbt, CFSTR("DELETE from inet;"), error) &&
803 SecDbExec(dbt, CFSTR("DELETE from cert;"), error) &&
804 SecDbExec(dbt, CFSTR("DELETE from keys;"), error));
805 return ok;
806 });
807 }
808
809 struct s3dl_export_row_ctx {
810 struct s3dl_query_ctx qc;
811 keybag_handle_t dest_keybag;
812 enum SecItemFilter filter;
813 };
814
815 static void s3dl_export_row(sqlite3_stmt *stmt, void *context) {
816 struct s3dl_export_row_ctx *c = context;
817 Query *q = c->qc.q;
818 SecAccessControlRef access_control = NULL;
819 CFErrorRef localError = NULL;
820
821 sqlite_int64 rowid = sqlite3_column_int64(stmt, 0);
822 CFMutableDictionaryRef item;
823 if (s3dl_item_from_col(stmt, q, 1, c->qc.accessGroups, &item, &access_control, &localError)) {
824 if (item) {
825 /* Only export sysbound items is do_sys_bound is true, only export non sysbound items otherwise. */
826 bool do_sys_bound = c->filter == kSecSysBoundItemFilter;
827 if (c->filter == kSecNoItemFilter ||
828 SecItemIsSystemBound(item, q->q_class) == do_sys_bound) {
829 /* Re-encode the item. */
830 secdebug("item", "export rowid %llu item: %@", rowid, item);
831 /* The code below could be moved into handle_row. */
832 CFDataRef pref = _SecItemMakePersistentRef(q->q_class->name, rowid);
833 if (pref) {
834 if (c->dest_keybag != KEYBAG_NONE) {
835 CFMutableDictionaryRef auth_attribs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
836 SecDbForEachAttrWithMask(q->q_class, desc, kSecDbInAuthenticatedDataFlag) {
837 CFTypeRef value = CFDictionaryGetValue(item, desc->name);
838 if(value) {
839 CFDictionaryAddValue(auth_attribs, desc->name, value);
840 CFDictionaryRemoveValue(item, desc->name);
841 }
842 }
843
844 /* Encode and encrypt the item to the specified keybag. */
845 CFDataRef edata = NULL;
846 bool encrypted = ks_encrypt_data(c->dest_keybag, access_control, &q->q_use_cred_handle, item, auth_attribs, &edata, &q->q_error);
847 CFDictionaryRemoveAllValues(item);
848 CFRelease(auth_attribs);
849 if (encrypted) {
850 CFDictionarySetValue(item, kSecValueData, edata);
851 CFReleaseSafe(edata);
852 } else {
853 seccritical("ks_encrypt_data %@,rowid=%" PRId64 ": failed: %@", q->q_class->name, rowid, q->q_error);
854 CFReleaseNull(q->q_error);
855 }
856 }
857 if (CFDictionaryGetCount(item)) {
858 CFDictionarySetValue(item, kSecValuePersistentRef, pref);
859 CFArrayAppendValue((CFMutableArrayRef)c->qc.result, item);
860 c->qc.found++;
861 }
862 CFReleaseSafe(pref);
863 }
864 }
865 CFRelease(item);
866 }
867 } else {
868 /* This happens a lot when trying to migrate keychain before first unlock, so only a notice */
869 /* If the error is "corrupted item" then we just ignore it, otherwise we save it in the query */
870 secnotice("item","Could not export item for rowid %llu: %@", rowid, localError);
871 if(SecErrorGetOSStatus(localError)==errSecDecode) {
872 CFReleaseNull(localError);
873 } else {
874 CFReleaseSafe(q->q_error);
875 q->q_error=localError;
876 }
877 }
878 CFReleaseSafe(access_control);
879 }
880
881 CF_RETURNS_RETAINED CFDictionaryRef SecServerExportKeychainPlist(SecDbConnectionRef dbt,
882 keybag_handle_t src_keybag, keybag_handle_t dest_keybag,
883 enum SecItemFilter filter, CFErrorRef *error) {
884 CFMutableDictionaryRef keychain;
885 keychain = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
886 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
887 if (!keychain) {
888 if (error && !*error)
889 SecError(errSecAllocate, error, CFSTR("Can't create keychain dictionary"));
890 goto errOut;
891 }
892 unsigned class_ix;
893 Query q = { .q_keybag = src_keybag };
894 q.q_return_type = kSecReturnDataMask | kSecReturnAttributesMask | \
895 kSecReturnPersistentRefMask;
896 q.q_limit = kSecMatchUnlimited;
897
898 /* Enable interactive means that auth-protected items will be silently ignored
899 (and their ACLs copied into query, which we will simply ignore). */
900 query_enable_interactive(&q);
901
902 /* Get rid of this duplicate. */
903 const SecDbClass *SecDbClasses[] = {
904 &genp_class,
905 &inet_class,
906 &cert_class,
907 &keys_class
908 };
909
910 for (class_ix = 0; class_ix < array_size(SecDbClasses);
911 ++class_ix) {
912 q.q_class = SecDbClasses[class_ix];
913 struct s3dl_export_row_ctx ctx = {
914 .qc = { .q = &q, .dbt = dbt },
915 .dest_keybag = dest_keybag, .filter = filter,
916 };
917
918 secnotice("item", "exporting class '%@'", q.q_class->name);
919
920 CFErrorRef localError = NULL;
921 if (s3dl_query(s3dl_export_row, &ctx, &localError)) {
922 if (CFArrayGetCount(ctx.qc.result))
923 CFDictionaryAddValue(keychain, q.q_class->name, ctx.qc.result);
924
925 } else {
926 OSStatus status = (OSStatus)CFErrorGetCode(localError);
927 if (status == errSecItemNotFound) {
928 CFRelease(localError);
929 } else {
930 secerror("Export failed: %@", localError);
931 if (error) {
932 CFReleaseSafe(*error);
933 *error = localError;
934 } else {
935 CFRelease(localError);
936 }
937 CFReleaseNull(keychain);
938 CFReleaseNull(ctx.qc.result);
939 break;
940 }
941 }
942 CFReleaseNull(ctx.qc.result);
943 }
944
945 /* Release all ignored ACLs of auth-protected items. */
946 CFReleaseNull(q.q_required_access_controls);
947 errOut:
948 return keychain;
949 }
950
951 struct SecServerImportClassState {
952 SecDbConnectionRef dbt;
953 CFErrorRef error;
954 keybag_handle_t src_keybag;
955 keybag_handle_t dest_keybag;
956 enum SecItemFilter filter;
957 };
958
959 struct SecServerImportItemState {
960 const SecDbClass *class;
961 struct SecServerImportClassState *s;
962 };
963
964 static void SecServerImportItem(const void *value, void *context) {
965 struct SecServerImportItemState *state =
966 (struct SecServerImportItemState *)context;
967 if (state->s->error)
968 return;
969 if (!isDictionary(value)) {
970 SecError(errSecParam, &state->s->error, CFSTR("value %@ is not a dictionary"), value);
971 return;
972 }
973
974 CFDictionaryRef dict = (CFDictionaryRef)value;
975
976 secdebug("item", "Import Item : %@", dict);
977
978 /* We don't filter non sys_bound items during import since we know we
979 will never have any in this case, we use the kSecSysBoundItemFilter
980 to indicate that we don't preserve rowid's during import instead. */
981 if (state->s->filter == kSecBackupableItemFilter &&
982 SecItemIsSystemBound(dict, state->class))
983 return;
984
985 SecDbItemRef item;
986
987 /* This is sligthly confusing:
988 - During upgrade all items are exported with KEYBAG_NONE.
989 - During restore from backup, existing sys_bound items are exported with KEYBAG_NONE, and are exported as dictionary of attributes.
990 - Item in the actual backup are export with a real keybag, and are exported as encrypted v_Data and v_PersistentRef
991 */
992 if (state->s->src_keybag == KEYBAG_NONE) {
993 item = SecDbItemCreateWithAttributes(kCFAllocatorDefault, state->class, dict, state->s->dest_keybag, &state->s->error);
994 } else {
995 item = SecDbItemCreateWithBackupDictionary(kCFAllocatorDefault, state->class, dict, state->s->src_keybag, state->s->dest_keybag, &state->s->error);
996 }
997
998 if (item) {
999 if(state->s->filter != kSecSysBoundItemFilter) {
1000 SecDbItemExtractRowIdFromBackupDictionary(item, dict, &state->s->error);
1001 }
1002 SecDbItemInferSyncable(item, &state->s->error);
1003 SecDbItemInsert(item, state->s->dbt, NULL, &state->s->error);
1004 }
1005
1006 /* Reset error if we had one, since we just skip the current item
1007 and continue importing what we can. */
1008 if (state->s->error) {
1009 secwarning("Failed to import an item (%@) of class '%@': %@ - ignoring error.",
1010 item, state->class->name, state->s->error);
1011 CFReleaseNull(state->s->error);
1012 }
1013
1014 CFReleaseSafe(item);
1015 }
1016
1017 static void SecServerImportClass(const void *key, const void *value,
1018 void *context) {
1019 struct SecServerImportClassState *state =
1020 (struct SecServerImportClassState *)context;
1021 if (state->error)
1022 return;
1023 if (!isString(key)) {
1024 SecError(errSecParam, &state->error, CFSTR("class name %@ is not a string"), key);
1025 return;
1026 }
1027 const SecDbClass *class = kc_class_with_name(key);
1028 if (!class || class == &identity_class) {
1029 SecError(errSecParam, &state->error, CFSTR("attempt to import an identity"));
1030 return;
1031 }
1032 struct SecServerImportItemState item_state = {
1033 .class = class, .s = state
1034 };
1035 if (isArray(value)) {
1036 CFArrayRef items = (CFArrayRef)value;
1037 CFArrayApplyFunction(items, CFRangeMake(0, CFArrayGetCount(items)),
1038 SecServerImportItem, &item_state);
1039 } else {
1040 CFDictionaryRef item = (CFDictionaryRef)value;
1041 SecServerImportItem(item, &item_state);
1042 }
1043 }
1044
1045 bool SecServerImportKeychainInPlist(SecDbConnectionRef dbt,
1046 keybag_handle_t src_keybag, keybag_handle_t dest_keybag,
1047 CFDictionaryRef keychain, enum SecItemFilter filter, CFErrorRef *error) {
1048 bool ok = true;
1049
1050 CFDictionaryRef sys_bound = NULL;
1051 if (filter == kSecBackupableItemFilter) {
1052 /* Grab a copy of all the items for which SecItemIsSystemBound()
1053 returns true. */
1054 require(sys_bound = SecServerExportKeychainPlist(dbt, KEYBAG_DEVICE,
1055 KEYBAG_NONE, kSecSysBoundItemFilter,
1056 error), errOut);
1057 }
1058
1059 /* Delete everything in the keychain. */
1060 require(ok = SecServerDeleteAll(dbt, error), errOut);
1061
1062 struct SecServerImportClassState state = {
1063 .dbt = dbt,
1064 .src_keybag = src_keybag,
1065 .dest_keybag = dest_keybag,
1066 .filter = filter,
1067 };
1068 /* Import the provided items, preserving rowids. */
1069 CFDictionaryApplyFunction(keychain, SecServerImportClass, &state);
1070
1071 if (sys_bound) {
1072 state.src_keybag = KEYBAG_NONE;
1073 /* Import the items we preserved with random rowids. */
1074 state.filter = kSecSysBoundItemFilter;
1075 CFDictionaryApplyFunction(sys_bound, SecServerImportClass, &state);
1076 }
1077 if (state.error) {
1078 if (error) {
1079 CFReleaseSafe(*error);
1080 *error = state.error;
1081 } else {
1082 CFRelease(state.error);
1083 }
1084 ok = false;
1085 }
1086
1087 errOut:
1088 CFReleaseSafe(sys_bound);
1089
1090 return ok;
1091 }
1092
1093 #pragma mark - key rolling support
1094 #if USE_KEYSTORE
1095
1096 struct check_generation_ctx {
1097 struct s3dl_query_ctx query_ctx;
1098 uint32_t current_generation;
1099 };
1100
1101 static void check_generation(sqlite3_stmt *stmt, void *context) {
1102 struct check_generation_ctx *c = context;
1103 CFDataRef blob = NULL;
1104 size_t blobLen = 0;
1105 const uint8_t *cursor = NULL;
1106 uint32_t version;
1107 keyclass_t keyclass;
1108 uint32_t current_generation = c->current_generation;
1109
1110 require(blob = s3dl_copy_data_from_col(stmt, 1, &c->query_ctx.q->q_error), out);
1111 blobLen = CFDataGetLength(blob);
1112 cursor = CFDataGetBytePtr(blob);
1113
1114 /* Check for underflow, ensuring we have at least one full AES block left. */
1115 if (blobLen < sizeof(version) + sizeof(keyclass)) {
1116 SecError(errSecDecode, &c->query_ctx.q->q_error, CFSTR("check_generation: Check for underflow"));
1117 goto out;
1118 }
1119
1120 version = *((uint32_t *)cursor);
1121 cursor += sizeof(version);
1122
1123 (void) version; // TODO: do something with the version number.
1124
1125 keyclass = *((keyclass_t *)cursor);
1126
1127 // TODO: export get_key_gen macro
1128 if (((keyclass & ~key_class_last) == 0) != (current_generation == 0)) {
1129 c->query_ctx.found++;
1130 }
1131
1132 CFReleaseSafe(blob);
1133 return;
1134
1135 out:
1136 c->query_ctx.found++;
1137 CFReleaseSafe(blob);
1138 }
1139
1140 bool s3dl_dbt_keys_current(SecDbConnectionRef dbt, uint32_t current_generation, CFErrorRef *error) {
1141 CFErrorRef localError = NULL;
1142 struct check_generation_ctx ctx = { .query_ctx = { .dbt = dbt }, .current_generation = current_generation };
1143
1144 const SecDbClass *classes[] = {
1145 &genp_class,
1146 &inet_class,
1147 &keys_class,
1148 &cert_class,
1149 };
1150
1151 for (size_t class_ix = 0; class_ix < array_size(classes); ++class_ix) {
1152 Query *q = query_create(classes[class_ix], NULL, &localError);
1153 if (!q)
1154 return false;
1155
1156 ctx.query_ctx.q = q;
1157 q->q_limit = kSecMatchUnlimited;
1158
1159 bool ok = s3dl_query(check_generation, &ctx, &localError);
1160 query_destroy(q, NULL);
1161 CFReleaseNull(ctx.query_ctx.result);
1162
1163 if (!ok && localError && (CFErrorGetCode(localError) == errSecItemNotFound)) {
1164 CFReleaseNull(localError);
1165 continue;
1166 }
1167 secerror("Class %@ not up to date", classes[class_ix]->name);
1168 return false;
1169 }
1170 return true;
1171 }
1172
1173 bool s3dl_dbt_update_keys(SecDbConnectionRef dbt, CFErrorRef *error) {
1174 return SecDbTransaction(dbt, kSecDbExclusiveTransactionType, error, ^(bool *commit) {
1175 __block bool ok = false;
1176 uint32_t keystore_generation_status;
1177
1178 /* can we migrate to new class keys right now? */
1179 if (!aks_generation(KEYBAG_DEVICE, generation_noop, &keystore_generation_status) &&
1180 (keystore_generation_status & generation_change_in_progress)) {
1181
1182 /* take a lock assertion */
1183 bool operated_while_unlocked = SecAKSDoWhileUserBagLocked(error, ^{
1184 CFErrorRef localError = NULL;
1185 CFDictionaryRef backup = SecServerExportKeychainPlist(dbt,
1186 KEYBAG_DEVICE, KEYBAG_NONE, kSecNoItemFilter, &localError);
1187 if (backup) {
1188 if (localError) {
1189 secerror("Ignoring export error: %@ during roll export", localError);
1190 CFReleaseNull(localError);
1191 }
1192 ok = SecServerImportKeychainInPlist(dbt, KEYBAG_NONE,
1193 KEYBAG_DEVICE, backup, kSecNoItemFilter, &localError);
1194 if (localError) {
1195 secerror("Ignoring export error: %@ during roll export", localError);
1196 CFReleaseNull(localError);
1197 }
1198 CFRelease(backup);
1199 }
1200 });
1201 if (!operated_while_unlocked)
1202 ok = false;
1203 } else {
1204 ok = SecError(errSecBadReq, error, CFSTR("No key roll in progress."));
1205 }
1206
1207 *commit = ok;
1208 });
1209 }
1210 #endif