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