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
32 #undef SECUREOBJECTSYNC
33 #undef SHAREDWEBCREDENTIALS
36 #include "keychain/securityd/SecItemDb.h"
37 #include <utilities/SecAKSWrappers.h>
39 #include "keychain/securityd/SecDbKeychainItem.h"
40 #include "keychain/securityd/SecItemSchema.h"
41 #include "keychain/securityd/SecItemServer.h"
42 #include <Security/SecAccessControlPriv.h>
43 #include <Security/SecBasePriv.h>
44 #include <Security/SecItem.h>
45 #include <Security/SecSignpost.h>
46 #include <Security/SecItemPriv.h>
47 #include <Security/SecItemInternal.h>
48 #include "keychain/securityd/SOSCloudCircleServer.h"
49 #include <utilities/array_size.h>
50 #include <utilities/SecIOFormat.h>
51 #include <utilities/SecCFCCWrappers.h>
52 #include <uuid/uuid.h>
54 #include "utilities/SecABC.h"
55 #include "utilities/sec_action.h"
57 #include "keychain/ckks/CKKS.h"
59 #define kSecBackupKeybagUUIDKey CFSTR("keybag-uuid")
61 const SecDbAttr
*SecDbAttrWithKey(const SecDbClass
*c
,
64 /* Special case: identites can have all attributes of either cert
66 if (c
== identity_class()) {
67 const SecDbAttr
*desc
;
68 if (!(desc
= SecDbAttrWithKey(cert_class(), key
, 0)))
69 desc
= SecDbAttrWithKey(keys_class(), key
, error
);
74 SecDbForEachAttr(c
, a
) {
75 if (CFEqual(a
->name
, key
))
78 if (CFEqual(kSecUseDataProtectionKeychain
, key
)) {
79 return NULL
; /* results in no ops for this attribute */
83 SecError(errSecNoSuchAttr
, error
, CFSTR("attribute %@ not found in class %@"), key
, c
->name
);
88 bool kc_transaction(SecDbConnectionRef dbt
, CFErrorRef
*error
, bool(^perform
)(void)) {
89 return kc_transaction_type(dbt
, kSecDbExclusiveTransactionType
, error
, perform
);
92 bool kc_transaction_type(SecDbConnectionRef dbt
, SecDbTransactionType type
, CFErrorRef
*error
, bool(^perform
)(void)) {
93 __block
bool ok
= true;
94 return ok
&& SecDbTransaction(dbt
, type
, error
, ^(bool *commit
) {
95 ok
= *commit
= perform();
99 static CFStringRef
SecDbGetKindSQL(SecDbAttrKind kind
) {
105 case kSecDbPrimaryKeyAttr
:
106 case kSecDbEncryptedDataAttr
:
107 return CFSTR("BLOB");
108 case kSecDbAccessAttr
:
109 case kSecDbStringAttr
:
110 return CFSTR("TEXT");
111 case kSecDbNumberAttr
:
114 return CFSTR("INTEGER");
116 case kSecDbCreationDateAttr
:
117 case kSecDbModificationDateAttr
:
118 return CFSTR("REAL");
119 case kSecDbRowIdAttr
:
120 return CFSTR("INTEGER PRIMARY KEY AUTOINCREMENT");
121 case kSecDbAccessControlAttr
:
122 case kSecDbUTombAttr
:
123 /* This attribute does not exist in the DB. */
128 static void SecDbAppendUnique(CFMutableStringRef sql
, CFStringRef value
, bool *haveUnique
) {
131 CFStringAppend(sql
, CFSTR("UNIQUE("));
133 SecDbAppendElement(sql
, value
, haveUnique
);
136 static void SecDbAppendCreateTableWithClass(CFMutableStringRef sql
, const SecDbClass
*c
) {
137 CFStringAppendFormat(sql
, 0, CFSTR("CREATE TABLE %@("), c
->name
);
138 SecDbForEachAttrWithMask(c
,desc
,kSecDbInFlag
) {
139 CFStringAppendFormat(sql
, 0, CFSTR("%@ %@"), desc
->name
, SecDbGetKindSQL(desc
->kind
));
140 if (desc
->flags
& kSecDbNotNullFlag
)
141 CFStringAppend(sql
, CFSTR(" NOT NULL"));
142 if (desc
->flags
& kSecDbDefault0Flag
)
143 CFStringAppend(sql
, CFSTR(" DEFAULT 0"));
144 if (desc
->flags
& kSecDbDefaultEmptyFlag
)
145 CFStringAppend(sql
, CFSTR(" DEFAULT ''"));
146 CFStringAppend(sql
, CFSTR(","));
149 bool haveUnique
= false;
150 SecDbForEachAttrWithMask(c
,desc
,kSecDbPrimaryKeyFlag
| kSecDbInFlag
) {
151 SecDbAppendUnique(sql
, desc
->name
, &haveUnique
);
154 CFStringAppend(sql
, CFSTR(")"));
156 CFStringAppend(sql
, CFSTR(");"));
159 SecDbForEachAttrWithMask(c
,desc
, kSecDbIndexFlag
| kSecDbInFlag
) {
160 if (desc
->kind
== kSecDbSyncAttr
) {
161 CFStringAppendFormat(sql
, 0, CFSTR("CREATE INDEX %@%@0 ON %@(%@) WHERE %@=0;"), c
->name
, desc
->name
, c
->name
, desc
->name
, desc
->name
);
163 CFStringAppendFormat(sql
, 0, CFSTR("CREATE INDEX %@%@ ON %@(%@);"), c
->name
, desc
->name
, c
->name
, desc
->name
);
168 static void SecDbAppendDropTableWithClass(CFMutableStringRef sql
, const SecDbClass
*c
) {
169 CFStringAppendFormat(sql
, 0, CFSTR("DROP TABLE %@;"), c
->name
);
172 static CFDataRef
SecPersistentRefCreateWithItem(SecDbItemRef item
, CFErrorRef
*error
) {
173 sqlite3_int64 row_id
= SecDbItemGetRowId(item
, error
);
175 return _SecItemCreatePersistentRef(SecDbItemGetClass(item
)->name
, row_id
, item
->attributes
);
179 bool SecItemDbCreateSchema(SecDbConnectionRef dbt
, const SecDbSchema
*schema
, CFArrayRef classIndexesForNewTables
, bool includeVersion
, CFErrorRef
*error
)
181 __block
bool ok
= true;
182 CFMutableStringRef sql
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
184 if (classIndexesForNewTables
) {
185 CFArrayForEach(classIndexesForNewTables
, ^(const void* index
) {
186 const SecDbClass
* class = schema
->classes
[(int)index
];
187 SecDbAppendCreateTableWithClass(sql
, class);
191 for (const SecDbClass
* const *pclass
= schema
->classes
; *pclass
; ++pclass
) {
192 SecDbAppendCreateTableWithClass(sql
, *pclass
);
196 if (includeVersion
) {
197 CFStringAppendFormat(sql
, NULL
, CFSTR("INSERT INTO tversion(version,minor) VALUES(%d, %d);"),
198 schema
->majorVersion
, schema
->minorVersion
);
200 CFStringPerformWithCString(sql
, ^(const char *sql_string
) {
201 ok
= SecDbErrorWithDb(sqlite3_exec(SecDbHandle(dbt
), sql_string
, NULL
, NULL
, NULL
),
202 SecDbHandle(dbt
), error
, CFSTR("sqlite3_exec: %s"), sql_string
);
208 bool SecItemDbDeleteSchema(SecDbConnectionRef dbt
, const SecDbSchema
*schema
, CFErrorRef
*error
)
210 __block
bool ok
= true;
211 CFMutableStringRef sql
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
212 for (const SecDbClass
* const *pclass
= schema
->classes
; *pclass
; ++pclass
) {
213 SecDbAppendDropTableWithClass(sql
, *pclass
);
215 CFStringPerformWithCString(sql
, ^(const char *sql_string
) {
216 ok
= SecDbErrorWithDb(sqlite3_exec(SecDbHandle(dbt
), sql_string
, NULL
, NULL
, NULL
),
217 SecDbHandle(dbt
), error
, CFSTR("sqlite3_exec: %s"), sql_string
);
223 CFTypeRef
SecDbItemCopyResult(SecDbItemRef item
, ReturnTypeMask return_type
, CFErrorRef
*error
) {
226 if (return_type
== 0) {
227 /* Caller isn't interested in any results at all. */
229 } else if (return_type
== kSecReturnDataMask
) {
230 a_result
= SecDbItemGetCachedValueWithName(item
, kSecValueData
);
232 CFRetainSafe(a_result
);
234 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
236 } else if (return_type
== kSecReturnPersistentRefMask
) {
237 a_result
= SecPersistentRefCreateWithItem(item
, error
);
239 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableForCFTypes(CFGetAllocator(item
));
240 /* We need to return more than one value. */
241 if (return_type
& kSecReturnRefMask
) {
242 CFDictionarySetValue(dict
, kSecClass
, SecDbItemGetClass(item
)->name
);
244 CFOptionFlags mask
= (((return_type
& kSecReturnDataMask
|| return_type
& kSecReturnRefMask
) ? kSecDbReturnDataFlag
: 0) |
245 ((return_type
& kSecReturnAttributesMask
|| return_type
& kSecReturnRefMask
) ? kSecDbReturnAttrFlag
: 0));
246 SecDbForEachAttr(SecDbItemGetClass(item
), desc
) {
247 if ((desc
->flags
& mask
) != 0) {
248 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
249 if (value
&& !CFEqual(kCFNull
, value
)) {
250 CFDictionarySetValue(dict
, desc
->name
, value
);
251 } else if (value
== NULL
) {
257 CFDictionaryRemoveValue(dict
, kSecAttrUUID
);
259 if (return_type
& kSecReturnPersistentRefMask
) {
260 CFDataRef pref
= SecPersistentRefCreateWithItem(item
, error
);
261 CFDictionarySetValue(dict
, kSecValuePersistentRef
, pref
);
271 /* AUDIT[securityd](done):
272 attributes (ok) is a caller provided dictionary, only its cf type has
276 s3dl_query_add(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
, CFErrorRef
*error
)
278 if (query_match_count(q
) != 0)
279 return errSecItemMatchUnsupported
;
281 /* Add requires a class to be specified unless we are adding a ref. */
282 if (q
->q_use_item_list
)
283 return errSecUseItemListUnsupported
;
285 /* Actual work here. */
286 SecDbItemRef item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, q
->q_class
, q
->q_item
, KEYBAG_DEVICE
, error
);
289 if (SecDbItemIsTombstone(item
))
290 SecDbItemSetValue(item
, &v7utomb
, q
->q_use_tomb
? q
->q_use_tomb
: kCFBooleanTrue
, NULL
);
294 ok
= SecDbItemSetValueWithName(item
, CFSTR("v_Data"), q
->q_data
, error
);
296 ok
= SecDbItemSetRowId(item
, q
->q_row_id
, error
);
298 ok
= SecDbItemSetValueWithName(item
, CFSTR("musr"), q
->q_musrView
, error
);
299 SecDbItemSetCredHandle(item
, q
->q_use_cred_handle
);
302 if(SecCKKSIsEnabled() && !SecCKKSTestDisableAutomaticUUID()) {
303 s3dl_item_make_new_uuid(item
, q
->q_uuid_from_primary_key
, error
);
305 if(q
->q_add_sync_callback
) {
306 CFTypeRef uuid
= SecDbItemGetValue(item
, &v10itemuuid
, error
);
308 CKKSRegisterSyncStatusCallback(uuid
, q
->q_add_sync_callback
);
310 secerror("Couldn't fetch UUID from item; can't call callback");
317 // We care about the new item's UUID only when we just made it from the primary key.
318 // Otherwise, we just made a random one, and don't mind if it changes.
319 ok
= SecDbItemInsert(item
, dbt
, q
->q_uuid_from_primary_key
, error
);
323 if (result
&& q
->q_return_type
) {
324 *result
= SecDbItemCopyResult(item
, q
->q_return_type
, error
);
327 if (!ok
&& error
&& *error
) {
328 if (CFEqual(CFErrorGetDomain(*error
), kSecDbErrorDomain
) && CFErrorGetCode(*error
) == SQLITE_CONSTRAINT
) {
329 CFReleaseNull(*error
);
330 SecError(errSecDuplicateItem
, error
, CFSTR("duplicate item %@"), item
);
331 } else if (CFEqual(CFErrorGetDomain(*error
), kSecErrorDomain
) && CFErrorGetCode(*error
) == errSecDecode
) { //handle situation when item have pdmn=akpu but passcode is not set
332 CFTypeRef value
= SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbAccessAttr
, error
), error
);
333 if (value
&& CFEqual(value
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
)) {
334 CFReleaseNull(*error
);
335 SecError(errSecAuthFailed
, error
, CFSTR("authentication failed"));
342 if (SecDbItemIsSyncable(item
))
343 q
->q_sync_changed
= true;
346 secdebug("dbitem", "inserting item %@%s%@", item
, ok
? "" : "failed: ", ok
|| error
== NULL
? (CFErrorRef
)CFSTR("") : *error
);
353 bool s3dl_item_make_new_uuid(SecDbItemRef item
, bool uuid_from_primary_key
, CFErrorRef
* error
) {
358 // Set the item UUID.
359 CFUUIDRef uuid
= NULL
;
360 // Were we asked to make the UUID static?
361 if (uuid_from_primary_key
) {
362 // This UUID isn't used in any security mechanism, so we can
363 // just use the first bits of the SHA256 hash.
364 CFDataRef pkhash
= SecDbKeychainItemCopySHA256PrimaryKey(item
, error
);
365 if(CFDataGetLength(pkhash
) >= 16) {
367 CFRange range
= CFRangeMake(0, 16);
368 CFDataGetBytes(pkhash
, range
, uuidBytes
);
370 uuid
= CFUUIDCreateWithBytes(NULL
,
388 CFReleaseNull(pkhash
);
391 uuid
= CFUUIDCreate(NULL
);
393 SecDbItemSetValueWithName(item
, kSecAttrUUID
, uuid
, error
);
398 typedef void (*s3dl_handle_row
)(sqlite3_stmt
*stmt
, void *context
);
401 s3dl_copy_data_from_col(sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
402 return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt
, col
),
403 sqlite3_column_bytes(stmt
, col
),
408 s3dl_item_from_col(sqlite3_stmt
*stmt
, Query
*q
, int col
, CFArrayRef accessGroups
,
409 CFMutableDictionaryRef
*item
, SecAccessControlRef
*access_control
, keyclass_t
* keyclass
, CFErrorRef
*error
) {
410 CFDataRef edata
= NULL
;
412 require(edata
= s3dl_copy_data_from_col(stmt
, col
, error
), out
);
413 ok
= s3dl_item_from_data(edata
, q
, accessGroups
, item
, access_control
, keyclass
, error
);
416 CFReleaseSafe(edata
);
420 struct s3dl_query_ctx
{
422 CFArrayRef accessGroups
;
423 SecDbConnectionRef dbt
;
428 /* Return whatever the caller requested based on the value of q->q_return_type.
429 keys and values must be 3 larger than attr_count in size to accomadate the
430 optional data, class and persistent ref results. This is so we can use
431 the CFDictionaryCreate() api here rather than appending to a
432 mutable dictionary. */
433 static CF_RETURNS_RETAINED CFTypeRef
434 handle_result(Query
*q
,
435 CFMutableDictionaryRef item
,
440 data
= CFDictionaryGetValue(item
, kSecValueData
);
441 CFDataRef pref
= NULL
;
442 if (q
->q_return_type
& kSecReturnPersistentRefMask
) {
443 pref
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, item
);
445 if (q
->q_return_type
== 0) {
446 /* Caller isn't interested in any results at all. */
448 } else if (q
->q_return_type
== kSecReturnDataMask
) {
453 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
455 } else if (q
->q_return_type
== kSecReturnPersistentRefMask
) {
456 a_result
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, item
);
458 /* We need to return more than one value. */
459 if (q
->q_return_type
& kSecReturnRefMask
) {
460 CFDictionarySetValue(item
, kSecClass
, q
->q_class
->name
);
461 } else if ((q
->q_return_type
& kSecReturnAttributesMask
)) {
462 if (!(q
->q_return_type
& kSecReturnDataMask
)) {
463 CFDictionaryRemoveValue(item
, kSecValueData
);
466 // Add any attributes which are supposed to be returned, are not present in the decrypted blob,
467 // and have a way to generate themselves.
468 SecDbItemRef itemRef
= NULL
;
469 SecDbForEachAttrWithMask(q
->q_class
, attr
, kSecDbReturnAttrFlag
) {
470 if(!CFDictionaryGetValue(item
, attr
->name
) && attr
->copyValue
) {
471 CFErrorRef cferror
= NULL
;
473 itemRef
= SecDbItemCreateWithAttributes(NULL
, q
->q_class
, item
, KEYBAG_DEVICE
, &cferror
);
475 if(!cferror
&& itemRef
) {
476 if (attr
->kind
!= kSecDbSHA1Attr
|| (q
->q_return_type
& kSecReturnDataMask
)) { // we'll skip returning the sha1 attribute unless the client has also asked us to return data, because without data our sha1 could be invalid
477 CFTypeRef attrValue
= attr
->copyValue(itemRef
, attr
, &cferror
);
478 if (!cferror
&& attrValue
) {
479 CFDictionarySetValue(item
, attr
->name
, attrValue
);
481 CFReleaseNull(attrValue
);
484 CFReleaseNull(cferror
);
487 CFReleaseNull(itemRef
);
489 CFDictionaryRemoveValue(item
, kSecAttrUUID
);
492 CFDictionaryRemoveAllValues(item
);
493 if ((q
->q_return_type
& kSecReturnDataMask
) && data
) {
494 CFDictionarySetValue(item
, kSecValueData
, data
);
498 if (q
->q_return_type
& kSecReturnPersistentRefMask
&& pref
!= NULL
) {
499 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
509 static void s3dl_merge_into_dict(const void *key
, const void *value
, void *context
) {
510 CFDictionarySetValue(context
, key
, value
);
513 static bool checkTokenObjectID(CFDataRef token_object_id
, CFDataRef value_data
) {
514 bool equalOID
= false;
515 require_quiet(value_data
, out
);
516 CFDictionaryRef itemValue
= SecTokenItemValueCopy(value_data
, NULL
);
517 require_quiet(itemValue
, out
);
518 CFDataRef oID
= CFDictionaryGetValue(itemValue
, kSecTokenValueObjectIDKey
);
519 equalOID
= CFEqualSafe(token_object_id
, oID
);
520 CFRelease(itemValue
);
525 static void s3dl_query_row(sqlite3_stmt
*stmt
, void *context
) {
526 struct s3dl_query_ctx
*c
= context
;
528 ReturnTypeMask saved_mask
= q
->q_return_type
;
530 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
531 CFMutableDictionaryRef item
= NULL
;
535 ok
= s3dl_item_from_col(stmt
, q
, 1, c
->accessGroups
, &item
, NULL
, NULL
, &q
->q_error
);
537 OSStatus status
= SecErrorGetOSStatus(q
->q_error
);
538 // errSecDecode means the item is corrupted, stash it for delete.
539 if (status
== errSecDecode
) {
540 secwarning("ignoring corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, rowid
, q
->q_error
);
542 // Can't get rid of this item on the read path. Let's come back from elsewhere.
543 CFStringRef tablename
= CFStringCreateCopy(kCFAllocatorDefault
, q
->q_class
->name
);
544 deleteCorruptedItemAsync(c
->dbt
, tablename
, rowid
);
545 CFReleaseNull(tablename
);
547 // provide helpful logging statement
548 CFDataRef edata
= s3dl_copy_data_from_col(stmt
, 1, NULL
);
549 CFMutableStringRef edatastring
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
551 CFStringAppendEncryptedData(edatastring
, edata
);
552 secnotice("item", "corrupted edata=%@", edatastring
);
554 CFReleaseSafe(edata
);
555 CFReleaseSafe(edatastring
);
557 CFReleaseNull(q
->q_error
); // This item was never here, keep going
558 } else if (status
== errSecAuthNeeded
) {
559 secwarning("Authentication is needed for %@,rowid=%" PRId64
" (%" PRIdOSStatus
"): %@", q
->q_class
->name
, rowid
, status
, q
->q_error
);
560 } else if (status
== errSecInteractionNotAllowed
) {
561 static dispatch_once_t kclockedtoken
;
562 static sec_action_t kclockedaction
;
563 dispatch_once(&kclockedtoken
, ^{
564 kclockedaction
= sec_action_create("ratelimiterdisabledlogevent", 1);
565 sec_action_set_handler(kclockedaction
, ^{
566 secerror("decode item failed, keychain is locked (%d)", (int)errSecInteractionNotAllowed
);
569 sec_action_perform(kclockedaction
);
570 } else if (status
== errSecMissingEntitlement
) {
571 // That's fine, let's pretend the item never existed for this query.
572 // We may find other, better items for the caller!
573 CFReleaseNull(q
->q_error
);
575 secerror("decode %@,rowid=%" PRId64
" failed (%" PRIdOSStatus
"): %@", q
->q_class
->name
, rowid
, status
, q
->q_error
);
577 // q->q_error will be released appropriately by a call to query_error
584 if (CFDictionaryContainsKey(item
, kSecAttrTokenID
) && (q
->q_return_type
& kSecReturnDataMask
) == 0) {
585 // For token-based items, to get really meaningful set of attributes we must provide also data field, so augment mask
586 // and restart item decoding cycle.
587 q
->q_return_type
|= kSecReturnDataMask
;
592 if (q
->q_token_object_id
!= NULL
&& !checkTokenObjectID(q
->q_token_object_id
, CFDictionaryGetValue(item
, kSecValueData
)))
595 if (q
->q_class
== identity_class()) {
596 // TODO: Use col 2 for key rowid and use both rowids in persistent ref.
598 CFMutableDictionaryRef key
;
599 /* TODO : if there is a errSecDecode error here, we should cleanup */
600 if (!s3dl_item_from_col(stmt
, q
, 3, c
->accessGroups
, &key
, NULL
, NULL
, &q
->q_error
) || !key
)
603 CFDataRef certData
= CFDictionaryGetValue(item
, kSecValueData
);
605 CFDictionarySetValue(key
, kSecAttrIdentityCertificateData
, certData
);
606 CFDictionaryRemoveValue(item
, kSecValueData
);
609 CFDataRef certTokenID
= CFDictionaryGetValue(item
, kSecAttrTokenID
);
611 CFDictionarySetValue(key
, kSecAttrIdentityCertificateTokenID
, certTokenID
);
612 CFDictionaryRemoveValue(item
, kSecAttrTokenID
);
614 CFDictionaryApplyFunction(item
, s3dl_merge_into_dict
, key
);
619 if (!match_item(c
->dbt
, q
, c
->accessGroups
, item
))
622 CFTypeRef a_result
= handle_result(q
, item
, rowid
);
624 if (a_result
== kCFNull
) {
625 /* Caller wasn't interested in a result, but we still
626 count this row as found. */
627 CFRelease(a_result
); // Help shut up clang
628 } else if (q
->q_limit
== 1) {
629 c
->result
= a_result
;
631 CFArrayAppendValue((CFMutableArrayRef
)c
->result
, a_result
);
638 q
->q_return_type
= saved_mask
;
643 SecDbAppendWhereROWID(CFMutableStringRef sql
,
644 CFStringRef col
, sqlite_int64 row_id
,
647 SecDbAppendWhereOrAnd(sql
, needWhere
);
648 CFStringAppendFormat(sql
, NULL
, CFSTR("%@=%lld"), col
, row_id
);
653 SecDbAppendWhereAttrs(CFMutableStringRef sql
, const Query
*q
, bool *needWhere
) {
654 CFIndex ix
, attr_count
= query_attr_count(q
);
655 for (ix
= 0; ix
< attr_count
; ++ix
) {
656 SecDbAppendWhereOrAndEquals(sql
, query_attr_at(q
, ix
).key
, needWhere
);
661 SecDbAppendWhereAccessGroups(CFMutableStringRef sql
,
663 CFArrayRef accessGroups
,
665 CFIndex ix
, ag_count
;
666 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
))) {
670 SecDbAppendWhereOrAnd(sql
, needWhere
);
671 CFStringAppend(sql
, col
);
672 CFStringAppend(sql
, CFSTR(" IN (?"));
673 for (ix
= 1; ix
< ag_count
; ++ix
) {
674 CFStringAppend(sql
, CFSTR(",?"));
676 CFStringAppend(sql
, CFSTR(")"));
680 isQueryOverAllMUSRViews(CFTypeRef musrView
)
682 return SecMUSRIsViewAllViews(musrView
);
686 isQueryOverSingleUserView(CFTypeRef musrView
)
688 return isNull(musrView
);
693 isQueryOverBothUserAndSystem(CFTypeRef musrView
, uid_t
*uid
)
695 return SecMUSRGetBothUserAndSystemUUID(musrView
, uid
);
700 SecDbAppendWhereMusr(CFMutableStringRef sql
,
704 SecDbAppendWhereOrAnd(sql
, needWhere
);
707 if (isQueryOverBothUserAndSystem(q
->q_musrView
, NULL
)) {
708 CFStringAppend(sql
, CFSTR("(musr = ? OR musr = ?)"));
711 if (isQueryOverAllMUSRViews(q
->q_musrView
)) {
712 /* query over all items, regardless of view */
713 } else if (isQueryOverSingleUserView(q
->q_musrView
)) {
714 CFStringAppend(sql
, CFSTR("musr = ?"));
716 CFStringAppend(sql
, CFSTR("musr = ?"));
721 SecDbAppendWhereAppClip(CFMutableStringRef sql
,
725 if (!q
->q_skip_app_clip_items
) {
729 SecDbAppendWhereOrAnd(sql
, needWhere
);
730 CFStringAppend(sql
, CFSTR("clip = 0"));
733 static void SecDbAppendWhereClause(CFMutableStringRef sql
, const Query
*q
,
734 CFArrayRef accessGroups
) {
735 bool needWhere
= true;
736 SecDbAppendWhereROWID(sql
, CFSTR("ROWID"), q
->q_row_id
, &needWhere
);
737 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
738 SecDbAppendWhereMusr(sql
, q
, &needWhere
);
739 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
740 SecDbAppendWhereAppClip(sql
, q
, &needWhere
);
743 static void SecDbAppendLimit(CFMutableStringRef sql
, CFIndex limit
) {
744 if (limit
!= kSecMatchUnlimited
)
745 CFStringAppendFormat(sql
, NULL
, CFSTR(" LIMIT %" PRIdCFIndex
), limit
);
748 static CFStringRef
s3dl_create_select_sql(Query
*q
, CFArrayRef accessGroups
) {
749 CFMutableStringRef sql
= CFStringCreateMutable(NULL
, 0);
750 if (q
->q_class
== identity_class()) {
751 CFStringAppend(sql
, CFSTR("SELECT crowid, certdata"
753 "(SELECT cert.rowid AS crowid, cert.labl AS labl,"
754 " cert.issr AS issr, cert.slnr AS slnr, cert.skid AS skid,"
755 " cert.tkid AS tkid,"
756 " keys.*,cert.data AS certdata"
758 " WHERE keys.priv == 1 AND cert.pkhh == keys.klbl"));
759 SecDbAppendWhereAccessGroups(sql
, CFSTR("cert.agrp"), accessGroups
, 0);
760 /* The next 3 SecDbAppendWhere calls are in the same order as in
761 SecDbAppendWhereClause(). This makes sqlBindWhereClause() work,
762 as long as we do an extra sqlBindAccessGroups first. */
763 SecDbAppendWhereROWID(sql
, CFSTR("crowid"), q
->q_row_id
, 0);
764 CFStringAppend(sql
, CFSTR(")"));
765 bool needWhere
= true;
766 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
767 SecDbAppendWhereMusr(sql
, q
, &needWhere
);
768 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
769 SecDbAppendWhereAppClip(sql
, q
, &needWhere
);
771 // Most of the time we don't need agrp, but if an item fails to decode and we want to know more then this is helpful
772 CFStringAppend(sql
, CFSTR("SELECT rowid, data, agrp FROM "));
773 CFStringAppend(sql
, q
->q_class
->name
);
774 SecDbAppendWhereClause(sql
, q
, accessGroups
);
776 //do not append limit for all queries which needs filtering
777 if (q
->q_match_issuer
== NULL
&& q
->q_match_policy
== NULL
&& q
->q_match_valid_on_date
== NULL
&& q
->q_match_trusted_only
== NULL
&& q
->q_token_object_id
== NULL
) {
778 SecDbAppendLimit(sql
, q
->q_limit
);
784 static bool sqlBindMusr(sqlite3_stmt
*stmt
, const Query
*q
, int *pParam
, CFErrorRef
*error
) {
790 if (isQueryOverBothUserAndSystem(q
->q_musrView
, &uid
)) {
791 /* network extensions are special and get to query both user and system views */
792 CFDataRef systemUUID
= SecMUSRGetSystemKeychainUUID();
793 result
= SecDbBindObject(stmt
, param
++, systemUUID
, error
);
795 CFDataRef activeUser
= SecMUSRCreateActiveUserUUID(uid
);
796 result
= SecDbBindObject(stmt
, param
++, activeUser
, error
);
797 CFReleaseNull(activeUser
);
801 if (isQueryOverAllMUSRViews(q
->q_musrView
)) {
802 /* query over all items, regardless of view */
803 } else if (isQueryOverSingleUserView(q
->q_musrView
)) {
804 CFDataRef singleUUID
= SecMUSRGetSingleUserKeychainUUID();
805 result
= SecDbBindObject(stmt
, param
++, singleUUID
, error
);
807 result
= SecDbBindObject(stmt
, param
++, q
->q_musrView
, error
);
815 static bool sqlBindAccessGroups(sqlite3_stmt
*stmt
, CFArrayRef accessGroups
,
816 int *pParam
, CFErrorRef
*error
) {
819 CFIndex ix
, count
= accessGroups
? CFArrayGetCount(accessGroups
) : 0;
820 for (ix
= 0; ix
< count
; ++ix
) {
821 result
= SecDbBindObject(stmt
, param
++,
822 CFArrayGetValueAtIndex(accessGroups
, ix
),
831 static bool sqlBindWhereClause(sqlite3_stmt
*stmt
, const Query
*q
,
832 CFArrayRef accessGroups
, int *pParam
, CFErrorRef
*error
) {
835 CFIndex ix
, attr_count
= query_attr_count(q
);
836 for (ix
= 0; ix
< attr_count
; ++ix
) {
837 result
= SecDbBindObject(stmt
, param
++, query_attr_at(q
, ix
).value
, error
);
843 result
= sqlBindMusr(stmt
, q
, ¶m
, error
);
846 /* Bind the access group to the sql. */
848 result
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
855 bool SecDbItemQuery(SecDbQueryRef query
, CFArrayRef accessGroups
, SecDbConnectionRef dbconn
, CFErrorRef
*error
,
856 void (^handle_row
)(SecDbItemRef item
, bool *stop
)) {
857 __block
bool ok
= true;
858 /* Sanity check the query. */
860 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
862 bool (^return_attr
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
863 // The attributes here must match field list hardcoded in s3dl_select_sql used below, which is
865 return attr
->kind
== kSecDbRowIdAttr
|| attr
->kind
== kSecDbEncryptedDataAttr
;
868 CFStringRef sql
= s3dl_create_select_sql(query
, accessGroups
);
871 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
872 /* Bind the values being searched for to the SELECT statement. */
874 if (query
->q_class
== identity_class()) {
875 /* Bind the access groups to cert.agrp. */
876 ok
&= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
879 ok
&= sqlBindWhereClause(stmt
, query
, accessGroups
, ¶m
, error
);
881 SecDbStep(dbconn
, stmt
, error
, ^(bool *stop
) {
882 SecDbItemRef itemFromStatement
= SecDbItemCreateWithStatement(kCFAllocatorDefault
, query
->q_class
, stmt
, query
->q_keybag
, error
, return_attr
);
883 if (itemFromStatement
) {
884 CFTransferRetained(itemFromStatement
->credHandle
, query
->q_use_cred_handle
);
885 if (match_item(dbconn
, query
, accessGroups
, itemFromStatement
->attributes
))
886 handle_row(itemFromStatement
, stop
);
887 CFReleaseNull(itemFromStatement
);
889 secerror("failed to create item from stmt: %@", error
? *error
: (CFErrorRef
)"no error");
891 CFReleaseNull(*error
);
906 s3dl_query(s3dl_handle_row handle_row
,
907 void *context
, CFErrorRef
*error
)
909 struct s3dl_query_ctx
*c
= context
;
910 SecDbConnectionRef dbt
= c
->dbt
;
912 CFArrayRef accessGroups
= c
->accessGroups
;
914 /* Sanity check the query. */
916 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
918 /* Actual work here. */
919 if (q
->q_limit
== 1) {
922 c
->result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
924 CFStringRef sql
= s3dl_create_select_sql(q
, accessGroups
);
925 bool ok
= SecDbWithSQL(dbt
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
927 /* Bind the values being searched for to the SELECT statement. */
929 if (q
->q_class
== identity_class()) {
930 /* Bind the access groups to cert.agrp. */
931 sql_ok
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
934 sql_ok
= sqlBindWhereClause(stmt
, q
, accessGroups
, ¶m
, error
);
936 SecDbForEach(dbt
, stmt
, error
, ^bool (int row_index
) {
937 handle_row(stmt
, context
);
939 bool needs_auth
= q
->q_error
&& CFErrorGetCode(q
->q_error
) == errSecAuthNeeded
;
940 if (q
->q_skip_acl_items
&& needs_auth
)
941 // Skip items needing authentication if we are told to do so.
942 CFReleaseNull(q
->q_error
);
944 bool stop
= q
->q_limit
!= kSecMatchUnlimited
&& c
->found
>= q
->q_limit
;
945 stop
= stop
|| (q
->q_error
&& !needs_auth
);
954 // First get the error from the query, since errSecDuplicateItem from an
955 // update query should superceed the errSecItemNotFound below.
956 if (!query_error(q
, error
))
958 if (ok
&& c
->found
== 0) {
959 ok
= SecError(errSecItemNotFound
, error
, CFSTR("no matching items found"));
960 if (q
->q_spindump_on_failure
) {
961 __security_stackshotreport(CFSTR("ItemNotFound"), __sec_exception_code_LostInMist
);
969 s3dl_copy_matching(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
,
970 CFArrayRef accessGroups
, CFErrorRef
*error
)
972 struct s3dl_query_ctx ctx
= {
973 .q
= q
, .accessGroups
= accessGroups
, .dbt
= dbt
,
975 if (q
->q_row_id
&& query_attr_count(q
))
976 return SecError(errSecItemIllegalQuery
, error
,
977 CFSTR("attributes to query illegal; both row_id and other attributes can't be searched at the same time"));
978 if (q
->q_token_object_id
&& query_attr_count(q
) != 1)
979 return SecError(errSecItemIllegalQuery
, error
,
980 CFSTR("attributes to query illegal; both token persistent ref and other attributes can't be searched at the same time"));
982 // Only copy things that aren't tombstones unless the client explicitly asks otherwise.
983 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
984 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
985 bool ok
= s3dl_query(s3dl_query_row
, &ctx
, error
);
987 *result
= ctx
.result
;
989 CFReleaseSafe(ctx
.result
);
994 typedef void (^s3dl_item_digest_callback
)(CFDataRef persistantReference
, CFDataRef encryptedData
);
996 struct s3dl_digest_ctx
{
998 SecDbConnectionRef dbt
;
999 s3dl_item_digest_callback item_callback
;
1002 static void s3dl_query_row_digest(sqlite3_stmt
*stmt
, void *context
) {
1003 struct s3dl_query_ctx
*c
= context
;
1006 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
1007 CFDataRef edata
= s3dl_copy_data_from_col(stmt
, 1, NULL
);
1008 CFDataRef persistant_reference
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, NULL
);
1009 CFDataRef digest
= NULL
;
1012 digest
= CFDataCopySHA256Digest(edata
, NULL
);
1015 if (digest
&& persistant_reference
) {
1016 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(NULL
,
1017 kSecValuePersistentRef
, persistant_reference
,
1018 kSecValueData
, digest
,
1021 CFArrayAppendValue((CFMutableArrayRef
)c
->result
, item
);
1022 CFReleaseNull(item
);
1025 secinfo("item", "rowid %lu in %@ failed to create pref/digest", (unsigned long)rowid
, q
->q_class
->name
);
1027 CFReleaseNull(digest
);
1028 CFReleaseNull(edata
);
1029 CFReleaseNull(persistant_reference
);
1034 s3dl_copy_digest(SecDbConnectionRef dbt
, Query
*q
, CFArrayRef
*result
, CFArrayRef accessGroups
, CFErrorRef
*error
)
1036 struct s3dl_query_ctx ctx
= {
1037 .q
= q
, .dbt
= dbt
, .accessGroups
= accessGroups
,
1039 // Force to always return an array
1040 q
->q_limit
= kSecMatchUnlimited
;
1041 // This interface only queries live data
1042 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
1043 bool ok
= s3dl_query(s3dl_query_row_digest
, &ctx
, error
);
1045 *result
= (CFArrayRef
)ctx
.result
;
1047 CFReleaseSafe(ctx
.result
);
1052 /* First remove key from q->q_pairs if it's present, then add the attribute again. */
1053 static void query_set_attribute_with_desc(const SecDbAttr
*desc
, const void *value
, Query
*q
) {
1054 if (CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1056 for (ix
= 0; ix
< q
->q_attr_end
; ++ix
) {
1057 if (CFEqual(desc
->name
, q
->q_pairs
[ix
].key
)) {
1058 CFReleaseSafe(q
->q_pairs
[ix
].value
);
1060 for (; ix
< q
->q_attr_end
; ++ix
) {
1061 q
->q_pairs
[ix
] = q
->q_pairs
[ix
+ 1];
1063 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
1068 query_add_attribute_with_desc(desc
, value
, q
);
1071 /* Update modification_date if needed. */
1072 static void query_pre_update(Query
*q
) {
1073 SecDbForEachAttr(q
->q_class
, desc
) {
1074 if (desc
->kind
== kSecDbModificationDateAttr
) {
1075 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
1076 query_set_attribute_with_desc(desc
, now
, q
);
1082 /* Make sure all attributes that are marked as not_null have a value. If
1083 force_date is false, only set mdat and cdat if they aren't already set. */
1084 void query_pre_add(Query
*q
, bool force_date
) {
1085 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
1086 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInFlag
) {
1087 if (desc
->kind
== kSecDbCreationDateAttr
||
1088 desc
->kind
== kSecDbModificationDateAttr
) {
1090 query_set_attribute_with_desc(desc
, now
, q
);
1091 } else if (!CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1092 query_add_attribute_with_desc(desc
, now
, q
);
1094 } else if ((desc
->flags
& kSecDbNotNullFlag
) &&
1095 !CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1096 CFTypeRef value
= NULL
;
1097 if (desc
->flags
& kSecDbDefault0Flag
) {
1098 if (desc
->kind
== kSecDbDateAttr
)
1099 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
1102 value
= CFNumberCreate(0, kCFNumberSInt32Type
, &vzero
);
1104 } else if (desc
->flags
& kSecDbDefaultEmptyFlag
) {
1105 if (desc
->kind
== kSecDbDataAttr
|| desc
->kind
== kSecDbUUIDAttr
)
1106 value
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
1113 /* Safe to use query_add_attribute here since the attr wasn't
1115 query_add_attribute_with_desc(desc
, value
, q
);
1123 // Return a tri state value false->never make a tombstone, true->always make a
1124 // tombstone, NULL->make a tombstone, but delete it if the tombstone itself is not currently being synced.
1125 static CFBooleanRef
s3dl_should_make_tombstone(Query
*q
, bool item_is_syncable
, SecDbItemRef item
) {
1127 return q
->q_use_tomb
;
1128 else if (item_is_syncable
&& !SecDbItemIsTombstone(item
))
1131 return kCFBooleanFalse
;
1135 s3dl_query_update(SecDbConnectionRef dbt
, Query
*q
,
1136 CFDictionaryRef attributesToUpdate
, CFArrayRef accessGroups
, CFErrorRef
*error
)
1138 /* Sanity check the query. */
1139 if (query_match_count(q
) != 0)
1140 return SecError(errSecItemMatchUnsupported
, error
, CFSTR("match not supported in attributes to update"));
1142 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported in attributes to update"));
1143 if (q
->q_row_id
&& query_attr_count(q
))
1144 return SecError(errSecItemIllegalQuery
, error
, CFSTR("attributes to update illegal; both row_id and other attributes can't be updated at the same time"));
1145 if (q
->q_token_object_id
&& query_attr_count(q
) != 1)
1146 return SecError(errSecItemIllegalQuery
, error
, CFSTR("attributes to update illegal; both token persistent ref and other attributes can't be updated at the same time"));
1148 __block
bool result
= true;
1149 Query
*u
= query_create(q
->q_class
, NULL
, attributesToUpdate
, NULL
, error
);
1150 if (u
== NULL
) return false;
1151 require_action_quiet(query_update_parse(u
, attributesToUpdate
, error
), errOut
, result
= false);
1152 query_pre_update(u
);
1153 result
&= SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
1154 // Make sure we only update real items, not tombstones, unless the client explicitly asks otherwise.
1155 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
1156 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
1157 result
&= SecDbItemQuery(q
, accessGroups
, dbt
, error
, ^(SecDbItemRef item
, bool *stop
) {
1158 // We always need to know the error here.
1159 CFErrorRef localError
= NULL
;
1160 if (q
->q_token_object_id
) {
1161 const SecDbAttr
*valueDataAttr
= SecDbClassAttrWithKind(item
->class, kSecDbDataAttr
, NULL
);
1162 CFDataRef valueData
= SecDbItemGetValue(item
, valueDataAttr
, NULL
);
1163 if (q
->q_token_object_id
!= NULL
&& !checkTokenObjectID(q
->q_token_object_id
, valueData
))
1166 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1167 const SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
);
1168 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, NULL
));
1169 SecDbItemRef new_item
= SecDbItemCopyWithUpdates(item
, u
->q_item
, &localError
);
1170 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
1171 CFReleaseSafe(storedSHA1
);
1172 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
1173 // We just ignore this, and treat as if item is not found.
1174 secwarning("deleting corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, SecDbItemGetRowId(item
, NULL
), localError
);
1175 CFReleaseNull(localError
);
1176 if (!SecDbItemDelete(item
, dbt
, false, false, &localError
)) {
1177 secerror("failed to delete corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, SecDbItemGetRowId(item
, NULL
), localError
);
1178 CFReleaseNull(localError
);
1180 CFReleaseNull(new_item
);
1183 if (new_item
!= NULL
&& u
->q_access_control
!= NULL
)
1184 SecDbItemSetAccessControl(new_item
, u
->q_access_control
, &localError
);
1185 result
= SecErrorPropagate(localError
, error
) && new_item
;
1187 bool item_is_sync
= SecDbItemIsSyncable(item
);
1188 result
= SecDbItemUpdate(item
, new_item
, dbt
, s3dl_should_make_tombstone(q
, item_is_sync
, item
), q
->q_uuid_from_primary_key
, error
);
1190 q
->q_changed
= true;
1191 if (item_is_sync
|| SecDbItemIsSyncable(new_item
))
1192 q
->q_sync_changed
= true;
1194 CFRelease(new_item
);
1200 if (result
&& !q
->q_changed
)
1201 result
= SecError(errSecItemNotFound
, error
, CFSTR("No items updated"));
1203 if (!query_destroy(u
, error
))
1208 static bool SecDbItemNeedAuth(SecDbItemRef item
, CFErrorRef
*error
)
1210 CFErrorRef localError
= NULL
;
1211 if (!SecDbItemEnsureDecrypted(item
, true, &localError
) && localError
&& CFErrorGetCode(localError
) == errSecAuthNeeded
) {
1213 *error
= localError
;
1217 CFReleaseSafe(localError
);
1222 s3dl_query_delete(SecDbConnectionRef dbt
, Query
*q
, CFArrayRef accessGroups
, CFErrorRef
*error
)
1224 __block
bool ok
= true;
1225 __block
bool needAuth
= false;
1226 // Only delete things that aren't tombstones, unless the client explicitly asks otherwise.
1227 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
1228 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
1229 ok
&= SecDbItemSelect(q
, dbt
, error
, NULL
, ^bool(const SecDbAttr
*attr
) {
1231 },^bool(CFMutableStringRef sql
, bool *needWhere
) {
1232 SecDbAppendWhereClause(sql
, q
, accessGroups
);
1234 },^bool(sqlite3_stmt
* stmt
, int col
) {
1235 return sqlBindWhereClause(stmt
, q
, accessGroups
, &col
, error
);
1236 }, ^(SecDbItemRef item
, bool *stop
) {
1237 // Check if item for token persitence ref
1238 if (q
->q_token_object_id
) {
1239 const SecDbAttr
*valueDataAttr
= SecDbClassAttrWithKind(item
->class, kSecDbDataAttr
, NULL
);
1240 CFDataRef valueData
= SecDbItemGetValue(item
, valueDataAttr
, NULL
);
1241 if (q
->q_token_object_id
!= NULL
&& !checkTokenObjectID(q
->q_token_object_id
, valueData
))
1244 // Check if item need to be authenticated by LocalAuthentication
1245 item
->cryptoOp
= kAKSKeyOpDelete
;
1246 if (SecDbItemNeedAuth(item
, error
)) {
1250 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1251 const SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
);
1252 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, NULL
));
1253 bool item_is_sync
= SecDbItemIsSyncable(item
);
1254 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
1255 CFReleaseSafe(storedSHA1
);
1256 ok
= SecDbItemDelete(item
, dbt
, s3dl_should_make_tombstone(q
, item_is_sync
, item
), q
->q_tombstone_use_mdat_from_item
, error
);
1258 q
->q_changed
= true;
1260 q
->q_sync_changed
= true;
1263 if (ok
&& !q
->q_changed
&& !needAuth
) {
1264 ok
= SecError(errSecItemNotFound
, error
, CFSTR("Delete failed to delete anything"));
1266 return ok
&& !needAuth
;
1270 matchAnyString(CFStringRef needle
, CFStringRef
*haystack
)
1273 if (CFEqual(needle
, *haystack
))
1280 /* Return true iff the item in question should not be backed up, nor restored,
1281 but when restoring a backup the original version of the item should be
1282 added back to the keychain again after the restore completes. */
1283 bool SecItemIsSystemBound(CFDictionaryRef item
, const SecDbClass
*cls
, bool multiUser
) {
1284 CFNumberRef sysb
= CFDictionaryGetValue(item
, kSecAttrSysBound
);
1285 if (isNumber(sysb
)) {
1287 if (!CFNumberGetValue(sysb
, kCFNumberSInt32Type
, &num
))
1289 if (num
== kSecSecAttrSysBoundNot
) {
1291 } else if (num
== kSecSecAttrSysBoundPreserveDuringRestore
) {
1297 CFStringRef agrp
= CFDictionaryGetValue(item
, kSecAttrAccessGroup
);
1298 if (!isString(agrp
))
1301 if (CFEqualSafe(agrp
, kSOSInternalAccessGroup
)) {
1302 secdebug("backup", "found sysbound item: %@", item
);
1306 if (CFEqual(agrp
, CFSTR("lockdown-identities"))) {
1307 secdebug("backup", "found sys_bound item: %@", item
);
1311 if (CFEqual(agrp
, CFSTR("apple")) && cls
== genp_class()) {
1312 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1313 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
1315 if (isString(service
) && isString(account
)) {
1316 static CFStringRef mcAccounts
[] = {
1322 if (CFEqual(service
, CFSTR("com.apple.managedconfiguration"))
1323 && matchAnyString(account
, mcAccounts
))
1325 secdebug("backup", "found sys_bound item: %@", item
);
1330 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.account.CloudKit.token"))) {
1331 secdebug("backup", "found sys_bound item: %@", item
);
1335 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.account.idms.continuation-key"))) {
1336 secdebug("backup", "found sys_bound item: %@", item
);
1341 if (multiUser
&& CFEqual(agrp
, CFSTR("com.apple.apsd")) && cls
== genp_class()) {
1342 static CFStringRef pushServices
[] = {
1343 CFSTR("push.apple.com"),
1344 CFSTR("push.apple.com,PerAppToken.v0"),
1347 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1349 if (isString(service
) && matchAnyString(service
, pushServices
)) {
1350 secdebug("backup", "found sys_bound item: %@", item
);
1355 if (multiUser
&& CFEqual(agrp
, CFSTR("appleaccount")) && cls
== genp_class()) {
1356 static CFStringRef accountServices
[] = {
1357 CFSTR("com.apple.appleaccount.fmf.token"), /* temporary tokens while accout is being setup */
1358 CFSTR("com.apple.appleaccount.fmf.apptoken"),
1359 CFSTR("com.apple.appleaccount.fmip.siritoken"),
1360 CFSTR("com.apple.appleaccount.cloudkit.token"),
1363 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1365 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1366 secdebug("backup", "found exact sys_bound item: %@", item
);
1371 if (multiUser
&& CFEqual(agrp
, CFSTR("apple")) && cls
== genp_class()) {
1372 static CFStringRef accountServices
[] = {
1373 /* accounts, remove with rdar://37595482 */
1374 CFSTR("com.apple.account.AppleAccount.token"),
1375 CFSTR("com.apple.account.AppleAccount.password"),
1376 CFSTR("com.apple.account.AppleAccount.rpassword"),
1377 CFSTR("com.apple.account.idms.token"),
1378 CFSTR("com.apple.account.idms.heartbeat-token"),
1379 CFSTR("com.apple.account.idms.continuation-key"),
1380 CFSTR("com.apple.account.CloudKit.token"),
1381 CFSTR("com.apple.account.IdentityServices.password"), /* accountsd for ids */
1382 CFSTR("com.apple.account.IdentityServices.rpassword"),
1383 CFSTR("com.apple.account.IdentityServices.token"),
1385 CFSTR("BackupIDSAccountToken"),
1386 CFSTR("com.apple.ids"),
1391 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1393 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1394 secdebug("backup", "found exact sys_bound item: %@", item
);
1397 if (isString(service
) && CFStringHasPrefix(service
, CFSTR("com.apple.gs."))) {
1398 secdebug("backup", "found exact sys_bound item: %@", item
);
1401 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.facetime"))) {
1402 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
1403 if (isString(account
) && CFEqual(account
, CFSTR("registrationV1"))) {
1404 secdebug("backup", "found exact sys_bound item: %@", item
);
1410 /* accounts, remove with rdar://37595482 */
1411 if (multiUser
&& CFEqual(agrp
, CFSTR("com.apple.ind")) && cls
== genp_class()) {
1412 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1413 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.ind.registration"))) {
1414 secdebug("backup", "found exact sys_bound item: %@", item
);
1419 if (multiUser
&& CFEqual(agrp
, CFSTR("ichat")) && cls
== genp_class()) {
1420 static CFStringRef accountServices
[] = {
1424 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1426 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1427 secdebug("backup", "found exact sys_bound item: %@", item
);
1432 if (multiUser
&& CFEqual(agrp
, CFSTR("ichat")) && cls
== keys_class()) {
1433 static CFStringRef exactMatchingLabel
[] = {
1434 CFSTR("iMessage Encryption Key"),
1435 CFSTR("iMessage Signing Key"),
1437 CFStringRef label
= CFDictionaryGetValue(item
, kSecAttrLabel
);
1438 if (isString(label
)) {
1439 if (matchAnyString(label
, exactMatchingLabel
)) {
1440 secdebug("backup", "found exact sys_bound item: %@", item
);
1446 if (multiUser
&& CFEqual(agrp
, CFSTR("com.apple.rapport")) && cls
== genp_class()) {
1447 secdebug("backup", "found exact sys_bound item: %@", item
);
1451 secdebug("backup", "found non sys_bound item: %@", item
);
1455 /* Delete all items from the current keychain. If this is not an in
1456 place upgrade we don't delete items in the 'lockdown-identities'
1457 access group, this ensures that an import or restore of a backup
1458 will never overwrite an existing activation record. */
1459 static bool SecServerDeleteAll(SecDbConnectionRef dbt
, CFErrorRef
*error
) {
1460 secwarning("SecServerDeleteAll");
1462 return kc_transaction(dbt
, error
, ^{
1464 bool ok
= (SecDbExec(dbt
, CFSTR("DELETE from genp;"), error
) &&
1465 SecDbExec(dbt
, CFSTR("DELETE from inet;"), error
) &&
1466 SecDbExec(dbt
, CFSTR("DELETE from cert;"), error
) &&
1467 SecDbExec(dbt
, CFSTR("DELETE from keys;"), error
));
1472 #if TARGET_OS_IPHONE
1474 static bool DeleteAllFromTableForMUSRView(SecDbConnectionRef dbt
,
1480 sqlite3_stmt
*stmt
= NULL
;
1481 CFStringRef sql2
= NULL
;
1485 sql2
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@ AND pdmn NOT IN ('aku','akpu','cku','dku')"), sql
);
1487 sql2
= CFRetain(sql
);
1489 require(sql2
, fail
);
1491 stmt
= SecDbCopyStmt(dbt
, sql2
, NULL
, error
);
1492 require(stmt
, fail
);
1494 ok
= SecDbBindObject(stmt
, 1, musr
, error
);
1497 ok
= SecDbStep(dbt
, stmt
, error
, ^(bool *stop
) { });
1502 ok
= SecDbFinalize(stmt
, error
);
1505 secwarning("DeleteAllFromTableForMUSRView failed for %@ for musr: %@: %@", sql2
, musr
, error
? *error
: NULL
);
1508 CFReleaseNull(sql2
);
1513 bool SecServerDeleteAllForUser(SecDbConnectionRef dbt
, CFDataRef musrView
, bool keepU
, CFErrorRef
*error
) {
1514 secwarning("SecServerDeleteAllForUser for user: %@ keepU %s", musrView
, keepU
? "yes" : "no");
1516 return kc_transaction(dbt
, error
, ^{
1519 ok
= (DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM genp WHERE musr = ?"), musrView
, keepU
, error
) &&
1520 DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM inet WHERE musr = ?"), musrView
, keepU
, error
) &&
1521 DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM cert WHERE musr = ?"), musrView
, keepU
, error
) &&
1522 DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM keys WHERE musr = ?"), musrView
, keepU
, error
));
1529 OSStatus
SecServerDeleteForAppClipApplicationIdentifier(CFStringRef identifier
) {
1530 secnotice("item", "Request to delete app clip keychain items for identifier '%@'", identifier
);
1532 __block CFErrorRef cfError
= NULL
;
1533 __block
bool ok
= true;
1534 ok
&= kc_with_dbt(true, &cfError
, ^bool(SecDbConnectionRef dbt
) {
1535 return kc_transaction(dbt
, &cfError
, ^bool{
1536 const SecDbSchema
* schema
= current_schema();
1537 for (const SecDbClass
*const * class = schema
->classes
; *class != NULL
; ++class) {
1538 if ((*class)->itemclass
) {
1539 CFStringRef sqlStr
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("DELETE FROM %@ WHERE agrp = ? AND clip = 1"), (*class)->name
);
1540 ok
&= SecDbPrepare(dbt
, sqlStr
, &cfError
, ^(sqlite3_stmt
*stmt
) {
1541 ok
&= SecDbBindObject(stmt
, 1, identifier
, &cfError
);
1542 ok
&= SecDbStep(dbt
, stmt
, &cfError
, NULL
);
1544 CFReleaseNull(sqlStr
);
1551 OSStatus status
= ok
? errSecSuccess
: errSecInternal
;
1552 secnotice("item", "Finished request to delete app clip keychain items for identifier '%@' with status %i: %@", identifier
, (int)status
, cfError
);
1553 CFReleaseNull(cfError
);
1558 struct s3dl_export_row_ctx
{
1559 struct s3dl_query_ctx qc
;
1560 keybag_handle_t dest_keybag
;
1561 enum SecItemFilter filter
;
1565 static void s3dl_export_row(sqlite3_stmt
*stmt
, void *context
) {
1566 struct s3dl_export_row_ctx
*c
= context
;
1568 SecAccessControlRef access_control
= NULL
;
1569 CFErrorRef localError
= NULL
;
1571 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
1572 CFMutableDictionaryRef allAttributes
= NULL
;
1573 CFMutableDictionaryRef metadataAttributes
= NULL
;
1574 CFMutableDictionaryRef secretStuff
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1575 keyclass_t keyclass
= 0;
1576 bool ok
= s3dl_item_from_col(stmt
, q
, 1, c
->qc
.accessGroups
, &allAttributes
, &access_control
, &keyclass
, &localError
);
1579 metadataAttributes
= CFDictionaryCreateMutableCopy(NULL
, 0, allAttributes
);
1580 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbReturnDataFlag
) {
1581 CFTypeRef value
= CFDictionaryGetValue(metadataAttributes
, desc
->name
);
1583 CFDictionarySetValue(secretStuff
, desc
->name
, value
);
1584 CFDictionaryRemoveValue(metadataAttributes
, desc
->name
);
1589 /* Skip akpu items when backing up, those are intentionally lost across restores. The same applies to SEP-based keys */
1590 bool is_akpu
= access_control
? CFEqualSafe(SecAccessControlGetProtection(access_control
), kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
)
1591 // Mask generation, only look at class per se
1592 : (keyclass
& key_class_last
) == key_class_akpu
;
1593 bool is_token
= (ok
&& allAttributes
!= NULL
) ? CFDictionaryContainsKey(allAttributes
, kSecAttrTokenID
) : false;
1594 bool skip_akpu_or_token
= (is_akpu
|| is_token
) && c
->filter
== kSecBackupableItemFilter
;
1596 if (ok
&& allAttributes
&& !skip_akpu_or_token
) {
1597 /* Only export sysbound items if do_sys_bound is true, only export non sysbound items otherwise. */
1598 bool do_sys_bound
= c
->filter
== kSecSysBoundItemFilter
;
1599 if (c
->filter
== kSecNoItemFilter
||
1600 SecItemIsSystemBound(allAttributes
, q
->q_class
, c
->multiUser
) == do_sys_bound
) {
1601 /* Re-encode the item. */
1602 secdebug("item", "export rowid %llu item: %@", rowid
, allAttributes
);
1603 /* The code below could be moved into handle_row. */
1604 CFDataRef pref
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, allAttributes
);
1606 if (c
->dest_keybag
!= KEYBAG_NONE
) {
1607 CFMutableDictionaryRef auth_attribs
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1608 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInAuthenticatedDataFlag
) {
1609 CFTypeRef value
= CFDictionaryGetValue(metadataAttributes
, desc
->name
);
1611 CFDictionaryAddValue(auth_attribs
, desc
->name
, value
);
1612 CFDictionaryRemoveValue(metadataAttributes
, desc
->name
);
1616 /* Encode and encrypt the item to the specified keybag. */
1617 CFDataRef edata
= NULL
;
1618 bool encrypted
= ks_encrypt_data(c
->dest_keybag
, access_control
, q
->q_use_cred_handle
, secretStuff
, metadataAttributes
, auth_attribs
, &edata
, false, &q
->q_error
);
1619 CFDictionaryRemoveAllValues(allAttributes
);
1620 CFRelease(auth_attribs
);
1622 CFDictionarySetValue(allAttributes
, kSecValueData
, edata
);
1623 CFReleaseSafe(edata
);
1625 seccritical("ks_encrypt_data %@,rowid=%" PRId64
": failed: %@", q
->q_class
->name
, rowid
, q
->q_error
);
1626 CFReleaseNull(q
->q_error
);
1629 if (CFDictionaryGetCount(allAttributes
)) {
1630 CFDictionarySetValue(allAttributes
, kSecValuePersistentRef
, pref
);
1631 CFArrayAppendValue((CFMutableArrayRef
)c
->qc
.result
, allAttributes
);
1634 CFReleaseSafe(pref
);
1637 } else if (!ok
|| !allAttributes
) {
1638 OSStatus status
= SecErrorGetOSStatus(localError
);
1640 if (status
== errSecInteractionNotAllowed
&& is_akpu
) {
1641 if (skip_akpu_or_token
) {
1642 secdebug("item", "Skipping akpu item for backup");
1643 } else { // Probably failed to decrypt sysbound item. Should never be an akpu item in backup.
1644 secerror("Encountered akpu item we cannot export (filter %d), skipping. %@", c
->filter
, localError
);
1645 if (sqlite3_column_count(stmt
) > 2) { // Should have rowid,data,agrp from s3dl_create_select_sql
1646 CFStringRef agrp
= CFStringCreateWithCString(kCFAllocatorDefault
, (const char*)sqlite3_column_text(stmt
, 2), kCFStringEncodingUTF8
);
1648 CFDictionaryRef payload
= CFDictionaryCreateForCFTypes(NULL
, CFSTR("agrp"), agrp
, NULL
);
1649 SecABCTrigger(CFSTR("keychain"), CFSTR("invalid-akpu+sysbound"), NULL
, payload
);
1650 CFReleaseNull(payload
);
1652 CFReleaseNull(agrp
);
1655 // We expect akpu items to be inaccessible when the device is locked.
1656 CFReleaseNull(localError
);
1658 /* This happens a lot when trying to migrate keychain before first unlock, so only a notice */
1659 /* If the error is "corrupted item" then we just ignore it, otherwise we save it in the query */
1660 secinfo("item","Could not export item for rowid %llu: %@", rowid
, localError
);
1662 if (status
== errSecDecode
) {
1663 CFReleaseNull(localError
);
1665 CFReleaseSafe(q
->q_error
);
1666 q
->q_error
= localError
;
1670 secnotice("item", "export rowid %llu skipped. akpu/token: %i", rowid
, skip_akpu_or_token
);
1673 CFReleaseNull(access_control
);
1674 CFReleaseNull(allAttributes
);
1675 CFReleaseNull(metadataAttributes
);
1676 CFReleaseNull(secretStuff
);
1680 SecCreateKeybagUUID(keybag_handle_t keybag
)
1682 #if !TARGET_HAS_KEYSTORE
1687 if (aks_get_bag_uuid(keybag
, uuid
) != KERN_SUCCESS
)
1689 uuid_unparse_lower(uuid
, uuidstr
);
1690 return CFStringCreateWithCString(NULL
, uuidstr
, kCFStringEncodingUTF8
);
1695 SecServerCopyKeychainPlist(SecDbConnectionRef dbt
,
1696 SecurityClient
*client
,
1697 keybag_handle_t src_keybag
,
1698 keybag_handle_t dest_keybag
,
1699 enum SecItemFilter filter
,
1700 CFErrorRef
*error
) {
1701 CFMutableDictionaryRef keychain
;
1702 keychain
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1703 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1705 bool inMultiUser
= false;
1706 CFStringRef keybaguuid
= NULL
;
1707 Query q
= { .q_keybag
= src_keybag
,
1712 if (error
&& !*error
)
1713 SecError(errSecAllocate
, error
, CFSTR("Can't create keychain dictionary"));
1718 kSecReturnDataMask
|
1719 kSecReturnAttributesMask
|
1720 kSecReturnPersistentRefMask
;
1721 q
.q_limit
= kSecMatchUnlimited
;
1722 q
.q_skip_acl_items
= true;
1723 q
.q_skip_app_clip_items
= true;
1725 #if TARGET_OS_IPHONE
1726 if (client
&& client
->inMultiUser
) {
1727 q
.q_musrView
= SecMUSRCreateActiveUserUUID(client
->uid
);
1732 q
.q_musrView
= SecMUSRGetSingleUserKeychainUUID();
1733 CFRetain(q
.q_musrView
);
1735 keybaguuid
= SecCreateKeybagUUID(dest_keybag
);
1737 CFDictionarySetValue(keychain
, kSecBackupKeybagUUIDKey
, keybaguuid
);
1741 /* Get rid of this duplicate. */
1742 const SecDbClass
*SecDbClasses
[] = {
1749 for (class_ix
= 0; class_ix
< array_size(SecDbClasses
);
1751 q
.q_class
= SecDbClasses
[class_ix
];
1752 struct s3dl_export_row_ctx ctx
= {
1753 .qc
= { .q
= &q
, .dbt
= dbt
},
1754 .dest_keybag
= dest_keybag
, .filter
= filter
,
1755 .multiUser
= inMultiUser
,
1758 secnotice("item", "exporting %ssysbound class '%@'", filter
!= kSecSysBoundItemFilter
? "non-" : "", q
.q_class
->name
);
1760 CFErrorRef localError
= NULL
;
1761 if (s3dl_query(s3dl_export_row
, &ctx
, &localError
)) {
1762 secnotice("item", "exporting class '%@' complete", q
.q_class
->name
);
1763 if (CFArrayGetCount(ctx
.qc
.result
)) {
1764 SecSignpostBackupCount(SecSignpostImpulseBackupClassCount
, q
.q_class
->name
, CFArrayGetCount(ctx
.qc
.result
), filter
);
1765 CFDictionaryAddValue(keychain
, q
.q_class
->name
, ctx
.qc
.result
);
1769 OSStatus status
= (OSStatus
)CFErrorGetCode(localError
);
1770 if (status
== errSecItemNotFound
) {
1771 secnotice("item", "exporting class '%@' complete (no items)", q
.q_class
->name
);
1772 CFRelease(localError
);
1774 secerror("exporting class '%@' failed: %@", q
.q_class
->name
, localError
);
1776 CFReleaseSafe(*error
);
1777 *error
= localError
;
1779 CFRelease(localError
);
1781 CFReleaseNull(keychain
);
1782 CFReleaseNull(ctx
.qc
.result
);
1786 CFReleaseNull(ctx
.qc
.result
);
1790 CFReleaseNull(q
.q_musrView
);
1791 CFReleaseNull(keybaguuid
);
1796 struct SecServerImportClassState
{
1797 SecDbConnectionRef dbt
;
1799 keybag_handle_t src_keybag
;
1800 keybag_handle_t dest_keybag
;
1801 SecurityClient
*client
;
1802 enum SecItemFilter filter
;
1805 struct SecServerImportItemState
{
1806 const SecDbClass
*class;
1807 struct SecServerImportClassState
*s
;
1811 SecServerImportItem(const void *value
, void *context
)
1813 struct SecServerImportItemState
*state
= (struct SecServerImportItemState
*)context
;
1814 bool inMultiUser
= false;
1815 #if TARGET_OS_IPHONE
1816 if (state
->s
->client
->inMultiUser
)
1820 if (state
->s
->error
)
1823 if (!isDictionary(value
)) {
1824 SecError(errSecParam
, &state
->s
->error
, CFSTR("value %@ is not a dictionary"), value
);
1828 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
1830 secdebug("item", "Import Item : %@", dict
);
1832 SecDbItemRef item
= NULL
;
1834 /* This is sligthly confusing:
1835 - During upgrade all items are exported with KEYBAG_NONE.
1836 - During restore from backup, existing sys_bound items are exported with KEYBAG_NONE, and are exported as dictionary of attributes.
1837 - Item in the actual backup are export with a real keybag, and are exported as encrypted v_Data and v_PersistentRef
1839 if (state
->s
->src_keybag
== KEYBAG_NONE
) {
1840 item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, state
->class, dict
, state
->s
->dest_keybag
, &state
->s
->error
);
1842 item
= SecDbItemCreateWithBackupDictionary(state
->class, dict
, state
->s
->src_keybag
, state
->s
->dest_keybag
, &state
->s
->error
);
1845 /* If item is NULL here, control flow ends up at the end where error is cleared. */
1846 if (item
&& !SecDbItemEnsureDecrypted(item
, true, &state
->s
->error
)) {
1847 secdebug("item", "Failed to import item because of decryption failure: %@", state
->s
->error
);
1848 CFReleaseNull(item
);
1849 /* No early return; as just above, go to the end where error is cleared. */
1852 /* We use the kSecSysBoundItemFilter to indicate that we don't
1853 * preserve rowid's during import.
1855 if (item
&& item
->attributes
&& state
->s
->filter
== kSecBackupableItemFilter
) {
1858 /* We don't filter non sys_bound items during import since we know we
1859 * will never have any in this case.
1861 if (SecItemIsSystemBound(item
->attributes
, state
->class, inMultiUser
)) {
1862 secdebug("item", "skipping backup of item: %@", dict
);
1863 CFReleaseNull(item
);
1868 * Don't bother with u items when in edu mode since our current backup system
1869 * don't keep track of items that blongs to the device (u) but rather just
1870 * merge them into one blob.
1872 if (inMultiUser
&& (pdmu
= CFDictionaryGetValue(item
->attributes
, kSecAttrAccessible
))) {
1873 if (CFEqual(pdmu
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) ||
1874 CFEqual(pdmu
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
) ||
1875 CFEqual(pdmu
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) ||
1876 CFEqual(pdmu
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
))
1878 secdebug("item", "Skipping KU item : %@", dict
);
1879 CFReleaseNull(item
);
1884 /* Avoid importing token-based items. Although newer backups should not have them,
1885 * older (iOS9, iOS10.0) produced backups with token-based items.
1887 if (CFDictionaryContainsKey(item
->attributes
, kSecAttrTokenID
)) {
1888 secdebug("item", "Skipping token-based item : %@", dict
);
1889 CFReleaseNull(item
);
1898 if (item
&& item
->attributes
) {
1899 CFDataRef musr
= NULL
;
1900 CFDataRef musrBackup
= CFDictionaryGetValue(item
->attributes
, kSecAttrMultiUser
);
1901 CFDataRef systemKeychainUUID
= SecMUSRGetSystemKeychainUUID();
1902 bool systemKeychain
= CFEqualSafe(musrBackup
, systemKeychainUUID
);
1904 #if TARGET_OS_IPHONE
1905 if (state
->s
->client
&& state
->s
->client
->inMultiUser
) {
1906 if (systemKeychain
) {
1907 secwarning("system keychain not allowed in multi user mode for item: %@", item
);
1909 musr
= SecMUSRCreateActiveUserUUID(state
->s
->client
->uid
);
1914 if (systemKeychain
) {
1915 musr
= SecMUSRCopySystemKeychainUUID();
1917 musr
= SecMUSRGetSingleUserKeychainUUID();
1922 CFReleaseNull(item
);
1924 SecDbItemSetValueWithName(item
, CFSTR("musr"), musr
, &state
->s
->error
);
1936 if(state
->s
->filter
!= kSecSysBoundItemFilter
) {
1937 SecDbItemExtractRowIdFromBackupDictionary(item
, dict
, &state
->s
->error
);
1939 SecDbItemInferSyncable(item
, &state
->s
->error
);
1940 insertStatus
= SecDbItemInsert(item
, state
->s
->dbt
, false, &state
->s
->error
);
1941 if (!insertStatus
) {
1943 When running in EduMode, multiple users share the same
1944 keychain and unfortionaly the rowid is used a
1945 persistant reference and is part of the contraints (its
1946 UNIQUE), so lets clear the rowid and try to insert the
1949 This even happens for normal operation because of
1950 SysBound entries, so in case of a failure, lets try
1951 again to insert the record.
1953 SecDbItemClearRowId(item
, NULL
);
1954 SecDbItemInsert(item
, state
->s
->dbt
, false, &state
->s
->error
);
1958 /* Reset error if we had one, since we just skip the current item
1959 and continue importing what we can. */
1960 if (state
->s
->error
) {
1961 secwarning("Failed to import an item (%@) of class '%@': %@ - ignoring error.",
1962 item
, state
->class->name
, state
->s
->error
);
1963 CFReleaseNull(state
->s
->error
);
1966 CFReleaseSafe(item
);
1969 static void SecServerImportClass(const void *key
, const void *value
,
1971 struct SecServerImportClassState
*state
=
1972 (struct SecServerImportClassState
*)context
;
1975 if (!isString(key
)) {
1976 SecError(errSecParam
, &state
->error
, CFSTR("class name %@ is not a string"), key
);
1979 /* ignore the Keybag UUID */
1980 if (CFEqual(key
, kSecBackupKeybagUUIDKey
))
1982 const SecDbClass
*class = kc_class_with_name(key
);
1984 secwarning("Ignoring unknown key class '%@'", key
);
1987 if (class == identity_class()) {
1988 SecError(errSecParam
, &state
->error
, CFSTR("attempt to import an identity"));
1991 struct SecServerImportItemState item_state
= {
1992 .class = class, .s
= state
,
1994 if (isArray(value
)) {
1995 CFArrayRef items
= (CFArrayRef
)value
;
1996 secwarning("Import %ld items of class %@ (filter %d)", (long)CFArrayGetCount(items
), key
, state
->filter
);
1997 SecSignpostBackupCount(SecSignpostImpulseRestoreClassCount
, class->name
, CFArrayGetCount(items
), state
->filter
);
1998 CFArrayApplyFunction(items
, CFRangeMake(0, CFArrayGetCount(items
)),
1999 SecServerImportItem
, &item_state
);
2000 } else if (isDictionary(value
)) {
2001 CFDictionaryRef item
= (CFDictionaryRef
)value
;
2002 secwarning("Import %ld items of class %@ (filter %d)", (long)1, key
, state
->filter
);
2003 SecSignpostBackupCount(SecSignpostImpulseRestoreClassCount
, class->name
, 1, state
->filter
);
2004 SecServerImportItem(item
, &item_state
);
2006 secwarning("Unknown value type for class %@ (filter %d)", key
, state
->filter
);
2010 bool SecServerImportKeychainInPlist(SecDbConnectionRef dbt
, SecurityClient
*client
,
2011 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
2012 CFDictionaryRef keychain
, enum SecItemFilter filter
,
2013 bool removeKeychainContent
, CFErrorRef
*error
) {
2014 CFStringRef keybaguuid
= NULL
;
2017 CFDictionaryRef sys_bound
= NULL
;
2018 if (filter
== kSecBackupableItemFilter
) {
2019 /* Grab a copy of all the items for which SecItemIsSystemBound()
2021 require(sys_bound
= SecServerCopyKeychainPlist(dbt
, client
, KEYBAG_DEVICE
,
2022 KEYBAG_NONE
, kSecSysBoundItemFilter
,
2027 * Validate the uuid of the source keybag matches what we have in the backup
2029 keybaguuid
= SecCreateKeybagUUID(src_keybag
);
2031 CFStringRef uuid
= CFDictionaryGetValue(keychain
, kSecBackupKeybagUUIDKey
);
2032 if (isString(uuid
)) {
2033 require_action(CFEqual(keybaguuid
, uuid
), errOut
,
2034 SecError(errSecDecode
, error
, CFSTR("Keybag UUID (%@) mismatch with backup (%@)"),
2041 * Shared iPad is very special, it always delete's the user keychain, and never merge content
2043 if (client
->inMultiUser
) {
2044 CFDataRef musrView
= SecMUSRCreateActiveUserUUID(client
->uid
);
2045 require_action(musrView
, errOut
, ok
= false);
2046 require_action(ok
= SecServerDeleteAllForUser(dbt
, musrView
, true, error
), errOut
, CFReleaseNull(musrView
));
2047 CFReleaseNull(musrView
);
2052 * Delete everything in the keychain.
2053 * We don't want this if we're restoring backups because we probably already synced stuff over
2055 if (removeKeychainContent
) {
2056 require(ok
= SecServerDeleteAll(dbt
, error
), errOut
);
2058 // Custom hack to support bluetooth's workflow for 11.3. Should be removed in a future release.
2059 __block CFErrorRef btError
= NULL
;
2060 bool deletedBT
= kc_transaction(dbt
, &btError
, ^bool{
2062 #define EXCLUDE_AGRPS "'com.apple.security.sos', 'com.apple.security.sos-usercredential', 'com.apple.security.ckks', 'com.apple.security.egoIdentities', 'com.apple.security.octagon'"
2064 bool tok
= SecDbExec(dbt
, CFSTR("DELETE FROM genp WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS
");"), &btError
);
2065 tok
&= SecDbExec(dbt
, CFSTR("DELETE FROM inet WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS
");"), &btError
);
2066 tok
&= SecDbExec(dbt
, CFSTR("DELETE FROM cert WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS
");"), &btError
);
2067 tok
&= SecDbExec(dbt
, CFSTR("DELETE FROM keys WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS
");"), &btError
);
2069 #undef EXCLUDE_AGRPS
2073 secerror("Unable to delete nonsyncable items prior to keychain restore: %@", btError
);
2075 secnotice("restore", "Successfully deleted nonsyncable items");
2077 CFReleaseNull(btError
);
2081 struct SecServerImportClassState state
= {
2083 .src_keybag
= src_keybag
,
2084 .dest_keybag
= dest_keybag
,
2088 /* Import the provided items, preserving rowids. */
2089 secwarning("Restoring backup items '%ld'", (long)CFDictionaryGetCount(keychain
));
2090 CFDictionaryApplyFunction(keychain
, SecServerImportClass
, &state
);
2093 state
.src_keybag
= KEYBAG_NONE
;
2094 /* Import the items we preserved with random rowids. */
2095 state
.filter
= kSecSysBoundItemFilter
;
2096 secwarning("Restoring sysbound items '%ld'", (long)CFDictionaryGetCount(sys_bound
));
2097 CFDictionaryApplyFunction(sys_bound
, SecServerImportClass
, &state
);
2101 CFReleaseSafe(*error
);
2102 *error
= state
.error
;
2104 CFRelease(state
.error
);
2109 // If CKKS had spun up, it's very likely that we just deleted its data.
2110 // Tell it to perform a local resync.
2112 SecCKKSPerformLocalResync();
2116 CFReleaseSafe(sys_bound
);
2117 CFReleaseSafe(keybaguuid
);
2123 SecServerBackupGetKeybagUUID(CFDictionaryRef keychain
, CFErrorRef
*error
)
2125 CFStringRef uuid
= CFDictionaryGetValue(keychain
, kSecBackupKeybagUUIDKey
);
2126 if (!isString(uuid
)) {
2127 SecError(errSecDecode
, error
, CFSTR("Missing or invalid %@ in backup dictionary"), kSecBackupKeybagUUIDKey
);
2133 #pragma mark - key rolling support
2136 struct check_generation_ctx
{
2137 struct s3dl_query_ctx query_ctx
;
2138 uint32_t current_generation
;
2141 static void check_generation(sqlite3_stmt
*stmt
, void *context
) {
2142 struct check_generation_ctx
*c
= context
;
2143 CFDataRef blob
= NULL
;
2145 const uint8_t *cursor
= NULL
;
2147 keyclass_t keyclass
;
2148 uint32_t current_generation
= c
->current_generation
;
2150 require(blob
= s3dl_copy_data_from_col(stmt
, 1, &c
->query_ctx
.q
->q_error
), out
);
2151 blobLen
= CFDataGetLength(blob
);
2152 cursor
= CFDataGetBytePtr(blob
);
2154 /* Check for underflow, ensuring we have at least one full AES block left. */
2155 if (blobLen
< sizeof(version
) + sizeof(keyclass
)) {
2156 SecError(errSecDecode
, &c
->query_ctx
.q
->q_error
, CFSTR("check_generation: Check for underflow"));
2160 version
= *((uint32_t *)cursor
);
2161 cursor
+= sizeof(version
);
2163 (void) version
; // TODO: do something with the version number.
2165 keyclass
= *((keyclass_t
*)cursor
);
2167 // TODO: export get_key_gen macro
2168 if (((keyclass
& ~key_class_last
) == 0) != (current_generation
== 0)) {
2169 c
->query_ctx
.found
++;
2172 CFReleaseSafe(blob
);
2176 c
->query_ctx
.found
++;
2177 CFReleaseSafe(blob
);
2180 bool s3dl_dbt_keys_current(SecDbConnectionRef dbt
, uint32_t current_generation
, CFErrorRef
*error
) {
2181 CFErrorRef localError
= NULL
;
2182 struct check_generation_ctx ctx
= { .query_ctx
= { .dbt
= dbt
}, .current_generation
= current_generation
};
2184 const SecDbClass
*classes
[] = {
2191 for (size_t class_ix
= 0; class_ix
< array_size(classes
); ++class_ix
) {
2192 Query
*q
= query_create(classes
[class_ix
], NULL
, NULL
, NULL
, &localError
);
2196 ctx
.query_ctx
.q
= q
;
2197 q
->q_limit
= kSecMatchUnlimited
;
2199 bool ok
= s3dl_query(check_generation
, &ctx
, &localError
);
2200 query_destroy(q
, NULL
);
2201 CFReleaseNull(ctx
.query_ctx
.result
);
2203 if (!ok
&& localError
&& (CFErrorGetCode(localError
) == errSecItemNotFound
)) {
2204 CFReleaseNull(localError
);
2207 secerror("Class %@ not up to date", classes
[class_ix
]->name
);
2213 bool s3dl_dbt_update_keys(SecDbConnectionRef dbt
, SecurityClient
*client
, CFErrorRef
*error
) {
2214 return SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
2215 __block
bool ok
= false;
2216 uint32_t keystore_generation_status
;
2218 /* can we migrate to new class keys right now? */
2219 if (!aks_generation(KEYBAG_DEVICE
, generation_noop
, &keystore_generation_status
) &&
2220 (keystore_generation_status
& generation_change_in_progress
)) {
2222 /* take a lock assertion */
2223 bool operated_while_unlocked
= SecAKSDoWithUserBagLockAssertion(error
, ^{
2224 CFErrorRef localError
= NULL
;
2225 CFDictionaryRef backup
= SecServerCopyKeychainPlist(dbt
, NULL
,
2226 KEYBAG_DEVICE
, KEYBAG_NONE
, kSecNoItemFilter
, &localError
);
2229 secerror("Ignoring export error: %@ during roll export", localError
);
2230 CFReleaseNull(localError
);
2232 // 'true' argument: we're replacing everything with newly wrapped entries so remove the old stuff
2233 ok
= SecServerImportKeychainInPlist(dbt
, client
, KEYBAG_NONE
,
2234 KEYBAG_DEVICE
, backup
, kSecNoItemFilter
, true, &localError
);
2236 secerror("Ignoring export error: %@ during roll export", localError
);
2237 CFReleaseNull(localError
);
2242 if (!operated_while_unlocked
)
2245 ok
= SecError(errSecBadReq
, error
, CFSTR("No key roll in progress."));