2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecItemDb.c - CoreFoundation-based constants and functions for
26 access to Security items (certificates, keys, identities, and
30 #include <securityd/SecItemDb.h>
31 #include <utilities/SecAKSWrappers.h>
33 #include <securityd/SecDbKeychainItem.h>
34 #include <securityd/SecItemSchema.h>
35 #include <securityd/SecItemServer.h>
36 #include <Security/SecAccessControlPriv.h>
37 #include <Security/SecBasePriv.h>
38 #include <Security/SecItem.h>
39 #include <Security/SecItemPriv.h>
40 #include <Security/SecItemInternal.h>
41 #include <securityd/SOSCloudCircleServer.h>
42 #include <utilities/array_size.h>
43 #include <utilities/SecIOFormat.h>
44 #include <utilities/SecCFCCWrappers.h>
45 #include <SecAccessControlPriv.h>
46 #include <uuid/uuid.h>
48 #include "keychain/ckks/CKKS.h"
50 #define kSecBackupKeybagUUIDKey CFSTR("keybag-uuid")
52 const SecDbAttr
*SecDbAttrWithKey(const SecDbClass
*c
,
55 /* Special case: identites can have all attributes of either cert
57 if (c
== identity_class()) {
58 const SecDbAttr
*desc
;
59 if (!(desc
= SecDbAttrWithKey(cert_class(), key
, 0)))
60 desc
= SecDbAttrWithKey(keys_class(), key
, error
);
65 SecDbForEachAttr(c
, a
) {
66 if (CFEqual(a
->name
, key
))
69 if (CFEqual(kSecAttrNoLegacy
, key
)) {
70 return NULL
; /* results in no ops for this attribute */
74 SecError(errSecNoSuchAttr
, error
, CFSTR("attribute %@ not found in class %@"), key
, c
->name
);
79 bool kc_transaction(SecDbConnectionRef dbt
, CFErrorRef
*error
, bool(^perform
)()) {
80 return kc_transaction_type(dbt
, kSecDbExclusiveTransactionType
, error
, perform
);
83 bool kc_transaction_type(SecDbConnectionRef dbt
, SecDbTransactionType type
, CFErrorRef
*error
, bool(^perform
)()) {
84 __block
bool ok
= true;
85 return ok
&& SecDbTransaction(dbt
, type
, error
, ^(bool *commit
) {
86 ok
= *commit
= perform();
90 static CFStringRef
SecDbGetKindSQL(SecDbAttrKind kind
) {
96 case kSecDbPrimaryKeyAttr
:
97 case kSecDbEncryptedDataAttr
:
99 case kSecDbAccessAttr
:
100 case kSecDbStringAttr
:
101 return CFSTR("TEXT");
102 case kSecDbNumberAttr
:
105 return CFSTR("INTEGER");
107 case kSecDbCreationDateAttr
:
108 case kSecDbModificationDateAttr
:
109 return CFSTR("REAL");
110 case kSecDbRowIdAttr
:
111 return CFSTR("INTEGER PRIMARY KEY AUTOINCREMENT");
112 case kSecDbAccessControlAttr
:
113 case kSecDbUTombAttr
:
114 /* This attribute does not exist in the DB. */
119 static void SecDbAppendUnique(CFMutableStringRef sql
, CFStringRef value
, bool *haveUnique
) {
122 CFStringAppend(sql
, CFSTR("UNIQUE("));
124 SecDbAppendElement(sql
, value
, haveUnique
);
127 static void SecDbAppendCreateTableWithClass(CFMutableStringRef sql
, const SecDbClass
*c
) {
128 CFStringAppendFormat(sql
, 0, CFSTR("CREATE TABLE %@("), c
->name
);
129 SecDbForEachAttrWithMask(c
,desc
,kSecDbInFlag
) {
130 CFStringAppendFormat(sql
, 0, CFSTR("%@ %@"), desc
->name
, SecDbGetKindSQL(desc
->kind
));
131 if (desc
->flags
& kSecDbNotNullFlag
)
132 CFStringAppend(sql
, CFSTR(" NOT NULL"));
133 if (desc
->flags
& kSecDbDefault0Flag
)
134 CFStringAppend(sql
, CFSTR(" DEFAULT 0"));
135 if (desc
->flags
& kSecDbDefaultEmptyFlag
)
136 CFStringAppend(sql
, CFSTR(" DEFAULT ''"));
137 CFStringAppend(sql
, CFSTR(","));
140 bool haveUnique
= false;
141 SecDbForEachAttrWithMask(c
,desc
,kSecDbPrimaryKeyFlag
| kSecDbInFlag
) {
142 SecDbAppendUnique(sql
, desc
->name
, &haveUnique
);
145 CFStringAppend(sql
, CFSTR(")"));
147 CFStringAppend(sql
, CFSTR(");"));
150 SecDbForEachAttrWithMask(c
,desc
, kSecDbIndexFlag
| kSecDbInFlag
) {
151 CFStringAppendFormat(sql
, 0, CFSTR("CREATE INDEX %@%@ ON %@(%@);"), c
->name
, desc
->name
, c
->name
, desc
->name
);
155 static void SecDbAppendDropTableWithClass(CFMutableStringRef sql
, const SecDbClass
*c
) {
156 CFStringAppendFormat(sql
, 0, CFSTR("DROP TABLE %@;"), c
->name
);
159 static CFDataRef
SecPersistentRefCreateWithItem(SecDbItemRef item
, CFErrorRef
*error
) {
160 sqlite3_int64 row_id
= SecDbItemGetRowId(item
, error
);
162 return _SecItemCreatePersistentRef(SecDbItemGetClass(item
)->name
, row_id
, item
->attributes
);
166 bool SecItemDbCreateSchema(SecDbConnectionRef dbt
, const SecDbSchema
*schema
, CFArrayRef classIndexesForNewTables
, bool includeVersion
, CFErrorRef
*error
)
168 __block
bool ok
= true;
169 CFMutableStringRef sql
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
171 if (classIndexesForNewTables
) {
172 CFArrayForEach(classIndexesForNewTables
, ^(const void* index
) {
173 const SecDbClass
* class = schema
->classes
[(int)index
];
174 SecDbAppendCreateTableWithClass(sql
, class);
178 for (const SecDbClass
* const *pclass
= schema
->classes
; *pclass
; ++pclass
) {
179 SecDbAppendCreateTableWithClass(sql
, *pclass
);
183 if (includeVersion
) {
184 CFStringAppendFormat(sql
, NULL
, CFSTR("INSERT INTO tversion(version,minor) VALUES(%d, %d);"),
185 schema
->majorVersion
, schema
->minorVersion
);
187 CFStringPerformWithCString(sql
, ^(const char *sql_string
) {
188 ok
= SecDbErrorWithDb(sqlite3_exec(SecDbHandle(dbt
), sql_string
, NULL
, NULL
, NULL
),
189 SecDbHandle(dbt
), error
, CFSTR("sqlite3_exec: %s"), sql_string
);
195 bool SecItemDbDeleteSchema(SecDbConnectionRef dbt
, const SecDbSchema
*schema
, CFErrorRef
*error
)
197 __block
bool ok
= true;
198 CFMutableStringRef sql
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
199 for (const SecDbClass
* const *pclass
= schema
->classes
; *pclass
; ++pclass
) {
200 SecDbAppendDropTableWithClass(sql
, *pclass
);
202 CFStringPerformWithCString(sql
, ^(const char *sql_string
) {
203 ok
= SecDbErrorWithDb(sqlite3_exec(SecDbHandle(dbt
), sql_string
, NULL
, NULL
, NULL
),
204 SecDbHandle(dbt
), error
, CFSTR("sqlite3_exec: %s"), sql_string
);
210 CFTypeRef
SecDbItemCopyResult(SecDbItemRef item
, ReturnTypeMask return_type
, CFErrorRef
*error
) {
213 if (return_type
== 0) {
214 /* Caller isn't interested in any results at all. */
216 } else if (return_type
== kSecReturnDataMask
) {
217 a_result
= SecDbItemGetCachedValueWithName(item
, kSecValueData
);
219 CFRetainSafe(a_result
);
221 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
223 } else if (return_type
== kSecReturnPersistentRefMask
) {
224 a_result
= SecPersistentRefCreateWithItem(item
, error
);
226 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableForCFTypes(CFGetAllocator(item
));
227 /* We need to return more than one value. */
228 if (return_type
& kSecReturnRefMask
) {
229 CFDictionarySetValue(dict
, kSecClass
, SecDbItemGetClass(item
)->name
);
231 CFOptionFlags mask
= (((return_type
& kSecReturnDataMask
|| return_type
& kSecReturnRefMask
) ? kSecDbReturnDataFlag
: 0) |
232 ((return_type
& kSecReturnAttributesMask
|| return_type
& kSecReturnRefMask
) ? kSecDbReturnAttrFlag
: 0));
233 SecDbForEachAttr(SecDbItemGetClass(item
), desc
) {
234 if ((desc
->flags
& mask
) != 0) {
235 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
236 if (value
&& !CFEqual(kCFNull
, value
)) {
237 CFDictionarySetValue(dict
, desc
->name
, value
);
238 } else if (value
== NULL
) {
244 CFDictionaryRemoveValue(dict
, kSecAttrUUID
);
246 if (return_type
& kSecReturnPersistentRefMask
) {
247 CFDataRef pref
= SecPersistentRefCreateWithItem(item
, error
);
248 CFDictionarySetValue(dict
, kSecValuePersistentRef
, pref
);
258 /* AUDIT[securityd](done):
259 attributes (ok) is a caller provided dictionary, only its cf type has
263 s3dl_query_add(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
, CFErrorRef
*error
)
265 if (query_match_count(q
) != 0)
266 return errSecItemMatchUnsupported
;
268 /* Add requires a class to be specified unless we are adding a ref. */
269 if (q
->q_use_item_list
)
270 return errSecUseItemListUnsupported
;
272 /* Actual work here. */
273 SecDbItemRef item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, q
->q_class
, q
->q_item
, KEYBAG_DEVICE
, error
);
276 if (SecDbItemIsTombstone(item
))
277 SecDbItemSetValue(item
, &v7utomb
, q
->q_use_tomb
? q
->q_use_tomb
: kCFBooleanTrue
, NULL
);
281 ok
= SecDbItemSetValueWithName(item
, CFSTR("v_Data"), q
->q_data
, error
);
283 ok
= SecDbItemSetRowId(item
, q
->q_row_id
, error
);
285 ok
= SecDbItemSetValueWithName(item
, CFSTR("musr"), q
->q_musrView
, error
);
286 SecDbItemSetCredHandle(item
, q
->q_use_cred_handle
);
289 if(SecCKKSIsEnabled() && !SecCKKSTestDisableAutomaticUUID()) {
290 s3dl_item_make_new_uuid(item
, q
->q_uuid_from_primary_key
, error
);
292 if(q
->q_add_sync_callback
) {
293 CFTypeRef uuid
= SecDbItemGetValue(item
, &v10itemuuid
, error
);
295 CKKSRegisterSyncStatusCallback(uuid
, q
->q_add_sync_callback
);
297 secerror("Couldn't fetch UUID from item; can't call callback");
304 ok
= SecDbItemInsert(item
, dbt
, error
);
307 if (result
&& q
->q_return_type
) {
308 *result
= SecDbItemCopyResult(item
, q
->q_return_type
, error
);
311 if (!ok
&& error
&& *error
) {
312 if (CFEqual(CFErrorGetDomain(*error
), kSecDbErrorDomain
) && CFErrorGetCode(*error
) == SQLITE_CONSTRAINT
) {
313 CFReleaseNull(*error
);
314 SecError(errSecDuplicateItem
, error
, CFSTR("duplicate item %@"), item
);
315 } else if (CFEqual(CFErrorGetDomain(*error
), kSecErrorDomain
) && CFErrorGetCode(*error
) == errSecDecode
) { //handle situation when item have pdmn=akpu but passcode is not set
316 CFTypeRef value
= SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbAccessAttr
, error
), error
);
317 if (value
&& CFEqual(value
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
)) {
318 CFReleaseNull(*error
);
319 SecError(errSecAuthFailed
, error
, CFSTR("authentication failed"));
326 if (SecDbItemIsSyncable(item
))
327 q
->q_sync_changed
= true;
330 secdebug("dbitem", "inserting item %@%s%@", item
, ok
? "" : "failed: ", ok
|| error
== NULL
? (CFErrorRef
)CFSTR("") : *error
);
337 bool s3dl_item_make_new_uuid(SecDbItemRef item
, bool uuid_from_primary_key
, CFErrorRef
* error
) {
342 // Set the item UUID.
343 CFUUIDRef uuid
= NULL
;
344 // Were we asked to make the UUID static?
345 if (uuid_from_primary_key
) {
346 // This UUID isn't used in any security mechanism, so we can
347 // just use the first bits of the SHA256 hash.
348 CFDataRef pkhash
= SecDbKeychainItemCopySHA256PrimaryKey(item
, error
);
349 if(CFDataGetLength(pkhash
) >= 16) {
351 CFRange range
= CFRangeMake(0, 16);
352 CFDataGetBytes(pkhash
, range
, uuidBytes
);
354 uuid
= CFUUIDCreateWithBytes(NULL
,
372 CFReleaseNull(pkhash
);
375 uuid
= CFUUIDCreate(NULL
);
377 SecDbItemSetValueWithName(item
, kSecAttrUUID
, uuid
, error
);
382 typedef void (*s3dl_handle_row
)(sqlite3_stmt
*stmt
, void *context
);
385 s3dl_copy_data_from_col(sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
386 return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt
, col
),
387 sqlite3_column_bytes(stmt
, col
),
392 s3dl_item_from_col(sqlite3_stmt
*stmt
, Query
*q
, int col
, CFArrayRef accessGroups
,
393 CFMutableDictionaryRef
*item
, SecAccessControlRef
*access_control
, CFErrorRef
*error
) {
394 CFDataRef edata
= NULL
;
396 require(edata
= s3dl_copy_data_from_col(stmt
, col
, error
), out
);
397 ok
= s3dl_item_from_data(edata
, q
, accessGroups
, item
, access_control
, error
);
400 CFReleaseSafe(edata
);
404 struct s3dl_query_ctx
{
406 CFArrayRef accessGroups
;
407 SecDbConnectionRef dbt
;
412 /* Return whatever the caller requested based on the value of q->q_return_type.
413 keys and values must be 3 larger than attr_count in size to accomadate the
414 optional data, class and persistent ref results. This is so we can use
415 the CFDictionaryCreate() api here rather than appending to a
416 mutable dictionary. */
417 static CF_RETURNS_RETAINED CFTypeRef
418 handle_result(Query
*q
,
419 CFMutableDictionaryRef item
,
424 data
= CFDictionaryGetValue(item
, kSecValueData
);
425 if (q
->q_return_type
== 0) {
426 /* Caller isn't interested in any results at all. */
428 } else if (q
->q_return_type
== kSecReturnDataMask
) {
433 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
435 } else if (q
->q_return_type
== kSecReturnPersistentRefMask
) {
436 a_result
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, item
);
438 /* We need to return more than one value. */
439 if (q
->q_return_type
& kSecReturnRefMask
) {
440 CFDictionarySetValue(item
, kSecClass
, q
->q_class
->name
);
441 } else if ((q
->q_return_type
& kSecReturnAttributesMask
)) {
442 if (!(q
->q_return_type
& kSecReturnDataMask
)) {
443 CFDictionaryRemoveValue(item
, kSecValueData
);
446 // Add any attributes which are supposed to be returned, are not present in the decrypted blob,
447 // and have a way to generate themselves.
448 SecDbItemRef itemRef
= NULL
;
449 SecDbForEachAttrWithMask(q
->q_class
, attr
, kSecDbReturnAttrFlag
) {
450 if(!CFDictionaryGetValue(item
, attr
->name
) && attr
->copyValue
) {
451 CFErrorRef cferror
= NULL
;
453 itemRef
= SecDbItemCreateWithAttributes(NULL
, q
->q_class
, item
, KEYBAG_DEVICE
, &cferror
);
455 if(!cferror
&& itemRef
) {
456 CFTypeRef attrValue
= attr
->copyValue(itemRef
, attr
, &cferror
);
457 if(!cferror
&& attrValue
) {
458 CFDictionarySetValue(item
, attr
->name
, attrValue
);
460 CFReleaseNull(attrValue
);
462 CFReleaseNull(cferror
);
465 CFReleaseNull(itemRef
);
467 CFDictionaryRemoveValue(item
, kSecAttrUUID
);
470 CFDictionaryRemoveAllValues(item
);
471 if ((q
->q_return_type
& kSecReturnDataMask
) && data
) {
472 CFDictionarySetValue(item
, kSecValueData
, data
);
476 if (q
->q_return_type
& kSecReturnPersistentRefMask
) {
477 CFDataRef pref
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, item
);
478 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
489 static void s3dl_merge_into_dict(const void *key
, const void *value
, void *context
) {
490 CFDictionarySetValue(context
, key
, value
);
493 static bool checkTokenObjectID(CFDataRef token_object_id
, CFDataRef value_data
) {
494 bool equalOID
= false;
495 CFDictionaryRef itemValue
= SecTokenItemValueCopy(value_data
, NULL
);
496 require_quiet(itemValue
, out
);
497 CFDataRef oID
= CFDictionaryGetValue(itemValue
, kSecTokenValueObjectIDKey
);
498 equalOID
= CFEqualSafe(token_object_id
, oID
);
499 CFRelease(itemValue
);
504 static void s3dl_query_row(sqlite3_stmt
*stmt
, void *context
) {
505 struct s3dl_query_ctx
*c
= context
;
508 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
509 CFMutableDictionaryRef item
= NULL
;
510 bool ok
= s3dl_item_from_col(stmt
, q
, 1, c
->accessGroups
, &item
, NULL
, &q
->q_error
);
512 OSStatus status
= SecErrorGetOSStatus(q
->q_error
);
513 // errSecDecode means the item is corrupted, stash it for delete.
514 if (status
== errSecDecode
) {
515 secwarning("ignoring corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, rowid
, q
->q_error
);
517 CFDataRef edata
= s3dl_copy_data_from_col(stmt
, 1, NULL
);
518 CFMutableStringRef edatastring
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
520 CFStringAppendEncryptedData(edatastring
, edata
);
521 secnotice("item", "corrupted edata=%@", edatastring
);
523 CFReleaseSafe(edata
);
524 CFReleaseSafe(edatastring
);
526 CFReleaseNull(q
->q_error
);
527 } else if (status
== errSecAuthNeeded
) {
528 secwarning("Authentication is needed for %@,rowid=%" PRId64
" (%" PRIdOSStatus
"): %@", q
->q_class
->name
, rowid
, status
, q
->q_error
);
530 secerror("decode %@,rowid=%" PRId64
" failed (%" PRIdOSStatus
"): %@", q
->q_class
->name
, rowid
, status
, q
->q_error
);
532 // q->q_error will be released appropriately by a call to query_error
539 if (q
->q_token_object_id
!= NULL
&& !checkTokenObjectID(q
->q_token_object_id
, CFDictionaryGetValue(item
, kSecValueData
)))
542 if (q
->q_class
== identity_class()) {
543 // TODO: Use col 2 for key rowid and use both rowids in persistent ref.
545 CFMutableDictionaryRef key
;
546 /* TODO : if there is a errSecDecode error here, we should cleanup */
547 if (!s3dl_item_from_col(stmt
, q
, 3, c
->accessGroups
, &key
, NULL
, &q
->q_error
) || !key
)
550 CFDataRef certData
= CFDictionaryGetValue(item
, kSecValueData
);
552 CFDictionarySetValue(key
, kSecAttrIdentityCertificateData
, certData
);
553 CFDictionaryRemoveValue(item
, kSecValueData
);
556 CFDataRef certTokenID
= CFDictionaryGetValue(item
, kSecAttrTokenID
);
558 CFDictionarySetValue(key
, kSecAttrIdentityCertificateTokenID
, certTokenID
);
559 CFDictionaryRemoveValue(item
, kSecAttrTokenID
);
561 CFDictionaryApplyFunction(item
, s3dl_merge_into_dict
, key
);
566 if (!match_item(c
->dbt
, q
, c
->accessGroups
, item
))
569 CFTypeRef a_result
= handle_result(q
, item
, rowid
);
571 if (a_result
== kCFNull
) {
572 /* Caller wasn't interested in a result, but we still
573 count this row as found. */
574 CFRelease(a_result
); // Help shut up clang
575 } else if (q
->q_limit
== 1) {
576 c
->result
= a_result
;
578 CFArrayAppendValue((CFMutableArrayRef
)c
->result
, a_result
);
589 SecDbAppendWhereROWID(CFMutableStringRef sql
,
590 CFStringRef col
, sqlite_int64 row_id
,
593 SecDbAppendWhereOrAnd(sql
, needWhere
);
594 CFStringAppendFormat(sql
, NULL
, CFSTR("%@=%lld"), col
, row_id
);
599 SecDbAppendWhereAttrs(CFMutableStringRef sql
, const Query
*q
, bool *needWhere
) {
600 CFIndex ix
, attr_count
= query_attr_count(q
);
601 for (ix
= 0; ix
< attr_count
; ++ix
) {
602 SecDbAppendWhereOrAndEquals(sql
, query_attr_at(q
, ix
).key
, needWhere
);
607 SecDbAppendWhereAccessGroups(CFMutableStringRef sql
,
609 CFArrayRef accessGroups
,
611 CFIndex ix
, ag_count
;
612 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
))) {
616 SecDbAppendWhereOrAnd(sql
, needWhere
);
617 CFStringAppend(sql
, col
);
618 CFStringAppend(sql
, CFSTR(" IN (?"));
619 for (ix
= 1; ix
< ag_count
; ++ix
) {
620 CFStringAppend(sql
, CFSTR(",?"));
622 CFStringAppend(sql
, CFSTR(")"));
626 isQueryOverAllMUSRViews(CFTypeRef musrView
)
628 return SecMUSRIsViewAllViews(musrView
);
632 isQueryOverSingleUserView(CFTypeRef musrView
)
634 return isNull(musrView
);
639 isQueryOverBothUserAndSystem(CFTypeRef musrView
, uid_t
*uid
)
641 return SecMUSRGetBothUserAndSystemUUID(musrView
, uid
);
646 SecDbAppendWhereMusr(CFMutableStringRef sql
,
650 SecDbAppendWhereOrAnd(sql
, needWhere
);
653 if (isQueryOverBothUserAndSystem(q
->q_musrView
, NULL
)) {
654 CFStringAppend(sql
, CFSTR("(musr = ? OR musr = ?)"));
657 if (isQueryOverAllMUSRViews(q
->q_musrView
)) {
658 /* query over all items, regardless of view */
659 } else if (isQueryOverSingleUserView(q
->q_musrView
)) {
660 CFStringAppend(sql
, CFSTR("musr = ?"));
662 CFStringAppend(sql
, CFSTR("musr = ?"));
666 static void SecDbAppendWhereClause(CFMutableStringRef sql
, const Query
*q
,
667 CFArrayRef accessGroups
) {
668 bool needWhere
= true;
669 SecDbAppendWhereROWID(sql
, CFSTR("ROWID"), q
->q_row_id
, &needWhere
);
670 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
671 SecDbAppendWhereMusr(sql
, q
, &needWhere
);
672 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
675 static void SecDbAppendLimit(CFMutableStringRef sql
, CFIndex limit
) {
676 if (limit
!= kSecMatchUnlimited
)
677 CFStringAppendFormat(sql
, NULL
, CFSTR(" LIMIT %" PRIdCFIndex
), limit
);
680 static CFStringRef
s3dl_create_select_sql(Query
*q
, CFArrayRef accessGroups
) {
681 CFMutableStringRef sql
= CFStringCreateMutable(NULL
, 0);
682 if (q
->q_class
== identity_class()) {
683 CFStringAppendFormat(sql
, NULL
, CFSTR("SELECT crowid, %@"
685 "(SELECT cert.rowid AS crowid, cert.labl AS labl,"
686 " cert.issr AS issr, cert.slnr AS slnr, cert.skid AS skid,"
687 " keys.*,cert.data AS %@"
689 " WHERE keys.priv == 1 AND cert.pkhh == keys.klbl"),
690 kSecAttrIdentityCertificateData
, kSecAttrIdentityCertificateData
);
691 SecDbAppendWhereAccessGroups(sql
, CFSTR("cert.agrp"), accessGroups
, 0);
692 /* The next 3 SecDbAppendWhere calls are in the same order as in
693 SecDbAppendWhereClause(). This makes sqlBindWhereClause() work,
694 as long as we do an extra sqlBindAccessGroups first. */
695 SecDbAppendWhereROWID(sql
, CFSTR("crowid"), q
->q_row_id
, 0);
696 CFStringAppend(sql
, CFSTR(")"));
697 bool needWhere
= true;
698 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
699 SecDbAppendWhereMusr(sql
, q
, &needWhere
);
700 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
702 CFStringAppend(sql
, CFSTR("SELECT rowid, data FROM "));
703 CFStringAppend(sql
, q
->q_class
->name
);
704 SecDbAppendWhereClause(sql
, q
, accessGroups
);
706 //do not append limit for all queries which needs filtering
707 if (q
->q_match_issuer
== NULL
&& q
->q_match_policy
== NULL
&& q
->q_match_valid_on_date
== NULL
&& q
->q_match_trusted_only
== NULL
&& q
->q_token_object_id
== NULL
) {
708 SecDbAppendLimit(sql
, q
->q_limit
);
714 static bool sqlBindMusr(sqlite3_stmt
*stmt
, const Query
*q
, int *pParam
, CFErrorRef
*error
) {
720 if (isQueryOverBothUserAndSystem(q
->q_musrView
, &uid
)) {
721 /* network extensions are special and get to query both user and system views */
722 CFDataRef systemUUID
= SecMUSRGetSystemKeychainUUID();
723 result
= SecDbBindObject(stmt
, param
++, systemUUID
, error
);
725 CFDataRef activeUser
= SecMUSRCreateActiveUserUUID(uid
);
726 result
= SecDbBindObject(stmt
, param
++, activeUser
, error
);
727 CFReleaseNull(activeUser
);
731 if (isQueryOverAllMUSRViews(q
->q_musrView
)) {
732 /* query over all items, regardless of view */
733 } else if (isQueryOverSingleUserView(q
->q_musrView
)) {
734 CFDataRef singleUUID
= SecMUSRGetSingleUserKeychainUUID();
735 result
= SecDbBindObject(stmt
, param
++, singleUUID
, error
);
737 result
= SecDbBindObject(stmt
, param
++, q
->q_musrView
, error
);
745 static bool sqlBindAccessGroups(sqlite3_stmt
*stmt
, CFArrayRef accessGroups
,
746 int *pParam
, CFErrorRef
*error
) {
749 CFIndex ix
, count
= accessGroups
? CFArrayGetCount(accessGroups
) : 0;
750 for (ix
= 0; ix
< count
; ++ix
) {
751 result
= SecDbBindObject(stmt
, param
++,
752 CFArrayGetValueAtIndex(accessGroups
, ix
),
761 static bool sqlBindWhereClause(sqlite3_stmt
*stmt
, const Query
*q
,
762 CFArrayRef accessGroups
, int *pParam
, CFErrorRef
*error
) {
765 CFIndex ix
, attr_count
= query_attr_count(q
);
766 for (ix
= 0; ix
< attr_count
; ++ix
) {
767 result
= SecDbBindObject(stmt
, param
++, query_attr_at(q
, ix
).value
, error
);
773 result
= sqlBindMusr(stmt
, q
, ¶m
, error
);
776 /* Bind the access group to the sql. */
778 result
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
785 bool SecDbItemQuery(SecDbQueryRef query
, CFArrayRef accessGroups
, SecDbConnectionRef dbconn
, CFErrorRef
*error
,
786 void (^handle_row
)(SecDbItemRef item
, bool *stop
)) {
787 __block
bool ok
= true;
788 /* Sanity check the query. */
790 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
792 bool (^return_attr
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
793 // The attributes here must match field list hardcoded in s3dl_select_sql used below, which is
795 return attr
->kind
== kSecDbRowIdAttr
|| attr
->kind
== kSecDbEncryptedDataAttr
;
798 CFStringRef sql
= s3dl_create_select_sql(query
, accessGroups
);
801 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
802 /* Bind the values being searched for to the SELECT statement. */
804 if (query
->q_class
== identity_class()) {
805 /* Bind the access groups to cert.agrp. */
806 ok
&= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
809 ok
&= sqlBindWhereClause(stmt
, query
, accessGroups
, ¶m
, error
);
811 SecDbStep(dbconn
, stmt
, error
, ^(bool *stop
) {
812 SecDbItemRef itemFromStatement
= SecDbItemCreateWithStatement(kCFAllocatorDefault
, query
->q_class
, stmt
, query
->q_keybag
, error
, return_attr
);
813 if (itemFromStatement
) {
814 CFTransferRetained(itemFromStatement
->credHandle
, query
->q_use_cred_handle
);
815 if (match_item(dbconn
, query
, accessGroups
, itemFromStatement
->attributes
))
816 handle_row(itemFromStatement
, stop
);
817 CFReleaseNull(itemFromStatement
);
819 secerror("failed to create item from stmt: %@", error
? *error
: (CFErrorRef
)"no error");
821 CFReleaseNull(*error
);
836 s3dl_query(s3dl_handle_row handle_row
,
837 void *context
, CFErrorRef
*error
)
839 struct s3dl_query_ctx
*c
= context
;
840 SecDbConnectionRef dbt
= c
->dbt
;
842 CFArrayRef accessGroups
= c
->accessGroups
;
844 /* Sanity check the query. */
846 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
848 /* Actual work here. */
849 if (q
->q_limit
== 1) {
852 c
->result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
854 CFStringRef sql
= s3dl_create_select_sql(q
, accessGroups
);
855 bool ok
= SecDbWithSQL(dbt
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
857 /* Bind the values being searched for to the SELECT statement. */
859 if (q
->q_class
== identity_class()) {
860 /* Bind the access groups to cert.agrp. */
861 sql_ok
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
864 sql_ok
= sqlBindWhereClause(stmt
, q
, accessGroups
, ¶m
, error
);
866 SecDbForEach(dbt
, stmt
, error
, ^bool (int row_index
) {
867 handle_row(stmt
, context
);
869 bool needs_auth
= q
->q_error
&& CFErrorGetCode(q
->q_error
) == errSecAuthNeeded
;
870 if (q
->q_skip_acl_items
&& needs_auth
)
871 // Skip items needing authentication if we are told to do so.
872 CFReleaseNull(q
->q_error
);
874 bool stop
= q
->q_limit
!= kSecMatchUnlimited
&& c
->found
>= q
->q_limit
;
875 stop
= stop
|| (q
->q_error
&& !needs_auth
);
884 // First get the error from the query, since errSecDuplicateItem from an
885 // update query should superceed the errSecItemNotFound below.
886 if (!query_error(q
, error
))
888 if (ok
&& c
->found
== 0) {
889 ok
= SecError(errSecItemNotFound
, error
, CFSTR("no matching items found"));
890 if (q
->q_spindump_on_failure
) {
891 __security_stackshotreport(CFSTR("ItemNotFound"), __sec_exception_code_LostInMist
);
899 s3dl_copy_matching(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
,
900 CFArrayRef accessGroups
, CFErrorRef
*error
)
902 struct s3dl_query_ctx ctx
= {
903 .q
= q
, .accessGroups
= accessGroups
, .dbt
= dbt
,
905 if (q
->q_row_id
&& query_attr_count(q
))
906 return SecError(errSecItemIllegalQuery
, error
,
907 CFSTR("attributes to query illegal; both row_id and other attributes can't be searched at the same time"));
908 if (q
->q_token_object_id
&& query_attr_count(q
) != 1)
909 return SecError(errSecItemIllegalQuery
, error
,
910 CFSTR("attributes to query illegal; both token persitent ref and other attributes can't be searched at the same time"));
912 // Only copy things that aren't tombstones unless the client explicitly asks otherwise.
913 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
914 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
915 bool ok
= s3dl_query(s3dl_query_row
, &ctx
, error
);
917 *result
= ctx
.result
;
919 CFReleaseSafe(ctx
.result
);
924 typedef void (^s3dl_item_digest_callback
)(CFDataRef persistantReference
, CFDataRef encryptedData
);
926 struct s3dl_digest_ctx
{
928 SecDbConnectionRef dbt
;
929 s3dl_item_digest_callback item_callback
;
932 static void s3dl_query_row_digest(sqlite3_stmt
*stmt
, void *context
) {
933 struct s3dl_query_ctx
*c
= context
;
936 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
937 CFDataRef edata
= s3dl_copy_data_from_col(stmt
, 1, NULL
);
938 CFDataRef persistant_reference
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, NULL
);
939 CFDataRef digest
= NULL
;
942 digest
= CFDataCopySHA256Digest(edata
, NULL
);
945 if (digest
&& persistant_reference
) {
946 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(NULL
,
947 kSecValuePersistentRef
, persistant_reference
,
948 kSecValueData
, digest
,
951 CFArrayAppendValue((CFMutableArrayRef
)c
->result
, item
);
955 secinfo("item", "rowid %lu in %@ failed to create pref/digest", (unsigned long)rowid
, q
->q_class
->name
);
957 CFReleaseNull(digest
);
958 CFReleaseNull(edata
);
959 CFReleaseNull(persistant_reference
);
964 s3dl_copy_digest(SecDbConnectionRef dbt
, Query
*q
, CFArrayRef
*result
, CFArrayRef accessGroups
, CFErrorRef
*error
)
966 struct s3dl_query_ctx ctx
= {
967 .q
= q
, .dbt
= dbt
, .accessGroups
= accessGroups
,
969 // Force to always return an array
970 q
->q_limit
= kSecMatchUnlimited
;
971 // This interface only queries live data
972 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
973 bool ok
= s3dl_query(s3dl_query_row_digest
, &ctx
, error
);
975 *result
= (CFArrayRef
)ctx
.result
;
977 CFReleaseSafe(ctx
.result
);
982 /* First remove key from q->q_pairs if it's present, then add the attribute again. */
983 static void query_set_attribute_with_desc(const SecDbAttr
*desc
, const void *value
, Query
*q
) {
984 if (CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
986 for (ix
= 0; ix
< q
->q_attr_end
; ++ix
) {
987 if (CFEqual(desc
->name
, q
->q_pairs
[ix
].key
)) {
988 CFReleaseSafe(q
->q_pairs
[ix
].value
);
990 for (; ix
< q
->q_attr_end
; ++ix
) {
991 q
->q_pairs
[ix
] = q
->q_pairs
[ix
+ 1];
993 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
998 query_add_attribute_with_desc(desc
, value
, q
);
1001 /* Update modification_date if needed. */
1002 static void query_pre_update(Query
*q
) {
1003 SecDbForEachAttr(q
->q_class
, desc
) {
1004 if (desc
->kind
== kSecDbModificationDateAttr
) {
1005 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
1006 query_set_attribute_with_desc(desc
, now
, q
);
1012 /* Make sure all attributes that are marked as not_null have a value. If
1013 force_date is false, only set mdat and cdat if they aren't already set. */
1014 void query_pre_add(Query
*q
, bool force_date
) {
1015 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
1016 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInFlag
) {
1017 if (desc
->kind
== kSecDbCreationDateAttr
||
1018 desc
->kind
== kSecDbModificationDateAttr
) {
1020 query_set_attribute_with_desc(desc
, now
, q
);
1021 } else if (!CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1022 query_add_attribute_with_desc(desc
, now
, q
);
1024 } else if ((desc
->flags
& kSecDbNotNullFlag
) &&
1025 !CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1026 CFTypeRef value
= NULL
;
1027 if (desc
->flags
& kSecDbDefault0Flag
) {
1028 if (desc
->kind
== kSecDbDateAttr
)
1029 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
1032 value
= CFNumberCreate(0, kCFNumberSInt32Type
, &vzero
);
1034 } else if (desc
->flags
& kSecDbDefaultEmptyFlag
) {
1035 if (desc
->kind
== kSecDbDataAttr
|| desc
->kind
== kSecDbUUIDAttr
)
1036 value
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
1043 /* Safe to use query_add_attribute here since the attr wasn't
1045 query_add_attribute_with_desc(desc
, value
, q
);
1053 // Return a tri state value false->never make a tombstone, true->always make a
1054 // tombstone, NULL->make a tombstone, but delete it if the tombstone itself is not currently being synced.
1055 static CFBooleanRef
s3dl_should_make_tombstone(Query
*q
, bool item_is_syncable
, SecDbItemRef item
) {
1057 return q
->q_use_tomb
;
1058 else if (item_is_syncable
&& !SecDbItemIsTombstone(item
))
1061 return kCFBooleanFalse
;
1063 /* AUDIT[securityd](done):
1064 attributesToUpdate (ok) is a caller provided dictionary,
1065 only its cf types have been checked.
1068 s3dl_query_update(SecDbConnectionRef dbt
, Query
*q
,
1069 CFDictionaryRef attributesToUpdate
, CFArrayRef accessGroups
, CFErrorRef
*error
)
1071 /* Sanity check the query. */
1072 if (query_match_count(q
) != 0)
1073 return SecError(errSecItemMatchUnsupported
, error
, CFSTR("match not supported in attributes to update"));
1075 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported in attributes to update"));
1076 if (q
->q_row_id
&& query_attr_count(q
))
1077 return SecError(errSecItemIllegalQuery
, error
, CFSTR("attributes to update illegal; both row_id and other attributes can't be updated at the same time"));
1078 if (q
->q_token_object_id
&& query_attr_count(q
) != 1)
1079 return SecError(errSecItemIllegalQuery
, error
, CFSTR("attributes to update illegal; both token persistent ref and other attributes can't be updated at the same time"));
1081 __block
bool result
= true;
1082 Query
*u
= query_create(q
->q_class
, NULL
, attributesToUpdate
, error
);
1083 if (u
== NULL
) return false;
1084 require_action_quiet(query_update_parse(u
, attributesToUpdate
, error
), errOut
, result
= false);
1085 query_pre_update(u
);
1086 result
&= SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
1087 // Make sure we only update real items, not tombstones, unless the client explicitly asks otherwise.
1088 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
1089 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
1090 result
&= SecDbItemQuery(q
, accessGroups
, dbt
, error
, ^(SecDbItemRef item
, bool *stop
) {
1091 // We always need to know the error here.
1092 CFErrorRef localError
= NULL
;
1093 if (q
->q_token_object_id
) {
1094 const SecDbAttr
*valueDataAttr
= SecDbClassAttrWithKind(item
->class, kSecDbDataAttr
, NULL
);
1095 CFDataRef valueData
= SecDbItemGetValue(item
, valueDataAttr
, NULL
);
1096 if (q
->q_token_object_id
!= NULL
&& !checkTokenObjectID(q
->q_token_object_id
, valueData
))
1099 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1100 const SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
);
1101 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, NULL
));
1102 SecDbItemRef new_item
= SecDbItemCopyWithUpdates(item
, u
->q_item
, &localError
);
1103 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
1104 CFReleaseSafe(storedSHA1
);
1105 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
1106 // We just ignore this, and treat as if item is not found.
1107 secwarning("deleting corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, SecDbItemGetRowId(item
, NULL
), localError
);
1108 CFReleaseNull(localError
);
1109 if (!SecDbItemDelete(item
, dbt
, false, &localError
)) {
1110 secerror("failed to delete corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, SecDbItemGetRowId(item
, NULL
), localError
);
1111 CFReleaseNull(localError
);
1115 if (new_item
!= NULL
&& u
->q_access_control
!= NULL
)
1116 SecDbItemSetAccessControl(new_item
, u
->q_access_control
, &localError
);
1117 result
= SecErrorPropagate(localError
, error
) && new_item
;
1119 bool item_is_sync
= SecDbItemIsSyncable(item
);
1120 result
= SecDbItemUpdate(item
, new_item
, dbt
, s3dl_should_make_tombstone(q
, item_is_sync
, item
), q
->q_uuid_from_primary_key
, error
);
1122 q
->q_changed
= true;
1123 if (item_is_sync
|| SecDbItemIsSyncable(new_item
))
1124 q
->q_sync_changed
= true;
1126 CFRelease(new_item
);
1132 if (result
&& !q
->q_changed
)
1133 result
= SecError(errSecItemNotFound
, error
, CFSTR("No items updated"));
1135 if (!query_destroy(u
, error
))
1140 static bool SecDbItemNeedAuth(SecDbItemRef item
, CFErrorRef
*error
)
1142 CFErrorRef localError
= NULL
;
1143 if (!SecDbItemEnsureDecrypted(item
, &localError
) && localError
&& CFErrorGetCode(localError
) == errSecAuthNeeded
) {
1145 *error
= localError
;
1149 CFReleaseSafe(localError
);
1154 s3dl_query_delete(SecDbConnectionRef dbt
, Query
*q
, CFArrayRef accessGroups
, CFErrorRef
*error
)
1156 __block
bool ok
= true;
1157 __block
bool needAuth
= false;
1158 // Only delete things that aren't tombstones, unless the client explicitly asks otherwise.
1159 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
1160 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
1161 ok
&= SecDbItemSelect(q
, dbt
, error
, NULL
, ^bool(const SecDbAttr
*attr
) {
1163 },^bool(CFMutableStringRef sql
, bool *needWhere
) {
1164 SecDbAppendWhereClause(sql
, q
, accessGroups
);
1166 },^bool(sqlite3_stmt
* stmt
, int col
) {
1167 return sqlBindWhereClause(stmt
, q
, accessGroups
, &col
, error
);
1168 }, ^(SecDbItemRef item
, bool *stop
) {
1169 // Check if item for token persitence ref
1170 if (q
->q_token_object_id
) {
1171 const SecDbAttr
*valueDataAttr
= SecDbClassAttrWithKind(item
->class, kSecDbDataAttr
, NULL
);
1172 CFDataRef valueData
= SecDbItemGetValue(item
, valueDataAttr
, NULL
);
1173 if (q
->q_token_object_id
!= NULL
&& !checkTokenObjectID(q
->q_token_object_id
, valueData
))
1176 // Check if item need to be authenticated by LocalAuthentication
1177 item
->cryptoOp
= kAKSKeyOpDelete
;
1178 if (SecDbItemNeedAuth(item
, error
)) {
1182 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1183 const SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
);
1184 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, NULL
));
1185 bool item_is_sync
= SecDbItemIsSyncable(item
);
1186 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
1187 CFReleaseSafe(storedSHA1
);
1188 ok
= SecDbItemDelete(item
, dbt
, s3dl_should_make_tombstone(q
, item_is_sync
, item
), error
);
1190 q
->q_changed
= true;
1192 q
->q_sync_changed
= true;
1195 if (ok
&& !q
->q_changed
&& !needAuth
) {
1196 ok
= SecError(errSecItemNotFound
, error
, CFSTR("Delete failed to delete anything"));
1198 return ok
&& !needAuth
;
1202 matchAnyString(CFStringRef needle
, CFStringRef
*haystack
)
1205 if (CFEqual(needle
, *haystack
))
1212 /* Return true iff the item in question should not be backed up, nor restored,
1213 but when restoring a backup the original version of the item should be
1214 added back to the keychain again after the restore completes. */
1215 static bool SecItemIsSystemBound(CFDictionaryRef item
, const SecDbClass
*cls
, bool multiUser
) {
1216 CFNumberRef sysb
= CFDictionaryGetValue(item
, kSecAttrSysBound
);
1217 if (isNumber(sysb
)) {
1219 if (!CFNumberGetValue(sysb
, kCFNumberSInt32Type
, &num
))
1221 if (num
== kSecSecAttrSysBoundNot
) {
1223 } else if (num
== kSecSecAttrSysBoundPreserveDuringRestore
) {
1229 CFStringRef agrp
= CFDictionaryGetValue(item
, kSecAttrAccessGroup
);
1230 if (!isString(agrp
))
1233 if (CFEqualSafe(agrp
, kSOSInternalAccessGroup
)) {
1234 secdebug("backup", "found sysbound item: %@", item
);
1238 if (CFEqual(agrp
, CFSTR("lockdown-identities"))) {
1239 secdebug("backup", "found sys_bound item: %@", item
);
1243 if (CFEqual(agrp
, CFSTR("apple")) && cls
== genp_class()) {
1244 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1245 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
1247 if (isString(service
) && isString(account
)) {
1248 static CFStringRef mcAccounts
[] = {
1254 if (CFEqual(service
, CFSTR("com.apple.managedconfiguration"))
1255 && matchAnyString(account
, mcAccounts
))
1257 secdebug("backup", "found sys_bound item: %@", item
);
1263 if (multiUser
&& CFEqual(agrp
, CFSTR("com.apple.apsd")) && cls
== genp_class()) {
1264 static CFStringRef pushServices
[] = {
1265 CFSTR("push.apple.com"),
1266 CFSTR("push.apple.com,PerAppToken.v0"),
1269 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1271 if (isString(service
) && matchAnyString(service
, pushServices
)) {
1272 secdebug("backup", "found sys_bound item: %@", item
);
1277 if (multiUser
&& CFEqual(agrp
, CFSTR("appleaccount")) && cls
== genp_class()) {
1278 static CFStringRef accountServices
[] = {
1279 CFSTR("com.apple.appleaccount.fmf.token"), /* temporary tokens while accout is being setup */
1280 CFSTR("com.apple.appleaccount.fmf.apptoken"),
1281 CFSTR("com.apple.appleaccount.fmip.siritoken"),
1282 CFSTR("com.apple.appleaccount.cloudkit.token"),
1285 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1287 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1288 secdebug("backup", "found exact sys_bound item: %@", item
);
1293 if (multiUser
&& CFEqual(agrp
, CFSTR("apple")) && cls
== genp_class()) {
1294 static CFStringRef accountServices
[] = {
1295 CFSTR("com.apple.account.AppleAccount.token"),
1296 CFSTR("com.apple.account.AppleAccount.password"),
1297 CFSTR("com.apple.account.AppleAccount.rpassword"),
1298 CFSTR("com.apple.account.idms.token"),
1299 CFSTR("com.apple.account.idms.heartbeat-token"),
1300 CFSTR("com.apple.account.idms.continuation-key"),
1301 CFSTR("com.apple.account.CloudKit.token"),
1302 CFSTR("com.apple.account.IdentityServices.password"), /* accountsd for ids */
1303 CFSTR("com.apple.account.IdentityServices.rpassword"),
1304 CFSTR("com.apple.account.IdentityServices.token"),
1305 CFSTR("BackupIDSAccountToken"),
1306 CFSTR("com.apple.ids"),
1311 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1313 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1314 secdebug("backup", "found exact sys_bound item: %@", item
);
1317 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.facetime"))) {
1318 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
1319 if (isString(account
) && CFEqual(account
, CFSTR("registrationV1"))) {
1320 secdebug("backup", "found exact sys_bound item: %@", item
);
1326 if (multiUser
&& CFEqual(agrp
, CFSTR("ichat")) && cls
== genp_class()) {
1327 static CFStringRef accountServices
[] = {
1331 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1333 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1334 secdebug("backup", "found exact sys_bound item: %@", item
);
1339 if (multiUser
&& CFEqual(agrp
, CFSTR("ichat")) && cls
== keys_class()) {
1340 static CFStringRef exactMatchingLabel
[] = {
1341 CFSTR("iMessage Encryption Key"),
1342 CFSTR("iMessage Signing Key"),
1344 CFStringRef label
= CFDictionaryGetValue(item
, kSecAttrLabel
);
1345 if (isString(label
)) {
1346 if (matchAnyString(label
, exactMatchingLabel
)) {
1347 secdebug("backup", "found exact sys_bound item: %@", item
);
1354 secdebug("backup", "found non sys_bound item: %@", item
);
1358 /* Delete all items from the current keychain. If this is not an in
1359 place upgrade we don't delete items in the 'lockdown-identities'
1360 access group, this ensures that an import or restore of a backup
1361 will never overwrite an existing activation record. */
1362 static bool SecServerDeleteAll(SecDbConnectionRef dbt
, CFErrorRef
*error
) {
1363 secwarning("SecServerDeleteAll");
1365 return kc_transaction(dbt
, error
, ^{
1367 bool ok
= (SecDbExec(dbt
, CFSTR("DELETE from genp;"), error
) &&
1368 SecDbExec(dbt
, CFSTR("DELETE from inet;"), error
) &&
1369 SecDbExec(dbt
, CFSTR("DELETE from cert;"), error
) &&
1370 SecDbExec(dbt
, CFSTR("DELETE from keys;"), error
));
1375 #if TARGET_OS_IPHONE
1377 static bool DeleteAllFromTableForMUSRView(SecDbConnectionRef dbt
,
1383 sqlite3_stmt
*stmt
= NULL
;
1384 CFStringRef sql2
= NULL
;
1388 sql2
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@ AND pdmn NOT IN ('aku','akpu','cku','dku')"), sql
);
1390 sql2
= CFRetain(sql
);
1392 require(sql2
, fail
);
1394 stmt
= SecDbCopyStmt(dbt
, sql2
, NULL
, error
);
1395 require(stmt
, fail
);
1397 ok
= SecDbBindObject(stmt
, 1, musr
, error
);
1400 ok
= SecDbStep(dbt
, stmt
, error
, ^(bool *stop
) { });
1405 ok
= SecDbFinalize(stmt
, error
);
1408 secwarning("DeleteAllFromTableForMUSRView failed for %@ for musr: %@: %@", sql2
, musr
, error
? *error
: NULL
);
1410 CFReleaseNull(sql2
);
1415 bool SecServerDeleteAllForUser(SecDbConnectionRef dbt
, CFDataRef musrView
, bool keepU
, CFErrorRef
*error
) {
1416 secwarning("SecServerDeleteAllForUser for user: %@ keepU %s", musrView
, keepU
? "yes" : "no");
1418 return kc_transaction(dbt
, error
, ^{
1421 ok
= (DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM genp WHERE musr = ?"), musrView
, keepU
, error
) &&
1422 DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM inet WHERE musr = ?"), musrView
, keepU
, error
) &&
1423 DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM cert WHERE musr = ?"), musrView
, keepU
, error
) &&
1424 DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM keys WHERE musr = ?"), musrView
, keepU
, error
));
1432 struct s3dl_export_row_ctx
{
1433 struct s3dl_query_ctx qc
;
1434 keybag_handle_t dest_keybag
;
1435 enum SecItemFilter filter
;
1439 static void s3dl_export_row(sqlite3_stmt
*stmt
, void *context
) {
1440 struct s3dl_export_row_ctx
*c
= context
;
1442 SecAccessControlRef access_control
= NULL
;
1443 CFErrorRef localError
= NULL
;
1445 /* Skip akpu items when backing up, those are intentionally lost across restores. The same applies to SEP-based keys */
1446 bool skip_akpu_or_token
= c
->filter
== kSecBackupableItemFilter
;
1448 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
1449 CFMutableDictionaryRef item
= NULL
;
1450 bool ok
= s3dl_item_from_col(stmt
, q
, 1, c
->qc
.accessGroups
, &item
, &access_control
, &localError
);
1452 bool is_akpu
= access_control
? CFEqualSafe(SecAccessControlGetProtection(access_control
),
1453 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
) : false;
1454 bool is_token
= (ok
&& item
!= NULL
) ? CFDictionaryContainsKey(item
, kSecAttrTokenID
) : false;
1456 if (ok
&& item
&& !(skip_akpu_or_token
&& (is_akpu
|| is_token
))) {
1457 /* Only export sysbound items if do_sys_bound is true, only export non sysbound items otherwise. */
1458 bool do_sys_bound
= c
->filter
== kSecSysBoundItemFilter
;
1459 if (c
->filter
== kSecNoItemFilter
||
1460 SecItemIsSystemBound(item
, q
->q_class
, c
->multiUser
) == do_sys_bound
) {
1461 /* Re-encode the item. */
1462 secdebug("item", "export rowid %llu item: %@", rowid
, item
);
1463 /* The code below could be moved into handle_row. */
1464 CFDataRef pref
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, item
);
1466 if (c
->dest_keybag
!= KEYBAG_NONE
) {
1467 CFMutableDictionaryRef auth_attribs
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1468 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInAuthenticatedDataFlag
) {
1469 CFTypeRef value
= CFDictionaryGetValue(item
, desc
->name
);
1471 CFDictionaryAddValue(auth_attribs
, desc
->name
, value
);
1472 CFDictionaryRemoveValue(item
, desc
->name
);
1476 /* Encode and encrypt the item to the specified keybag. */
1477 CFDataRef edata
= NULL
;
1478 bool encrypted
= ks_encrypt_data(c
->dest_keybag
, access_control
, q
->q_use_cred_handle
, item
, auth_attribs
, &edata
, false, &q
->q_error
);
1479 CFDictionaryRemoveAllValues(item
);
1480 CFRelease(auth_attribs
);
1482 CFDictionarySetValue(item
, kSecValueData
, edata
);
1483 CFReleaseSafe(edata
);
1485 seccritical("ks_encrypt_data %@,rowid=%" PRId64
": failed: %@", q
->q_class
->name
, rowid
, q
->q_error
);
1486 CFReleaseNull(q
->q_error
);
1489 if (CFDictionaryGetCount(item
)) {
1490 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
1491 CFArrayAppendValue((CFMutableArrayRef
)c
->qc
.result
, item
);
1494 CFReleaseSafe(pref
);
1499 OSStatus status
= SecErrorGetOSStatus(localError
);
1501 if (status
== errSecInteractionNotAllowed
&& is_akpu
&& skip_akpu_or_token
) {
1502 // We expect akpu items to be inaccessible when the device is locked.
1503 CFReleaseNull(localError
);
1505 /* This happens a lot when trying to migrate keychain before first unlock, so only a notice */
1506 /* If the error is "corrupted item" then we just ignore it, otherwise we save it in the query */
1507 secinfo("item","Could not export item for rowid %llu: %@", rowid
, localError
);
1509 if(status
== errSecDecode
) {
1510 CFReleaseNull(localError
);
1512 CFReleaseSafe(q
->q_error
);
1513 q
->q_error
=localError
;
1517 CFReleaseSafe(access_control
);
1521 SecCreateKeybagUUID(keybag_handle_t keybag
)
1523 #if !TARGET_HAS_KEYSTORE
1528 if (aks_get_bag_uuid(keybag
, uuid
) != KERN_SUCCESS
)
1530 uuid_unparse_lower(uuid
, uuidstr
);
1531 return CFStringCreateWithCString(NULL
, uuidstr
, kCFStringEncodingUTF8
);
1537 SecServerCopyKeychainPlist(SecDbConnectionRef dbt
,
1538 SecurityClient
*client
,
1539 keybag_handle_t src_keybag
,
1540 keybag_handle_t dest_keybag
,
1541 enum SecItemFilter filter
,
1542 CFErrorRef
*error
) {
1543 CFMutableDictionaryRef keychain
;
1544 keychain
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1545 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1547 bool inMultiUser
= false;
1548 CFStringRef keybaguuid
= NULL
;
1549 Query q
= { .q_keybag
= src_keybag
,
1554 if (error
&& !*error
)
1555 SecError(errSecAllocate
, error
, CFSTR("Can't create keychain dictionary"));
1560 kSecReturnDataMask
|
1561 kSecReturnAttributesMask
|
1562 kSecReturnPersistentRefMask
;
1563 q
.q_limit
= kSecMatchUnlimited
;
1564 q
.q_skip_acl_items
= true;
1567 #if TARGET_OS_IPHONE
1568 if (client
&& client
->inMultiUser
) {
1569 q
.q_musrView
= SecMUSRCreateActiveUserUUID(client
->uid
);
1574 q
.q_musrView
= SecMUSRGetSingleUserKeychainUUID();
1575 CFRetain(q
.q_musrView
);
1577 keybaguuid
= SecCreateKeybagUUID(dest_keybag
);
1579 CFDictionarySetValue(keychain
, kSecBackupKeybagUUIDKey
, keybaguuid
);
1582 /* Get rid of this duplicate. */
1583 const SecDbClass
*SecDbClasses
[] = {
1590 for (class_ix
= 0; class_ix
< array_size(SecDbClasses
);
1592 q
.q_class
= SecDbClasses
[class_ix
];
1593 struct s3dl_export_row_ctx ctx
= {
1594 .qc
= { .q
= &q
, .dbt
= dbt
},
1595 .dest_keybag
= dest_keybag
, .filter
= filter
,
1596 .multiUser
= inMultiUser
,
1599 secnotice("item", "exporting class '%@'", q
.q_class
->name
);
1601 CFErrorRef localError
= NULL
;
1602 if (s3dl_query(s3dl_export_row
, &ctx
, &localError
)) {
1603 if (CFArrayGetCount(ctx
.qc
.result
))
1604 CFDictionaryAddValue(keychain
, q
.q_class
->name
, ctx
.qc
.result
);
1607 OSStatus status
= (OSStatus
)CFErrorGetCode(localError
);
1608 if (status
== errSecItemNotFound
) {
1609 CFRelease(localError
);
1611 secerror("Export failed: %@", localError
);
1613 CFReleaseSafe(*error
);
1614 *error
= localError
;
1616 CFRelease(localError
);
1618 CFReleaseNull(keychain
);
1619 CFReleaseNull(ctx
.qc
.result
);
1623 CFReleaseNull(ctx
.qc
.result
);
1627 CFReleaseNull(q
.q_musrView
);
1628 CFReleaseNull(keybaguuid
);
1633 struct SecServerImportClassState
{
1634 SecDbConnectionRef dbt
;
1636 keybag_handle_t src_keybag
;
1637 keybag_handle_t dest_keybag
;
1638 SecurityClient
*client
;
1639 enum SecItemFilter filter
;
1642 struct SecServerImportItemState
{
1643 const SecDbClass
*class;
1644 struct SecServerImportClassState
*s
;
1648 SecServerImportItem(const void *value
, void *context
)
1650 struct SecServerImportItemState
*state
= (struct SecServerImportItemState
*)context
;
1651 bool inMultiUser
= false;
1652 #if TARGET_OS_IPHONE
1653 if (state
->s
->client
->inMultiUser
)
1657 if (state
->s
->error
)
1660 if (!isDictionary(value
)) {
1661 SecError(errSecParam
, &state
->s
->error
, CFSTR("value %@ is not a dictionary"), value
);
1665 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
1667 secdebug("item", "Import Item : %@", dict
);
1669 /* We use the kSecSysBoundItemFilter to indicate that we don't
1670 * preserve rowid's during import.
1672 if (state
->s
->filter
== kSecBackupableItemFilter
) {
1675 /* We don't filter non sys_bound items during import since we know we
1676 * will never have any in this case.
1678 if (SecItemIsSystemBound(dict
, state
->class, inMultiUser
))
1682 * Don't bother with u items when in edu mode since our current backup system
1683 * don't keep track of items that blongs to the device (u) but rather just
1684 * merge them into one blob.
1686 if (inMultiUser
&& (pdmu
= CFDictionaryGetValue(dict
, kSecAttrAccessible
))) {
1687 if (CFEqual(pdmu
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) ||
1688 CFEqual(pdmu
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
) ||
1689 CFEqual(pdmu
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) ||
1690 CFEqual(pdmu
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
))
1692 secdebug("item", "Skipping KU item : %@", dict
);
1697 /* Avoid importing token-based items. Although newer backups should not have them,
1698 * older (iOS9, iOS10.0) produced backups with token-based items.
1700 if (CFDictionaryContainsKey(dict
, kSecAttrTokenID
)) {
1701 secdebug("item", "Skipping token-based item : %@", dict
);
1708 /* This is sligthly confusing:
1709 - During upgrade all items are exported with KEYBAG_NONE.
1710 - During restore from backup, existing sys_bound items are exported with KEYBAG_NONE, and are exported as dictionary of attributes.
1711 - Item in the actual backup are export with a real keybag, and are exported as encrypted v_Data and v_PersistentRef
1713 if (state
->s
->src_keybag
== KEYBAG_NONE
) {
1714 item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, state
->class, dict
, state
->s
->dest_keybag
, &state
->s
->error
);
1716 item
= SecDbItemCreateWithBackupDictionary(kCFAllocatorDefault
, state
->class, dict
, state
->s
->src_keybag
, state
->s
->dest_keybag
, &state
->s
->error
);
1724 CFDataRef musr
= NULL
;
1725 CFDataRef musrBackup
= CFDictionaryGetValue(dict
, kSecAttrMultiUser
);
1726 CFDataRef systemKeychainUUID
= SecMUSRGetSystemKeychainUUID();
1727 bool systemKeychain
= CFEqualSafe(musrBackup
, systemKeychainUUID
);
1729 #if TARGET_OS_IPHONE
1730 if (state
->s
->client
&& state
->s
->client
->inMultiUser
) {
1731 if (systemKeychain
) {
1732 secwarning("system keychain not allowed in multi user mode for item: %@", item
);
1734 musr
= SecMUSRCreateActiveUserUUID(state
->s
->client
->uid
);
1739 if (systemKeychain
) {
1740 musr
= SecMUSRCopySystemKeychainUUID();
1742 musr
= SecMUSRGetSingleUserKeychainUUID();
1747 CFReleaseNull(item
);
1749 SecDbItemSetValueWithName(item
, CFSTR("musr"), musr
, &state
->s
->error
);
1761 if(state
->s
->filter
!= kSecSysBoundItemFilter
) {
1762 SecDbItemExtractRowIdFromBackupDictionary(item
, dict
, &state
->s
->error
);
1764 SecDbItemInferSyncable(item
, &state
->s
->error
);
1765 insertStatus
= SecDbItemInsert(item
, state
->s
->dbt
, &state
->s
->error
);
1766 if (!insertStatus
) {
1768 When running in EduMode, multiple users share the same
1769 keychain and unfortionaly the rowid is used a
1770 persistant reference and is part of the contraints (its
1771 UNIQUE), so lets clear the rowid and try to insert the
1774 This even happens for normal operation because of
1775 SysBound entries, so in case of a failure, lets try
1776 again to insert the record.
1778 SecDbItemClearRowId(item
, NULL
);
1779 SecDbItemInsert(item
, state
->s
->dbt
, &state
->s
->error
);
1783 /* Reset error if we had one, since we just skip the current item
1784 and continue importing what we can. */
1785 if (state
->s
->error
) {
1786 secwarning("Failed to import an item (%@) of class '%@': %@ - ignoring error.",
1787 item
, state
->class->name
, state
->s
->error
);
1788 CFReleaseNull(state
->s
->error
);
1791 CFReleaseSafe(item
);
1794 static void SecServerImportClass(const void *key
, const void *value
,
1796 struct SecServerImportClassState
*state
=
1797 (struct SecServerImportClassState
*)context
;
1800 if (!isString(key
)) {
1801 SecError(errSecParam
, &state
->error
, CFSTR("class name %@ is not a string"), key
);
1804 /* ignore the Keybag UUID */
1805 if (CFEqual(key
, kSecBackupKeybagUUIDKey
))
1807 const SecDbClass
*class = kc_class_with_name(key
);
1809 secwarning("Ignoring unknown key class '%@'", key
);
1812 if (class == identity_class()) {
1813 SecError(errSecParam
, &state
->error
, CFSTR("attempt to import an identity"));
1816 struct SecServerImportItemState item_state
= {
1817 .class = class, .s
= state
,
1819 if (isArray(value
)) {
1820 CFArrayRef items
= (CFArrayRef
)value
;
1821 secwarning("Import %ld items of class %@ (filter %d)", (long)CFArrayGetCount(items
), key
, state
->filter
);
1822 CFArrayApplyFunction(items
, CFRangeMake(0, CFArrayGetCount(items
)),
1823 SecServerImportItem
, &item_state
);
1824 } else if (isDictionary(value
)) {
1825 CFDictionaryRef item
= (CFDictionaryRef
)value
;
1826 secwarning("Import %ld items of class %@ (filter %d)", (long)CFDictionaryGetCount(item
), key
, state
->filter
);
1827 SecServerImportItem(item
, &item_state
);
1829 secwarning("Unknown value type for class %@ (filter %d)", key
, state
->filter
);
1833 bool SecServerImportKeychainInPlist(SecDbConnectionRef dbt
, SecurityClient
*client
,
1834 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
1835 CFDictionaryRef keychain
, enum SecItemFilter filter
, CFErrorRef
*error
) {
1836 CFStringRef keybaguuid
= NULL
;
1839 CFDictionaryRef sys_bound
= NULL
;
1840 if (filter
== kSecBackupableItemFilter
) {
1841 /* Grab a copy of all the items for which SecItemIsSystemBound()
1843 require(sys_bound
= SecServerCopyKeychainPlist(dbt
, client
, KEYBAG_DEVICE
,
1844 KEYBAG_NONE
, kSecSysBoundItemFilter
,
1849 * Validate the uuid of the source keybag matches what we have in the backup
1851 keybaguuid
= SecCreateKeybagUUID(src_keybag
);
1853 CFStringRef uuid
= CFDictionaryGetValue(keychain
, kSecBackupKeybagUUIDKey
);
1854 if (isString(uuid
)) {
1855 require_action(CFEqual(keybaguuid
, uuid
), errOut
,
1856 SecError(errSecDecode
, error
, CFSTR("Keybag UUID (%@) mismatch with backup (%@)"),
1861 /* Delete everything in the keychain. */
1862 #if TARGET_OS_IPHONE
1863 if (client
->inMultiUser
) {
1864 CFDataRef musrView
= SecMUSRCreateActiveUserUUID(client
->uid
);
1865 require_action(musrView
, errOut
, ok
= false);
1866 require_action(ok
= SecServerDeleteAllForUser(dbt
, musrView
, true, error
), errOut
, CFReleaseNull(musrView
));
1867 CFReleaseNull(musrView
);
1871 require(ok
= SecServerDeleteAll(dbt
, error
), errOut
);
1874 struct SecServerImportClassState state
= {
1876 .src_keybag
= src_keybag
,
1877 .dest_keybag
= dest_keybag
,
1881 /* Import the provided items, preserving rowids. */
1882 secwarning("Restoring backup items '%ld'", (long)CFDictionaryGetCount(keychain
));
1883 CFDictionaryApplyFunction(keychain
, SecServerImportClass
, &state
);
1886 state
.src_keybag
= KEYBAG_NONE
;
1887 /* Import the items we preserved with random rowids. */
1888 state
.filter
= kSecSysBoundItemFilter
;
1889 secwarning("Restoring sysbound items '%ld'", (long)CFDictionaryGetCount(sys_bound
));
1890 CFDictionaryApplyFunction(sys_bound
, SecServerImportClass
, &state
);
1894 CFReleaseSafe(*error
);
1895 *error
= state
.error
;
1897 CFRelease(state
.error
);
1903 CFReleaseSafe(sys_bound
);
1904 CFReleaseSafe(keybaguuid
);
1910 SecServerBackupGetKeybagUUID(CFDictionaryRef keychain
, CFErrorRef
*error
)
1912 CFStringRef uuid
= CFDictionaryGetValue(keychain
, kSecBackupKeybagUUIDKey
);
1913 if (!isString(uuid
)) {
1914 SecError(errSecDecode
, error
, CFSTR("Missing or invalid %@ in backup dictionary"), kSecBackupKeybagUUIDKey
);
1920 #pragma mark - key rolling support
1923 struct check_generation_ctx
{
1924 struct s3dl_query_ctx query_ctx
;
1925 uint32_t current_generation
;
1928 static void check_generation(sqlite3_stmt
*stmt
, void *context
) {
1929 struct check_generation_ctx
*c
= context
;
1930 CFDataRef blob
= NULL
;
1932 const uint8_t *cursor
= NULL
;
1934 keyclass_t keyclass
;
1935 uint32_t current_generation
= c
->current_generation
;
1937 require(blob
= s3dl_copy_data_from_col(stmt
, 1, &c
->query_ctx
.q
->q_error
), out
);
1938 blobLen
= CFDataGetLength(blob
);
1939 cursor
= CFDataGetBytePtr(blob
);
1941 /* Check for underflow, ensuring we have at least one full AES block left. */
1942 if (blobLen
< sizeof(version
) + sizeof(keyclass
)) {
1943 SecError(errSecDecode
, &c
->query_ctx
.q
->q_error
, CFSTR("check_generation: Check for underflow"));
1947 version
= *((uint32_t *)cursor
);
1948 cursor
+= sizeof(version
);
1950 (void) version
; // TODO: do something with the version number.
1952 keyclass
= *((keyclass_t
*)cursor
);
1954 // TODO: export get_key_gen macro
1955 if (((keyclass
& ~key_class_last
) == 0) != (current_generation
== 0)) {
1956 c
->query_ctx
.found
++;
1959 CFReleaseSafe(blob
);
1963 c
->query_ctx
.found
++;
1964 CFReleaseSafe(blob
);
1967 bool s3dl_dbt_keys_current(SecDbConnectionRef dbt
, uint32_t current_generation
, CFErrorRef
*error
) {
1968 CFErrorRef localError
= NULL
;
1969 struct check_generation_ctx ctx
= { .query_ctx
= { .dbt
= dbt
}, .current_generation
= current_generation
};
1971 const SecDbClass
*classes
[] = {
1978 for (size_t class_ix
= 0; class_ix
< array_size(classes
); ++class_ix
) {
1979 Query
*q
= query_create(classes
[class_ix
], NULL
, NULL
, &localError
);
1983 ctx
.query_ctx
.q
= q
;
1984 q
->q_limit
= kSecMatchUnlimited
;
1986 bool ok
= s3dl_query(check_generation
, &ctx
, &localError
);
1987 query_destroy(q
, NULL
);
1988 CFReleaseNull(ctx
.query_ctx
.result
);
1990 if (!ok
&& localError
&& (CFErrorGetCode(localError
) == errSecItemNotFound
)) {
1991 CFReleaseNull(localError
);
1994 secerror("Class %@ not up to date", classes
[class_ix
]->name
);
2000 bool s3dl_dbt_update_keys(SecDbConnectionRef dbt
, SecurityClient
*client
, CFErrorRef
*error
) {
2001 return SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
2002 __block
bool ok
= false;
2003 uint32_t keystore_generation_status
;
2005 /* can we migrate to new class keys right now? */
2006 if (!aks_generation(KEYBAG_DEVICE
, generation_noop
, &keystore_generation_status
) &&
2007 (keystore_generation_status
& generation_change_in_progress
)) {
2009 /* take a lock assertion */
2010 bool operated_while_unlocked
= SecAKSDoWhileUserBagLocked(error
, ^{
2011 CFErrorRef localError
= NULL
;
2012 CFDictionaryRef backup
= SecServerCopyKeychainPlist(dbt
, NULL
,
2013 KEYBAG_DEVICE
, KEYBAG_NONE
, kSecNoItemFilter
, &localError
);
2016 secerror("Ignoring export error: %@ during roll export", localError
);
2017 CFReleaseNull(localError
);
2019 ok
= SecServerImportKeychainInPlist(dbt
, client
, KEYBAG_NONE
,
2020 KEYBAG_DEVICE
, backup
, kSecNoItemFilter
, &localError
);
2022 secerror("Ignoring export error: %@ during roll export", localError
);
2023 CFReleaseNull(localError
);
2028 if (!operated_while_unlocked
)
2031 ok
= SecError(errSecBadReq
, error
, CFSTR("No key roll in progress."));