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 ok
= SecDbItemInsert(item
, dbt
, error
);
320 if (result
&& q
->q_return_type
) {
321 *result
= SecDbItemCopyResult(item
, q
->q_return_type
, error
);
324 if (!ok
&& error
&& *error
) {
325 if (CFEqual(CFErrorGetDomain(*error
), kSecDbErrorDomain
) && CFErrorGetCode(*error
) == SQLITE_CONSTRAINT
) {
326 CFReleaseNull(*error
);
327 SecError(errSecDuplicateItem
, error
, CFSTR("duplicate item %@"), item
);
328 } else if (CFEqual(CFErrorGetDomain(*error
), kSecErrorDomain
) && CFErrorGetCode(*error
) == errSecDecode
) { //handle situation when item have pdmn=akpu but passcode is not set
329 CFTypeRef value
= SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbAccessAttr
, error
), error
);
330 if (value
&& CFEqual(value
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
)) {
331 CFReleaseNull(*error
);
332 SecError(errSecAuthFailed
, error
, CFSTR("authentication failed"));
339 if (SecDbItemIsSyncable(item
))
340 q
->q_sync_changed
= true;
343 secdebug("dbitem", "inserting item %@%s%@", item
, ok
? "" : "failed: ", ok
|| error
== NULL
? (CFErrorRef
)CFSTR("") : *error
);
350 bool s3dl_item_make_new_uuid(SecDbItemRef item
, bool uuid_from_primary_key
, CFErrorRef
* error
) {
355 // Set the item UUID.
356 CFUUIDRef uuid
= NULL
;
357 // Were we asked to make the UUID static?
358 if (uuid_from_primary_key
) {
359 // This UUID isn't used in any security mechanism, so we can
360 // just use the first bits of the SHA256 hash.
361 CFDataRef pkhash
= SecDbKeychainItemCopySHA256PrimaryKey(item
, error
);
362 if(CFDataGetLength(pkhash
) >= 16) {
364 CFRange range
= CFRangeMake(0, 16);
365 CFDataGetBytes(pkhash
, range
, uuidBytes
);
367 uuid
= CFUUIDCreateWithBytes(NULL
,
385 CFReleaseNull(pkhash
);
388 uuid
= CFUUIDCreate(NULL
);
390 SecDbItemSetValueWithName(item
, kSecAttrUUID
, uuid
, error
);
395 typedef void (*s3dl_handle_row
)(sqlite3_stmt
*stmt
, void *context
);
398 s3dl_copy_data_from_col(sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
399 return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt
, col
),
400 sqlite3_column_bytes(stmt
, col
),
405 s3dl_item_from_col(sqlite3_stmt
*stmt
, Query
*q
, int col
, CFArrayRef accessGroups
,
406 CFMutableDictionaryRef
*item
, SecAccessControlRef
*access_control
, keyclass_t
* keyclass
, CFErrorRef
*error
) {
407 CFDataRef edata
= NULL
;
409 require(edata
= s3dl_copy_data_from_col(stmt
, col
, error
), out
);
410 ok
= s3dl_item_from_data(edata
, q
, accessGroups
, item
, access_control
, keyclass
, error
);
413 CFReleaseSafe(edata
);
417 struct s3dl_query_ctx
{
419 CFArrayRef accessGroups
;
420 SecDbConnectionRef dbt
;
425 /* Return whatever the caller requested based on the value of q->q_return_type.
426 keys and values must be 3 larger than attr_count in size to accomadate the
427 optional data, class and persistent ref results. This is so we can use
428 the CFDictionaryCreate() api here rather than appending to a
429 mutable dictionary. */
430 static CF_RETURNS_RETAINED CFTypeRef
431 handle_result(Query
*q
,
432 CFMutableDictionaryRef item
,
437 data
= CFDictionaryGetValue(item
, kSecValueData
);
438 CFDataRef pref
= NULL
;
439 if (q
->q_return_type
& kSecReturnPersistentRefMask
) {
440 pref
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, item
);
442 if (q
->q_return_type
== 0) {
443 /* Caller isn't interested in any results at all. */
445 } else if (q
->q_return_type
== kSecReturnDataMask
) {
450 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
452 } else if (q
->q_return_type
== kSecReturnPersistentRefMask
) {
453 a_result
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, item
);
455 /* We need to return more than one value. */
456 if (q
->q_return_type
& kSecReturnRefMask
) {
457 CFDictionarySetValue(item
, kSecClass
, q
->q_class
->name
);
458 } else if ((q
->q_return_type
& kSecReturnAttributesMask
)) {
459 if (!(q
->q_return_type
& kSecReturnDataMask
)) {
460 CFDictionaryRemoveValue(item
, kSecValueData
);
463 // Add any attributes which are supposed to be returned, are not present in the decrypted blob,
464 // and have a way to generate themselves.
465 SecDbItemRef itemRef
= NULL
;
466 SecDbForEachAttrWithMask(q
->q_class
, attr
, kSecDbReturnAttrFlag
) {
467 if(!CFDictionaryGetValue(item
, attr
->name
) && attr
->copyValue
) {
468 CFErrorRef cferror
= NULL
;
470 itemRef
= SecDbItemCreateWithAttributes(NULL
, q
->q_class
, item
, KEYBAG_DEVICE
, &cferror
);
472 if(!cferror
&& itemRef
) {
473 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
474 CFTypeRef attrValue
= attr
->copyValue(itemRef
, attr
, &cferror
);
475 if (!cferror
&& attrValue
) {
476 CFDictionarySetValue(item
, attr
->name
, attrValue
);
478 CFReleaseNull(attrValue
);
481 CFReleaseNull(cferror
);
484 CFReleaseNull(itemRef
);
486 CFDictionaryRemoveValue(item
, kSecAttrUUID
);
489 CFDictionaryRemoveAllValues(item
);
490 if ((q
->q_return_type
& kSecReturnDataMask
) && data
) {
491 CFDictionarySetValue(item
, kSecValueData
, data
);
495 if (q
->q_return_type
& kSecReturnPersistentRefMask
&& pref
!= NULL
) {
496 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
506 static void s3dl_merge_into_dict(const void *key
, const void *value
, void *context
) {
507 CFDictionarySetValue(context
, key
, value
);
510 static bool checkTokenObjectID(CFDataRef token_object_id
, CFDataRef value_data
) {
511 bool equalOID
= false;
512 require_quiet(value_data
, out
);
513 CFDictionaryRef itemValue
= SecTokenItemValueCopy(value_data
, NULL
);
514 require_quiet(itemValue
, out
);
515 CFDataRef oID
= CFDictionaryGetValue(itemValue
, kSecTokenValueObjectIDKey
);
516 equalOID
= CFEqualSafe(token_object_id
, oID
);
517 CFRelease(itemValue
);
522 static void s3dl_query_row(sqlite3_stmt
*stmt
, void *context
) {
523 struct s3dl_query_ctx
*c
= context
;
525 ReturnTypeMask saved_mask
= q
->q_return_type
;
527 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
528 CFMutableDictionaryRef item
= NULL
;
532 ok
= s3dl_item_from_col(stmt
, q
, 1, c
->accessGroups
, &item
, NULL
, NULL
, &q
->q_error
);
534 OSStatus status
= SecErrorGetOSStatus(q
->q_error
);
535 // errSecDecode means the item is corrupted, stash it for delete.
536 if (status
== errSecDecode
) {
537 secwarning("ignoring corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, rowid
, q
->q_error
);
539 CFStringRef tablename
= CFStringCreateCopy(kCFAllocatorDefault
, q
->q_class
->name
);
540 // Can't get rid of this item on the read path. Let's come back from elsewhere.
541 dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT
, 0), ^{
542 __block CFErrorRef localErr
= NULL
;
543 __block
bool ok
= true;
544 ok
&= kc_with_dbt(true, &localErr
, ^bool(SecDbConnectionRef dbt
) {
545 CFStringRef sql
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("DELETE FROM %@ WHERE rowid=%lli"), tablename
, rowid
);
546 ok
&= SecDbPrepare(dbt
, sql
, &localErr
, ^(sqlite3_stmt
*stmt
) {
547 ok
&= SecDbStep(dbt
, stmt
, &localErr
, NULL
);
550 if (!ok
|| localErr
) {
551 secerror("Failed to delete corrupt item, %@ row %lli: %@", tablename
, rowid
, localErr
);
553 secnotice("item", "Deleted corrupt rowid %lli from table %@", rowid
, tablename
);
555 CFReleaseNull(localErr
);
557 CFReleaseSafe(tablename
);
562 CFDataRef edata
= s3dl_copy_data_from_col(stmt
, 1, NULL
);
563 CFMutableStringRef edatastring
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
565 CFStringAppendEncryptedData(edatastring
, edata
);
566 secnotice("item", "corrupted edata=%@", edatastring
);
568 CFReleaseSafe(edata
);
569 CFReleaseSafe(edatastring
);
571 CFReleaseNull(q
->q_error
); // This item was never here, keep going
572 } else if (status
== errSecAuthNeeded
) {
573 secwarning("Authentication is needed for %@,rowid=%" PRId64
" (%" PRIdOSStatus
"): %@", q
->q_class
->name
, rowid
, status
, q
->q_error
);
574 } else if (status
== errSecInteractionNotAllowed
) {
575 static dispatch_once_t kclockedtoken
;
576 static sec_action_t kclockedaction
;
577 dispatch_once(&kclockedtoken
, ^{
578 kclockedaction
= sec_action_create("ratelimiterdisabledlogevent", 1);
579 sec_action_set_handler(kclockedaction
, ^{
580 secerror("decode item failed, keychain is locked (%d)", (int)errSecInteractionNotAllowed
);
583 sec_action_perform(kclockedaction
);
584 } else if (status
== errSecMissingEntitlement
) {
585 // That's fine, let's pretend the item never existed for this query.
586 // We may find other, better items for the caller!
587 CFReleaseNull(q
->q_error
);
589 secerror("decode %@,rowid=%" PRId64
" failed (%" PRIdOSStatus
"): %@", q
->q_class
->name
, rowid
, status
, q
->q_error
);
591 // q->q_error will be released appropriately by a call to query_error
598 if (CFDictionaryContainsKey(item
, kSecAttrTokenID
) && (q
->q_return_type
& kSecReturnDataMask
) == 0) {
599 // For token-based items, to get really meaningful set of attributes we must provide also data field, so augment mask
600 // and restart item decoding cycle.
601 q
->q_return_type
|= kSecReturnDataMask
;
606 if (q
->q_token_object_id
!= NULL
&& !checkTokenObjectID(q
->q_token_object_id
, CFDictionaryGetValue(item
, kSecValueData
)))
609 if (q
->q_class
== identity_class()) {
610 // TODO: Use col 2 for key rowid and use both rowids in persistent ref.
612 CFMutableDictionaryRef key
;
613 /* TODO : if there is a errSecDecode error here, we should cleanup */
614 if (!s3dl_item_from_col(stmt
, q
, 3, c
->accessGroups
, &key
, NULL
, NULL
, &q
->q_error
) || !key
)
617 CFDataRef certData
= CFDictionaryGetValue(item
, kSecValueData
);
619 CFDictionarySetValue(key
, kSecAttrIdentityCertificateData
, certData
);
620 CFDictionaryRemoveValue(item
, kSecValueData
);
623 CFDataRef certTokenID
= CFDictionaryGetValue(item
, kSecAttrTokenID
);
625 CFDictionarySetValue(key
, kSecAttrIdentityCertificateTokenID
, certTokenID
);
626 CFDictionaryRemoveValue(item
, kSecAttrTokenID
);
628 CFDictionaryApplyFunction(item
, s3dl_merge_into_dict
, key
);
633 if (!match_item(c
->dbt
, q
, c
->accessGroups
, item
))
636 CFTypeRef a_result
= handle_result(q
, item
, rowid
);
638 if (a_result
== kCFNull
) {
639 /* Caller wasn't interested in a result, but we still
640 count this row as found. */
641 CFRelease(a_result
); // Help shut up clang
642 } else if (q
->q_limit
== 1) {
643 c
->result
= a_result
;
645 CFArrayAppendValue((CFMutableArrayRef
)c
->result
, a_result
);
652 q
->q_return_type
= saved_mask
;
657 SecDbAppendWhereROWID(CFMutableStringRef sql
,
658 CFStringRef col
, sqlite_int64 row_id
,
661 SecDbAppendWhereOrAnd(sql
, needWhere
);
662 CFStringAppendFormat(sql
, NULL
, CFSTR("%@=%lld"), col
, row_id
);
667 SecDbAppendWhereAttrs(CFMutableStringRef sql
, const Query
*q
, bool *needWhere
) {
668 CFIndex ix
, attr_count
= query_attr_count(q
);
669 for (ix
= 0; ix
< attr_count
; ++ix
) {
670 SecDbAppendWhereOrAndEquals(sql
, query_attr_at(q
, ix
).key
, needWhere
);
675 SecDbAppendWhereAccessGroups(CFMutableStringRef sql
,
677 CFArrayRef accessGroups
,
679 CFIndex ix
, ag_count
;
680 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
))) {
684 SecDbAppendWhereOrAnd(sql
, needWhere
);
685 CFStringAppend(sql
, col
);
686 CFStringAppend(sql
, CFSTR(" IN (?"));
687 for (ix
= 1; ix
< ag_count
; ++ix
) {
688 CFStringAppend(sql
, CFSTR(",?"));
690 CFStringAppend(sql
, CFSTR(")"));
694 isQueryOverAllMUSRViews(CFTypeRef musrView
)
696 return SecMUSRIsViewAllViews(musrView
);
700 isQueryOverSingleUserView(CFTypeRef musrView
)
702 return isNull(musrView
);
707 isQueryOverBothUserAndSystem(CFTypeRef musrView
, uid_t
*uid
)
709 return SecMUSRGetBothUserAndSystemUUID(musrView
, uid
);
714 SecDbAppendWhereMusr(CFMutableStringRef sql
,
718 SecDbAppendWhereOrAnd(sql
, needWhere
);
721 if (isQueryOverBothUserAndSystem(q
->q_musrView
, NULL
)) {
722 CFStringAppend(sql
, CFSTR("(musr = ? OR musr = ?)"));
725 if (isQueryOverAllMUSRViews(q
->q_musrView
)) {
726 /* query over all items, regardless of view */
727 } else if (isQueryOverSingleUserView(q
->q_musrView
)) {
728 CFStringAppend(sql
, CFSTR("musr = ?"));
730 CFStringAppend(sql
, CFSTR("musr = ?"));
734 static void SecDbAppendWhereClause(CFMutableStringRef sql
, const Query
*q
,
735 CFArrayRef accessGroups
) {
736 bool needWhere
= true;
737 SecDbAppendWhereROWID(sql
, CFSTR("ROWID"), q
->q_row_id
, &needWhere
);
738 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
739 SecDbAppendWhereMusr(sql
, q
, &needWhere
);
740 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &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 CFStringAppendFormat(sql
, NULL
, CFSTR("SELECT crowid, %@"
753 "(SELECT cert.rowid AS crowid, cert.labl AS labl,"
754 " cert.issr AS issr, cert.slnr AS slnr, cert.skid AS skid,"
755 " keys.*,cert.data AS %@"
757 " WHERE keys.priv == 1 AND cert.pkhh == keys.klbl"),
758 kSecAttrIdentityCertificateData
, kSecAttrIdentityCertificateData
);
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
);
770 CFStringAppend(sql
, CFSTR("SELECT rowid, data FROM "));
771 CFStringAppend(sql
, q
->q_class
->name
);
772 SecDbAppendWhereClause(sql
, q
, accessGroups
);
774 //do not append limit for all queries which needs filtering
775 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
) {
776 SecDbAppendLimit(sql
, q
->q_limit
);
782 static bool sqlBindMusr(sqlite3_stmt
*stmt
, const Query
*q
, int *pParam
, CFErrorRef
*error
) {
788 if (isQueryOverBothUserAndSystem(q
->q_musrView
, &uid
)) {
789 /* network extensions are special and get to query both user and system views */
790 CFDataRef systemUUID
= SecMUSRGetSystemKeychainUUID();
791 result
= SecDbBindObject(stmt
, param
++, systemUUID
, error
);
793 CFDataRef activeUser
= SecMUSRCreateActiveUserUUID(uid
);
794 result
= SecDbBindObject(stmt
, param
++, activeUser
, error
);
795 CFReleaseNull(activeUser
);
799 if (isQueryOverAllMUSRViews(q
->q_musrView
)) {
800 /* query over all items, regardless of view */
801 } else if (isQueryOverSingleUserView(q
->q_musrView
)) {
802 CFDataRef singleUUID
= SecMUSRGetSingleUserKeychainUUID();
803 result
= SecDbBindObject(stmt
, param
++, singleUUID
, error
);
805 result
= SecDbBindObject(stmt
, param
++, q
->q_musrView
, error
);
813 static bool sqlBindAccessGroups(sqlite3_stmt
*stmt
, CFArrayRef accessGroups
,
814 int *pParam
, CFErrorRef
*error
) {
817 CFIndex ix
, count
= accessGroups
? CFArrayGetCount(accessGroups
) : 0;
818 for (ix
= 0; ix
< count
; ++ix
) {
819 result
= SecDbBindObject(stmt
, param
++,
820 CFArrayGetValueAtIndex(accessGroups
, ix
),
829 static bool sqlBindWhereClause(sqlite3_stmt
*stmt
, const Query
*q
,
830 CFArrayRef accessGroups
, int *pParam
, CFErrorRef
*error
) {
833 CFIndex ix
, attr_count
= query_attr_count(q
);
834 for (ix
= 0; ix
< attr_count
; ++ix
) {
835 result
= SecDbBindObject(stmt
, param
++, query_attr_at(q
, ix
).value
, error
);
841 result
= sqlBindMusr(stmt
, q
, ¶m
, error
);
844 /* Bind the access group to the sql. */
846 result
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
853 bool SecDbItemQuery(SecDbQueryRef query
, CFArrayRef accessGroups
, SecDbConnectionRef dbconn
, CFErrorRef
*error
,
854 void (^handle_row
)(SecDbItemRef item
, bool *stop
)) {
855 __block
bool ok
= true;
856 /* Sanity check the query. */
858 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
860 bool (^return_attr
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
861 // The attributes here must match field list hardcoded in s3dl_select_sql used below, which is
863 return attr
->kind
== kSecDbRowIdAttr
|| attr
->kind
== kSecDbEncryptedDataAttr
;
866 CFStringRef sql
= s3dl_create_select_sql(query
, accessGroups
);
869 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
870 /* Bind the values being searched for to the SELECT statement. */
872 if (query
->q_class
== identity_class()) {
873 /* Bind the access groups to cert.agrp. */
874 ok
&= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
877 ok
&= sqlBindWhereClause(stmt
, query
, accessGroups
, ¶m
, error
);
879 SecDbStep(dbconn
, stmt
, error
, ^(bool *stop
) {
880 SecDbItemRef itemFromStatement
= SecDbItemCreateWithStatement(kCFAllocatorDefault
, query
->q_class
, stmt
, query
->q_keybag
, error
, return_attr
);
881 if (itemFromStatement
) {
882 CFTransferRetained(itemFromStatement
->credHandle
, query
->q_use_cred_handle
);
883 if (match_item(dbconn
, query
, accessGroups
, itemFromStatement
->attributes
))
884 handle_row(itemFromStatement
, stop
);
885 CFReleaseNull(itemFromStatement
);
887 secerror("failed to create item from stmt: %@", error
? *error
: (CFErrorRef
)"no error");
889 CFReleaseNull(*error
);
904 s3dl_query(s3dl_handle_row handle_row
,
905 void *context
, CFErrorRef
*error
)
907 struct s3dl_query_ctx
*c
= context
;
908 SecDbConnectionRef dbt
= c
->dbt
;
910 CFArrayRef accessGroups
= c
->accessGroups
;
912 /* Sanity check the query. */
914 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
916 /* Actual work here. */
917 if (q
->q_limit
== 1) {
920 c
->result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
922 CFStringRef sql
= s3dl_create_select_sql(q
, accessGroups
);
923 bool ok
= SecDbWithSQL(dbt
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
925 /* Bind the values being searched for to the SELECT statement. */
927 if (q
->q_class
== identity_class()) {
928 /* Bind the access groups to cert.agrp. */
929 sql_ok
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
932 sql_ok
= sqlBindWhereClause(stmt
, q
, accessGroups
, ¶m
, error
);
934 SecDbForEach(dbt
, stmt
, error
, ^bool (int row_index
) {
935 handle_row(stmt
, context
);
937 bool needs_auth
= q
->q_error
&& CFErrorGetCode(q
->q_error
) == errSecAuthNeeded
;
938 if (q
->q_skip_acl_items
&& needs_auth
)
939 // Skip items needing authentication if we are told to do so.
940 CFReleaseNull(q
->q_error
);
942 bool stop
= q
->q_limit
!= kSecMatchUnlimited
&& c
->found
>= q
->q_limit
;
943 stop
= stop
|| (q
->q_error
&& !needs_auth
);
952 // First get the error from the query, since errSecDuplicateItem from an
953 // update query should superceed the errSecItemNotFound below.
954 if (!query_error(q
, error
))
956 if (ok
&& c
->found
== 0) {
957 ok
= SecError(errSecItemNotFound
, error
, CFSTR("no matching items found"));
958 if (q
->q_spindump_on_failure
) {
959 __security_stackshotreport(CFSTR("ItemNotFound"), __sec_exception_code_LostInMist
);
967 s3dl_copy_matching(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
,
968 CFArrayRef accessGroups
, CFErrorRef
*error
)
970 struct s3dl_query_ctx ctx
= {
971 .q
= q
, .accessGroups
= accessGroups
, .dbt
= dbt
,
973 if (q
->q_row_id
&& query_attr_count(q
))
974 return SecError(errSecItemIllegalQuery
, error
,
975 CFSTR("attributes to query illegal; both row_id and other attributes can't be searched at the same time"));
976 if (q
->q_token_object_id
&& query_attr_count(q
) != 1)
977 return SecError(errSecItemIllegalQuery
, error
,
978 CFSTR("attributes to query illegal; both token persitent ref and other attributes can't be searched at the same time"));
980 // Only copy things that aren't tombstones unless the client explicitly asks otherwise.
981 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
982 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
983 bool ok
= s3dl_query(s3dl_query_row
, &ctx
, error
);
985 *result
= ctx
.result
;
987 CFReleaseSafe(ctx
.result
);
992 typedef void (^s3dl_item_digest_callback
)(CFDataRef persistantReference
, CFDataRef encryptedData
);
994 struct s3dl_digest_ctx
{
996 SecDbConnectionRef dbt
;
997 s3dl_item_digest_callback item_callback
;
1000 static void s3dl_query_row_digest(sqlite3_stmt
*stmt
, void *context
) {
1001 struct s3dl_query_ctx
*c
= context
;
1004 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
1005 CFDataRef edata
= s3dl_copy_data_from_col(stmt
, 1, NULL
);
1006 CFDataRef persistant_reference
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, NULL
);
1007 CFDataRef digest
= NULL
;
1010 digest
= CFDataCopySHA256Digest(edata
, NULL
);
1013 if (digest
&& persistant_reference
) {
1014 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(NULL
,
1015 kSecValuePersistentRef
, persistant_reference
,
1016 kSecValueData
, digest
,
1019 CFArrayAppendValue((CFMutableArrayRef
)c
->result
, item
);
1020 CFReleaseNull(item
);
1023 secinfo("item", "rowid %lu in %@ failed to create pref/digest", (unsigned long)rowid
, q
->q_class
->name
);
1025 CFReleaseNull(digest
);
1026 CFReleaseNull(edata
);
1027 CFReleaseNull(persistant_reference
);
1032 s3dl_copy_digest(SecDbConnectionRef dbt
, Query
*q
, CFArrayRef
*result
, CFArrayRef accessGroups
, CFErrorRef
*error
)
1034 struct s3dl_query_ctx ctx
= {
1035 .q
= q
, .dbt
= dbt
, .accessGroups
= accessGroups
,
1037 // Force to always return an array
1038 q
->q_limit
= kSecMatchUnlimited
;
1039 // This interface only queries live data
1040 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
1041 bool ok
= s3dl_query(s3dl_query_row_digest
, &ctx
, error
);
1043 *result
= (CFArrayRef
)ctx
.result
;
1045 CFReleaseSafe(ctx
.result
);
1050 /* First remove key from q->q_pairs if it's present, then add the attribute again. */
1051 static void query_set_attribute_with_desc(const SecDbAttr
*desc
, const void *value
, Query
*q
) {
1052 if (CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1054 for (ix
= 0; ix
< q
->q_attr_end
; ++ix
) {
1055 if (CFEqual(desc
->name
, q
->q_pairs
[ix
].key
)) {
1056 CFReleaseSafe(q
->q_pairs
[ix
].value
);
1058 for (; ix
< q
->q_attr_end
; ++ix
) {
1059 q
->q_pairs
[ix
] = q
->q_pairs
[ix
+ 1];
1061 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
1066 query_add_attribute_with_desc(desc
, value
, q
);
1069 /* Update modification_date if needed. */
1070 static void query_pre_update(Query
*q
) {
1071 SecDbForEachAttr(q
->q_class
, desc
) {
1072 if (desc
->kind
== kSecDbModificationDateAttr
) {
1073 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
1074 query_set_attribute_with_desc(desc
, now
, q
);
1080 /* Make sure all attributes that are marked as not_null have a value. If
1081 force_date is false, only set mdat and cdat if they aren't already set. */
1082 void query_pre_add(Query
*q
, bool force_date
) {
1083 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
1084 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInFlag
) {
1085 if (desc
->kind
== kSecDbCreationDateAttr
||
1086 desc
->kind
== kSecDbModificationDateAttr
) {
1088 query_set_attribute_with_desc(desc
, now
, q
);
1089 } else if (!CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1090 query_add_attribute_with_desc(desc
, now
, q
);
1092 } else if ((desc
->flags
& kSecDbNotNullFlag
) &&
1093 !CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1094 CFTypeRef value
= NULL
;
1095 if (desc
->flags
& kSecDbDefault0Flag
) {
1096 if (desc
->kind
== kSecDbDateAttr
)
1097 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
1100 value
= CFNumberCreate(0, kCFNumberSInt32Type
, &vzero
);
1102 } else if (desc
->flags
& kSecDbDefaultEmptyFlag
) {
1103 if (desc
->kind
== kSecDbDataAttr
|| desc
->kind
== kSecDbUUIDAttr
)
1104 value
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
1111 /* Safe to use query_add_attribute here since the attr wasn't
1113 query_add_attribute_with_desc(desc
, value
, q
);
1121 // Return a tri state value false->never make a tombstone, true->always make a
1122 // tombstone, NULL->make a tombstone, but delete it if the tombstone itself is not currently being synced.
1123 static CFBooleanRef
s3dl_should_make_tombstone(Query
*q
, bool item_is_syncable
, SecDbItemRef item
) {
1125 return q
->q_use_tomb
;
1126 else if (item_is_syncable
&& !SecDbItemIsTombstone(item
))
1129 return kCFBooleanFalse
;
1131 /* AUDIT[securityd](done):
1132 attributesToUpdate (ok) is a caller provided dictionary,
1133 only its cf types have been checked.
1136 s3dl_query_update(SecDbConnectionRef dbt
, Query
*q
,
1137 CFDictionaryRef attributesToUpdate
, CFArrayRef accessGroups
, CFErrorRef
*error
)
1139 /* Sanity check the query. */
1140 if (query_match_count(q
) != 0)
1141 return SecError(errSecItemMatchUnsupported
, error
, CFSTR("match not supported in attributes to update"));
1143 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported in attributes to update"));
1144 if (q
->q_row_id
&& query_attr_count(q
))
1145 return SecError(errSecItemIllegalQuery
, error
, CFSTR("attributes to update illegal; both row_id and other attributes can't be updated at the same time"));
1146 if (q
->q_token_object_id
&& query_attr_count(q
) != 1)
1147 return SecError(errSecItemIllegalQuery
, error
, CFSTR("attributes to update illegal; both token persistent ref and other attributes can't be updated at the same time"));
1149 __block
bool result
= true;
1150 Query
*u
= query_create(q
->q_class
, NULL
, attributesToUpdate
, error
);
1151 if (u
== NULL
) return false;
1152 require_action_quiet(query_update_parse(u
, attributesToUpdate
, error
), errOut
, result
= false);
1153 query_pre_update(u
);
1154 result
&= SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
1155 // Make sure we only update real items, not tombstones, unless the client explicitly asks otherwise.
1156 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
1157 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
1158 result
&= SecDbItemQuery(q
, accessGroups
, dbt
, error
, ^(SecDbItemRef item
, bool *stop
) {
1159 // We always need to know the error here.
1160 CFErrorRef localError
= NULL
;
1161 if (q
->q_token_object_id
) {
1162 const SecDbAttr
*valueDataAttr
= SecDbClassAttrWithKind(item
->class, kSecDbDataAttr
, NULL
);
1163 CFDataRef valueData
= SecDbItemGetValue(item
, valueDataAttr
, NULL
);
1164 if (q
->q_token_object_id
!= NULL
&& !checkTokenObjectID(q
->q_token_object_id
, valueData
))
1167 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1168 const SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
);
1169 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, NULL
));
1170 SecDbItemRef new_item
= SecDbItemCopyWithUpdates(item
, u
->q_item
, &localError
);
1171 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
1172 CFReleaseSafe(storedSHA1
);
1173 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
1174 // We just ignore this, and treat as if item is not found.
1175 secwarning("deleting corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, SecDbItemGetRowId(item
, NULL
), localError
);
1176 CFReleaseNull(localError
);
1177 if (!SecDbItemDelete(item
, dbt
, false, &localError
)) {
1178 secerror("failed to delete corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, SecDbItemGetRowId(item
, NULL
), localError
);
1179 CFReleaseNull(localError
);
1181 CFReleaseNull(new_item
);
1184 if (new_item
!= NULL
&& u
->q_access_control
!= NULL
)
1185 SecDbItemSetAccessControl(new_item
, u
->q_access_control
, &localError
);
1186 result
= SecErrorPropagate(localError
, error
) && new_item
;
1188 bool item_is_sync
= SecDbItemIsSyncable(item
);
1189 result
= SecDbItemUpdate(item
, new_item
, dbt
, s3dl_should_make_tombstone(q
, item_is_sync
, item
), q
->q_uuid_from_primary_key
, error
);
1191 q
->q_changed
= true;
1192 if (item_is_sync
|| SecDbItemIsSyncable(new_item
))
1193 q
->q_sync_changed
= true;
1195 CFRelease(new_item
);
1201 if (result
&& !q
->q_changed
)
1202 result
= SecError(errSecItemNotFound
, error
, CFSTR("No items updated"));
1204 if (!query_destroy(u
, error
))
1209 static bool SecDbItemNeedAuth(SecDbItemRef item
, CFErrorRef
*error
)
1211 CFErrorRef localError
= NULL
;
1212 if (!SecDbItemEnsureDecrypted(item
, true, &localError
) && localError
&& CFErrorGetCode(localError
) == errSecAuthNeeded
) {
1214 *error
= localError
;
1218 CFReleaseSafe(localError
);
1223 s3dl_query_delete(SecDbConnectionRef dbt
, Query
*q
, CFArrayRef accessGroups
, CFErrorRef
*error
)
1225 __block
bool ok
= true;
1226 __block
bool needAuth
= false;
1227 // Only delete things that aren't tombstones, unless the client explicitly asks otherwise.
1228 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
1229 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
1230 ok
&= SecDbItemSelect(q
, dbt
, error
, NULL
, ^bool(const SecDbAttr
*attr
) {
1232 },^bool(CFMutableStringRef sql
, bool *needWhere
) {
1233 SecDbAppendWhereClause(sql
, q
, accessGroups
);
1235 },^bool(sqlite3_stmt
* stmt
, int col
) {
1236 return sqlBindWhereClause(stmt
, q
, accessGroups
, &col
, error
);
1237 }, ^(SecDbItemRef item
, bool *stop
) {
1238 // Check if item for token persitence ref
1239 if (q
->q_token_object_id
) {
1240 const SecDbAttr
*valueDataAttr
= SecDbClassAttrWithKind(item
->class, kSecDbDataAttr
, NULL
);
1241 CFDataRef valueData
= SecDbItemGetValue(item
, valueDataAttr
, NULL
);
1242 if (q
->q_token_object_id
!= NULL
&& !checkTokenObjectID(q
->q_token_object_id
, valueData
))
1245 // Check if item need to be authenticated by LocalAuthentication
1246 item
->cryptoOp
= kAKSKeyOpDelete
;
1247 if (SecDbItemNeedAuth(item
, error
)) {
1251 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1252 const SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
);
1253 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, NULL
));
1254 bool item_is_sync
= SecDbItemIsSyncable(item
);
1255 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
1256 CFReleaseSafe(storedSHA1
);
1257 ok
= SecDbItemDelete(item
, dbt
, s3dl_should_make_tombstone(q
, item_is_sync
, item
), error
);
1259 q
->q_changed
= true;
1261 q
->q_sync_changed
= true;
1264 if (ok
&& !q
->q_changed
&& !needAuth
) {
1265 ok
= SecError(errSecItemNotFound
, error
, CFSTR("Delete failed to delete anything"));
1267 return ok
&& !needAuth
;
1271 matchAnyString(CFStringRef needle
, CFStringRef
*haystack
)
1274 if (CFEqual(needle
, *haystack
))
1281 /* Return true iff the item in question should not be backed up, nor restored,
1282 but when restoring a backup the original version of the item should be
1283 added back to the keychain again after the restore completes. */
1284 bool SecItemIsSystemBound(CFDictionaryRef item
, const SecDbClass
*cls
, bool multiUser
) {
1285 CFNumberRef sysb
= CFDictionaryGetValue(item
, kSecAttrSysBound
);
1286 if (isNumber(sysb
)) {
1288 if (!CFNumberGetValue(sysb
, kCFNumberSInt32Type
, &num
))
1290 if (num
== kSecSecAttrSysBoundNot
) {
1292 } else if (num
== kSecSecAttrSysBoundPreserveDuringRestore
) {
1298 CFStringRef agrp
= CFDictionaryGetValue(item
, kSecAttrAccessGroup
);
1299 if (!isString(agrp
))
1302 if (CFEqualSafe(agrp
, kSOSInternalAccessGroup
)) {
1303 secdebug("backup", "found sysbound item: %@", item
);
1307 if (CFEqual(agrp
, CFSTR("lockdown-identities"))) {
1308 secdebug("backup", "found sys_bound item: %@", item
);
1312 if (CFEqual(agrp
, CFSTR("apple")) && cls
== genp_class()) {
1313 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1314 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
1316 if (isString(service
) && isString(account
)) {
1317 static CFStringRef mcAccounts
[] = {
1323 if (CFEqual(service
, CFSTR("com.apple.managedconfiguration"))
1324 && matchAnyString(account
, mcAccounts
))
1326 secdebug("backup", "found sys_bound item: %@", item
);
1331 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.account.CloudKit.token"))) {
1332 secdebug("backup", "found sys_bound item: %@", item
);
1336 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.account.idms.continuation-key"))) {
1337 secdebug("backup", "found sys_bound item: %@", item
);
1342 if (multiUser
&& CFEqual(agrp
, CFSTR("com.apple.apsd")) && cls
== genp_class()) {
1343 static CFStringRef pushServices
[] = {
1344 CFSTR("push.apple.com"),
1345 CFSTR("push.apple.com,PerAppToken.v0"),
1348 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1350 if (isString(service
) && matchAnyString(service
, pushServices
)) {
1351 secdebug("backup", "found sys_bound item: %@", item
);
1356 if (multiUser
&& CFEqual(agrp
, CFSTR("appleaccount")) && cls
== genp_class()) {
1357 static CFStringRef accountServices
[] = {
1358 CFSTR("com.apple.appleaccount.fmf.token"), /* temporary tokens while accout is being setup */
1359 CFSTR("com.apple.appleaccount.fmf.apptoken"),
1360 CFSTR("com.apple.appleaccount.fmip.siritoken"),
1361 CFSTR("com.apple.appleaccount.cloudkit.token"),
1364 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1366 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1367 secdebug("backup", "found exact sys_bound item: %@", item
);
1372 if (multiUser
&& CFEqual(agrp
, CFSTR("apple")) && cls
== genp_class()) {
1373 static CFStringRef accountServices
[] = {
1374 /* accounts, remove with rdar://37595482 */
1375 CFSTR("com.apple.account.AppleAccount.token"),
1376 CFSTR("com.apple.account.AppleAccount.password"),
1377 CFSTR("com.apple.account.AppleAccount.rpassword"),
1378 CFSTR("com.apple.account.idms.token"),
1379 CFSTR("com.apple.account.idms.heartbeat-token"),
1380 CFSTR("com.apple.account.idms.continuation-key"),
1381 CFSTR("com.apple.account.CloudKit.token"),
1382 CFSTR("com.apple.account.IdentityServices.password"), /* accountsd for ids */
1383 CFSTR("com.apple.account.IdentityServices.rpassword"),
1384 CFSTR("com.apple.account.IdentityServices.token"),
1386 CFSTR("BackupIDSAccountToken"),
1387 CFSTR("com.apple.ids"),
1392 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1394 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1395 secdebug("backup", "found exact sys_bound item: %@", item
);
1398 if (isString(service
) && CFStringHasPrefix(service
, CFSTR("com.apple.gs."))) {
1399 secdebug("backup", "found exact sys_bound item: %@", item
);
1402 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.facetime"))) {
1403 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
1404 if (isString(account
) && CFEqual(account
, CFSTR("registrationV1"))) {
1405 secdebug("backup", "found exact sys_bound item: %@", item
);
1411 /* accounts, remove with rdar://37595482 */
1412 if (multiUser
&& CFEqual(agrp
, CFSTR("com.apple.ind")) && cls
== genp_class()) {
1413 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1414 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.ind.registration"))) {
1415 secdebug("backup", "found exact sys_bound item: %@", item
);
1420 if (multiUser
&& CFEqual(agrp
, CFSTR("ichat")) && cls
== genp_class()) {
1421 static CFStringRef accountServices
[] = {
1425 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1427 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1428 secdebug("backup", "found exact sys_bound item: %@", item
);
1433 if (multiUser
&& CFEqual(agrp
, CFSTR("ichat")) && cls
== keys_class()) {
1434 static CFStringRef exactMatchingLabel
[] = {
1435 CFSTR("iMessage Encryption Key"),
1436 CFSTR("iMessage Signing Key"),
1438 CFStringRef label
= CFDictionaryGetValue(item
, kSecAttrLabel
);
1439 if (isString(label
)) {
1440 if (matchAnyString(label
, exactMatchingLabel
)) {
1441 secdebug("backup", "found exact sys_bound item: %@", item
);
1447 if (multiUser
&& CFEqual(agrp
, CFSTR("com.apple.rapport")) && cls
== genp_class()) {
1448 secdebug("backup", "found exact sys_bound item: %@", item
);
1452 secdebug("backup", "found non sys_bound item: %@", item
);
1456 /* Delete all items from the current keychain. If this is not an in
1457 place upgrade we don't delete items in the 'lockdown-identities'
1458 access group, this ensures that an import or restore of a backup
1459 will never overwrite an existing activation record. */
1460 static bool SecServerDeleteAll(SecDbConnectionRef dbt
, CFErrorRef
*error
) {
1461 secwarning("SecServerDeleteAll");
1463 return kc_transaction(dbt
, error
, ^{
1465 bool ok
= (SecDbExec(dbt
, CFSTR("DELETE from genp;"), error
) &&
1466 SecDbExec(dbt
, CFSTR("DELETE from inet;"), error
) &&
1467 SecDbExec(dbt
, CFSTR("DELETE from cert;"), error
) &&
1468 SecDbExec(dbt
, CFSTR("DELETE from keys;"), error
));
1473 #if TARGET_OS_IPHONE
1475 static bool DeleteAllFromTableForMUSRView(SecDbConnectionRef dbt
,
1481 sqlite3_stmt
*stmt
= NULL
;
1482 CFStringRef sql2
= NULL
;
1486 sql2
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@ AND pdmn NOT IN ('aku','akpu','cku','dku')"), sql
);
1488 sql2
= CFRetain(sql
);
1490 require(sql2
, fail
);
1492 stmt
= SecDbCopyStmt(dbt
, sql2
, NULL
, error
);
1493 require(stmt
, fail
);
1495 ok
= SecDbBindObject(stmt
, 1, musr
, error
);
1498 ok
= SecDbStep(dbt
, stmt
, error
, ^(bool *stop
) { });
1503 ok
= SecDbFinalize(stmt
, error
);
1506 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
));
1530 struct s3dl_export_row_ctx
{
1531 struct s3dl_query_ctx qc
;
1532 keybag_handle_t dest_keybag
;
1533 enum SecItemFilter filter
;
1537 static void s3dl_export_row(sqlite3_stmt
*stmt
, void *context
) {
1538 struct s3dl_export_row_ctx
*c
= context
;
1540 SecAccessControlRef access_control
= NULL
;
1541 CFErrorRef localError
= NULL
;
1543 /* Skip akpu items when backing up, those are intentionally lost across restores. The same applies to SEP-based keys */
1544 bool skip_akpu_or_token
= c
->filter
== kSecBackupableItemFilter
;
1546 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
1547 CFMutableDictionaryRef allAttributes
= NULL
;
1548 CFMutableDictionaryRef metadataAttributes
= NULL
;
1549 CFMutableDictionaryRef secretStuff
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1550 keyclass_t keyclass
= 0;
1551 bool ok
= s3dl_item_from_col(stmt
, q
, 1, c
->qc
.accessGroups
, &allAttributes
, &access_control
, &keyclass
, &localError
);
1554 metadataAttributes
= CFDictionaryCreateMutableCopy(NULL
, 0, allAttributes
);
1555 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbReturnDataFlag
) {
1556 CFTypeRef value
= CFDictionaryGetValue(metadataAttributes
, desc
->name
);
1558 CFDictionarySetValue(secretStuff
, desc
->name
, value
);
1559 CFDictionaryRemoveValue(metadataAttributes
, desc
->name
);
1564 bool is_akpu
= access_control
? CFEqualSafe(SecAccessControlGetProtection(access_control
), kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
)
1565 // Mask generation, only look at class per se
1566 : (keyclass
& key_class_last
) == key_class_akpu
;
1567 bool is_token
= (ok
&& allAttributes
!= NULL
) ? CFDictionaryContainsKey(allAttributes
, kSecAttrTokenID
) : false;
1569 if (ok
&& allAttributes
&& !(skip_akpu_or_token
&& (is_akpu
|| is_token
))) {
1570 /* Only export sysbound items if do_sys_bound is true, only export non sysbound items otherwise. */
1571 bool do_sys_bound
= c
->filter
== kSecSysBoundItemFilter
;
1572 if (c
->filter
== kSecNoItemFilter
||
1573 SecItemIsSystemBound(allAttributes
, q
->q_class
, c
->multiUser
) == do_sys_bound
) {
1574 /* Re-encode the item. */
1575 secdebug("item", "export rowid %llu item: %@", rowid
, allAttributes
);
1576 /* The code below could be moved into handle_row. */
1577 CFDataRef pref
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, allAttributes
);
1579 if (c
->dest_keybag
!= KEYBAG_NONE
) {
1580 CFMutableDictionaryRef auth_attribs
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1581 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInAuthenticatedDataFlag
) {
1582 CFTypeRef value
= CFDictionaryGetValue(metadataAttributes
, desc
->name
);
1584 CFDictionaryAddValue(auth_attribs
, desc
->name
, value
);
1585 CFDictionaryRemoveValue(metadataAttributes
, desc
->name
);
1589 /* Encode and encrypt the item to the specified keybag. */
1590 CFDataRef edata
= NULL
;
1591 bool encrypted
= ks_encrypt_data(c
->dest_keybag
, access_control
, q
->q_use_cred_handle
, secretStuff
, metadataAttributes
, auth_attribs
, &edata
, false, &q
->q_error
);
1592 CFDictionaryRemoveAllValues(allAttributes
);
1593 CFRelease(auth_attribs
);
1595 CFDictionarySetValue(allAttributes
, kSecValueData
, edata
);
1596 CFReleaseSafe(edata
);
1598 seccritical("ks_encrypt_data %@,rowid=%" PRId64
": failed: %@", q
->q_class
->name
, rowid
, q
->q_error
);
1599 CFReleaseNull(q
->q_error
);
1602 if (CFDictionaryGetCount(allAttributes
)) {
1603 CFDictionarySetValue(allAttributes
, kSecValuePersistentRef
, pref
);
1604 CFArrayAppendValue((CFMutableArrayRef
)c
->qc
.result
, allAttributes
);
1607 CFReleaseSafe(pref
);
1611 OSStatus status
= SecErrorGetOSStatus(localError
);
1613 if (status
== errSecInteractionNotAllowed
&& is_akpu
) {
1614 if (skip_akpu_or_token
) {
1615 secdebug("item", "Skipping akpu item for backup");
1616 } else { // Probably failed to decrypt sysbound item. Should never be an akpu item in backup.
1617 secerror("Encountered akpu item we cannot export (filter %d), skipping. %@", c
->filter
, localError
);
1618 CFDictionaryRef payload
= NULL
;
1619 CFTypeRef agrp
= CFDictionaryGetValue(allAttributes
, CFSTR("agrp"));
1621 payload
= CFDictionaryCreateForCFTypes(NULL
, CFSTR("agrp"), agrp
, NULL
);
1623 SecABCTrigger(CFSTR("keychain"), CFSTR("invalid-akpu+sysbound"), NULL
, payload
);
1624 CFReleaseNull(payload
);
1626 // We expect akpu items to be inaccessible when the device is locked.
1627 CFReleaseNull(localError
);
1629 /* This happens a lot when trying to migrate keychain before first unlock, so only a notice */
1630 /* If the error is "corrupted item" then we just ignore it, otherwise we save it in the query */
1631 secinfo("item","Could not export item for rowid %llu: %@", rowid
, localError
);
1633 if (status
== errSecDecode
) {
1634 CFReleaseNull(localError
);
1636 CFReleaseSafe(q
->q_error
);
1637 q
->q_error
= localError
;
1641 CFReleaseNull(access_control
);
1642 CFReleaseNull(allAttributes
);
1643 CFReleaseNull(metadataAttributes
);
1644 CFReleaseNull(secretStuff
);
1648 SecCreateKeybagUUID(keybag_handle_t keybag
)
1650 #if !TARGET_HAS_KEYSTORE
1655 if (aks_get_bag_uuid(keybag
, uuid
) != KERN_SUCCESS
)
1657 uuid_unparse_lower(uuid
, uuidstr
);
1658 return CFStringCreateWithCString(NULL
, uuidstr
, kCFStringEncodingUTF8
);
1664 SecServerCopyKeychainPlist(SecDbConnectionRef dbt
,
1665 SecurityClient
*client
,
1666 keybag_handle_t src_keybag
,
1667 keybag_handle_t dest_keybag
,
1668 enum SecItemFilter filter
,
1669 CFErrorRef
*error
) {
1670 CFMutableDictionaryRef keychain
;
1671 keychain
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1672 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1674 bool inMultiUser
= false;
1675 CFStringRef keybaguuid
= NULL
;
1676 Query q
= { .q_keybag
= src_keybag
,
1681 if (error
&& !*error
)
1682 SecError(errSecAllocate
, error
, CFSTR("Can't create keychain dictionary"));
1687 kSecReturnDataMask
|
1688 kSecReturnAttributesMask
|
1689 kSecReturnPersistentRefMask
;
1690 q
.q_limit
= kSecMatchUnlimited
;
1691 q
.q_skip_acl_items
= true;
1694 #if TARGET_OS_IPHONE
1695 if (client
&& client
->inMultiUser
) {
1696 q
.q_musrView
= SecMUSRCreateActiveUserUUID(client
->uid
);
1701 q
.q_musrView
= SecMUSRGetSingleUserKeychainUUID();
1702 CFRetain(q
.q_musrView
);
1704 keybaguuid
= SecCreateKeybagUUID(dest_keybag
);
1706 CFDictionarySetValue(keychain
, kSecBackupKeybagUUIDKey
, keybaguuid
);
1710 /* Get rid of this duplicate. */
1711 const SecDbClass
*SecDbClasses
[] = {
1718 for (class_ix
= 0; class_ix
< array_size(SecDbClasses
);
1720 q
.q_class
= SecDbClasses
[class_ix
];
1721 struct s3dl_export_row_ctx ctx
= {
1722 .qc
= { .q
= &q
, .dbt
= dbt
},
1723 .dest_keybag
= dest_keybag
, .filter
= filter
,
1724 .multiUser
= inMultiUser
,
1727 secnotice("item", "exporting %ssysbound class '%@'", filter
!= kSecSysBoundItemFilter
? "non-" : "", q
.q_class
->name
);
1729 CFErrorRef localError
= NULL
;
1730 if (s3dl_query(s3dl_export_row
, &ctx
, &localError
)) {
1731 secnotice("item", "exporting class '%@' complete", q
.q_class
->name
);
1732 if (CFArrayGetCount(ctx
.qc
.result
)) {
1733 SecSignpostBackupCount(SecSignpostImpulseBackupClassCount
, q
.q_class
->name
, CFArrayGetCount(ctx
.qc
.result
), filter
);
1734 CFDictionaryAddValue(keychain
, q
.q_class
->name
, ctx
.qc
.result
);
1738 OSStatus status
= (OSStatus
)CFErrorGetCode(localError
);
1739 if (status
== errSecItemNotFound
) {
1740 secnotice("item", "exporting class '%@' complete (no items)", q
.q_class
->name
);
1741 CFRelease(localError
);
1743 secerror("exporting class '%@' failed: %@", q
.q_class
->name
, localError
);
1745 CFReleaseSafe(*error
);
1746 *error
= localError
;
1748 CFRelease(localError
);
1750 CFReleaseNull(keychain
);
1751 CFReleaseNull(ctx
.qc
.result
);
1755 CFReleaseNull(ctx
.qc
.result
);
1759 CFReleaseNull(q
.q_musrView
);
1760 CFReleaseNull(keybaguuid
);
1765 struct SecServerImportClassState
{
1766 SecDbConnectionRef dbt
;
1768 keybag_handle_t src_keybag
;
1769 keybag_handle_t dest_keybag
;
1770 SecurityClient
*client
;
1771 enum SecItemFilter filter
;
1774 struct SecServerImportItemState
{
1775 const SecDbClass
*class;
1776 struct SecServerImportClassState
*s
;
1780 SecServerImportItem(const void *value
, void *context
)
1782 struct SecServerImportItemState
*state
= (struct SecServerImportItemState
*)context
;
1783 bool inMultiUser
= false;
1784 #if TARGET_OS_IPHONE
1785 if (state
->s
->client
->inMultiUser
)
1789 if (state
->s
->error
)
1792 if (!isDictionary(value
)) {
1793 SecError(errSecParam
, &state
->s
->error
, CFSTR("value %@ is not a dictionary"), value
);
1797 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
1799 secdebug("item", "Import Item : %@", dict
);
1801 SecDbItemRef item
= NULL
;
1803 /* This is sligthly confusing:
1804 - During upgrade all items are exported with KEYBAG_NONE.
1805 - During restore from backup, existing sys_bound items are exported with KEYBAG_NONE, and are exported as dictionary of attributes.
1806 - Item in the actual backup are export with a real keybag, and are exported as encrypted v_Data and v_PersistentRef
1808 if (state
->s
->src_keybag
== KEYBAG_NONE
) {
1809 item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, state
->class, dict
, state
->s
->dest_keybag
, &state
->s
->error
);
1811 item
= SecDbItemCreateWithBackupDictionary(kCFAllocatorDefault
, state
->class, dict
, state
->s
->src_keybag
, state
->s
->dest_keybag
, &state
->s
->error
);
1814 /* If item is NULL here, control flow ends up at the end where error is cleared. */
1815 if (item
&& !SecDbItemEnsureDecrypted(item
, true, &state
->s
->error
)) {
1816 secdebug("item", "Failed to import item because of decryption failure: %@", state
->s
->error
);
1817 CFReleaseNull(item
);
1818 /* No early return; as just above, go to the end where error is cleared. */
1821 /* We use the kSecSysBoundItemFilter to indicate that we don't
1822 * preserve rowid's during import.
1824 if (item
&& item
->attributes
&& state
->s
->filter
== kSecBackupableItemFilter
) {
1827 /* We don't filter non sys_bound items during import since we know we
1828 * will never have any in this case.
1830 if (SecItemIsSystemBound(item
->attributes
, state
->class, inMultiUser
)) {
1831 secdebug("item", "skipping backup of item: %@", dict
);
1832 CFReleaseNull(item
);
1837 * Don't bother with u items when in edu mode since our current backup system
1838 * don't keep track of items that blongs to the device (u) but rather just
1839 * merge them into one blob.
1841 if (inMultiUser
&& (pdmu
= CFDictionaryGetValue(item
->attributes
, kSecAttrAccessible
))) {
1842 if (CFEqual(pdmu
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) ||
1843 CFEqual(pdmu
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
) ||
1844 CFEqual(pdmu
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) ||
1845 CFEqual(pdmu
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
))
1847 secdebug("item", "Skipping KU item : %@", dict
);
1848 CFReleaseNull(item
);
1853 /* Avoid importing token-based items. Although newer backups should not have them,
1854 * older (iOS9, iOS10.0) produced backups with token-based items.
1856 if (CFDictionaryContainsKey(item
->attributes
, kSecAttrTokenID
)) {
1857 secdebug("item", "Skipping token-based item : %@", dict
);
1858 CFReleaseNull(item
);
1867 if (item
&& item
->attributes
) {
1868 CFDataRef musr
= NULL
;
1869 CFDataRef musrBackup
= CFDictionaryGetValue(item
->attributes
, kSecAttrMultiUser
);
1870 CFDataRef systemKeychainUUID
= SecMUSRGetSystemKeychainUUID();
1871 bool systemKeychain
= CFEqualSafe(musrBackup
, systemKeychainUUID
);
1873 #if TARGET_OS_IPHONE
1874 if (state
->s
->client
&& state
->s
->client
->inMultiUser
) {
1875 if (systemKeychain
) {
1876 secwarning("system keychain not allowed in multi user mode for item: %@", item
);
1878 musr
= SecMUSRCreateActiveUserUUID(state
->s
->client
->uid
);
1883 if (systemKeychain
) {
1884 musr
= SecMUSRCopySystemKeychainUUID();
1886 musr
= SecMUSRGetSingleUserKeychainUUID();
1891 CFReleaseNull(item
);
1893 SecDbItemSetValueWithName(item
, CFSTR("musr"), musr
, &state
->s
->error
);
1905 if(state
->s
->filter
!= kSecSysBoundItemFilter
) {
1906 SecDbItemExtractRowIdFromBackupDictionary(item
, dict
, &state
->s
->error
);
1908 SecDbItemInferSyncable(item
, &state
->s
->error
);
1909 insertStatus
= SecDbItemInsert(item
, state
->s
->dbt
, &state
->s
->error
);
1910 if (!insertStatus
) {
1912 When running in EduMode, multiple users share the same
1913 keychain and unfortionaly the rowid is used a
1914 persistant reference and is part of the contraints (its
1915 UNIQUE), so lets clear the rowid and try to insert the
1918 This even happens for normal operation because of
1919 SysBound entries, so in case of a failure, lets try
1920 again to insert the record.
1922 SecDbItemClearRowId(item
, NULL
);
1923 SecDbItemInsert(item
, state
->s
->dbt
, &state
->s
->error
);
1927 /* Reset error if we had one, since we just skip the current item
1928 and continue importing what we can. */
1929 if (state
->s
->error
) {
1930 secwarning("Failed to import an item (%@) of class '%@': %@ - ignoring error.",
1931 item
, state
->class->name
, state
->s
->error
);
1932 CFReleaseNull(state
->s
->error
);
1935 CFReleaseSafe(item
);
1938 static void SecServerImportClass(const void *key
, const void *value
,
1940 struct SecServerImportClassState
*state
=
1941 (struct SecServerImportClassState
*)context
;
1944 if (!isString(key
)) {
1945 SecError(errSecParam
, &state
->error
, CFSTR("class name %@ is not a string"), key
);
1948 /* ignore the Keybag UUID */
1949 if (CFEqual(key
, kSecBackupKeybagUUIDKey
))
1951 const SecDbClass
*class = kc_class_with_name(key
);
1953 secwarning("Ignoring unknown key class '%@'", key
);
1956 if (class == identity_class()) {
1957 SecError(errSecParam
, &state
->error
, CFSTR("attempt to import an identity"));
1960 struct SecServerImportItemState item_state
= {
1961 .class = class, .s
= state
,
1963 if (isArray(value
)) {
1964 CFArrayRef items
= (CFArrayRef
)value
;
1965 secwarning("Import %ld items of class %@ (filter %d)", (long)CFArrayGetCount(items
), key
, state
->filter
);
1966 SecSignpostBackupCount(SecSignpostImpulseRestoreClassCount
, class->name
, CFArrayGetCount(items
), state
->filter
);
1967 CFArrayApplyFunction(items
, CFRangeMake(0, CFArrayGetCount(items
)),
1968 SecServerImportItem
, &item_state
);
1969 } else if (isDictionary(value
)) {
1970 CFDictionaryRef item
= (CFDictionaryRef
)value
;
1971 secwarning("Import %ld items of class %@ (filter %d)", (long)1, key
, state
->filter
);
1972 SecSignpostBackupCount(SecSignpostImpulseRestoreClassCount
, class->name
, 1, state
->filter
);
1973 SecServerImportItem(item
, &item_state
);
1975 secwarning("Unknown value type for class %@ (filter %d)", key
, state
->filter
);
1979 bool SecServerImportKeychainInPlist(SecDbConnectionRef dbt
, SecurityClient
*client
,
1980 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
1981 CFDictionaryRef keychain
, enum SecItemFilter filter
,
1982 bool removeKeychainContent
, CFErrorRef
*error
) {
1983 CFStringRef keybaguuid
= NULL
;
1986 CFDictionaryRef sys_bound
= NULL
;
1987 if (filter
== kSecBackupableItemFilter
) {
1988 /* Grab a copy of all the items for which SecItemIsSystemBound()
1990 require(sys_bound
= SecServerCopyKeychainPlist(dbt
, client
, KEYBAG_DEVICE
,
1991 KEYBAG_NONE
, kSecSysBoundItemFilter
,
1996 * Validate the uuid of the source keybag matches what we have in the backup
1998 keybaguuid
= SecCreateKeybagUUID(src_keybag
);
2000 CFStringRef uuid
= CFDictionaryGetValue(keychain
, kSecBackupKeybagUUIDKey
);
2001 if (isString(uuid
)) {
2002 require_action(CFEqual(keybaguuid
, uuid
), errOut
,
2003 SecError(errSecDecode
, error
, CFSTR("Keybag UUID (%@) mismatch with backup (%@)"),
2010 * Shared iPad is very special, it always delete's the user keychain, and never merge content
2012 if (client
->inMultiUser
) {
2013 CFDataRef musrView
= SecMUSRCreateActiveUserUUID(client
->uid
);
2014 require_action(musrView
, errOut
, ok
= false);
2015 require_action(ok
= SecServerDeleteAllForUser(dbt
, musrView
, true, error
), errOut
, CFReleaseNull(musrView
));
2016 CFReleaseNull(musrView
);
2021 * Delete everything in the keychain.
2022 * We don't want this if we're restoring backups because we probably already synced stuff over
2024 if (removeKeychainContent
) {
2025 require(ok
= SecServerDeleteAll(dbt
, error
), errOut
);
2027 // Custom hack to support bluetooth's workflow for 11.3. Should be removed in a future release.
2028 __block CFErrorRef btError
= NULL
;
2029 bool deletedBT
= kc_transaction(dbt
, &btError
, ^bool{
2031 #define EXCLUDE_AGRPS "'com.apple.security.sos', 'com.apple.security.sos-usercredential', 'com.apple.security.ckks', 'com.apple.security.egoIdentities', 'com.apple.security.octagon'"
2033 bool tok
= SecDbExec(dbt
, CFSTR("DELETE FROM genp WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS
");"), &btError
);
2034 tok
&= SecDbExec(dbt
, CFSTR("DELETE FROM inet WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS
");"), &btError
);
2035 tok
&= SecDbExec(dbt
, CFSTR("DELETE FROM cert WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS
");"), &btError
);
2036 tok
&= SecDbExec(dbt
, CFSTR("DELETE FROM keys WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS
");"), &btError
);
2038 #undef EXCLUDE_AGRPS
2042 secerror("Unable to delete nonsyncable items prior to keychain restore: %@", btError
);
2044 secnotice("restore", "Successfully deleted nonsyncable items");
2046 CFReleaseNull(btError
);
2050 struct SecServerImportClassState state
= {
2052 .src_keybag
= src_keybag
,
2053 .dest_keybag
= dest_keybag
,
2057 /* Import the provided items, preserving rowids. */
2058 secwarning("Restoring backup items '%ld'", (long)CFDictionaryGetCount(keychain
));
2059 CFDictionaryApplyFunction(keychain
, SecServerImportClass
, &state
);
2062 state
.src_keybag
= KEYBAG_NONE
;
2063 /* Import the items we preserved with random rowids. */
2064 state
.filter
= kSecSysBoundItemFilter
;
2065 secwarning("Restoring sysbound items '%ld'", (long)CFDictionaryGetCount(sys_bound
));
2066 CFDictionaryApplyFunction(sys_bound
, SecServerImportClass
, &state
);
2070 CFReleaseSafe(*error
);
2071 *error
= state
.error
;
2073 CFRelease(state
.error
);
2078 // If CKKS had spun up, it's very likely that we just deleted its data.
2079 // Tell it to perform a local resync.
2081 SecCKKSPerformLocalResync();
2085 CFReleaseSafe(sys_bound
);
2086 CFReleaseSafe(keybaguuid
);
2092 SecServerBackupGetKeybagUUID(CFDictionaryRef keychain
, CFErrorRef
*error
)
2094 CFStringRef uuid
= CFDictionaryGetValue(keychain
, kSecBackupKeybagUUIDKey
);
2095 if (!isString(uuid
)) {
2096 SecError(errSecDecode
, error
, CFSTR("Missing or invalid %@ in backup dictionary"), kSecBackupKeybagUUIDKey
);
2102 #pragma mark - key rolling support
2105 struct check_generation_ctx
{
2106 struct s3dl_query_ctx query_ctx
;
2107 uint32_t current_generation
;
2110 static void check_generation(sqlite3_stmt
*stmt
, void *context
) {
2111 struct check_generation_ctx
*c
= context
;
2112 CFDataRef blob
= NULL
;
2114 const uint8_t *cursor
= NULL
;
2116 keyclass_t keyclass
;
2117 uint32_t current_generation
= c
->current_generation
;
2119 require(blob
= s3dl_copy_data_from_col(stmt
, 1, &c
->query_ctx
.q
->q_error
), out
);
2120 blobLen
= CFDataGetLength(blob
);
2121 cursor
= CFDataGetBytePtr(blob
);
2123 /* Check for underflow, ensuring we have at least one full AES block left. */
2124 if (blobLen
< sizeof(version
) + sizeof(keyclass
)) {
2125 SecError(errSecDecode
, &c
->query_ctx
.q
->q_error
, CFSTR("check_generation: Check for underflow"));
2129 version
= *((uint32_t *)cursor
);
2130 cursor
+= sizeof(version
);
2132 (void) version
; // TODO: do something with the version number.
2134 keyclass
= *((keyclass_t
*)cursor
);
2136 // TODO: export get_key_gen macro
2137 if (((keyclass
& ~key_class_last
) == 0) != (current_generation
== 0)) {
2138 c
->query_ctx
.found
++;
2141 CFReleaseSafe(blob
);
2145 c
->query_ctx
.found
++;
2146 CFReleaseSafe(blob
);
2149 bool s3dl_dbt_keys_current(SecDbConnectionRef dbt
, uint32_t current_generation
, CFErrorRef
*error
) {
2150 CFErrorRef localError
= NULL
;
2151 struct check_generation_ctx ctx
= { .query_ctx
= { .dbt
= dbt
}, .current_generation
= current_generation
};
2153 const SecDbClass
*classes
[] = {
2160 for (size_t class_ix
= 0; class_ix
< array_size(classes
); ++class_ix
) {
2161 Query
*q
= query_create(classes
[class_ix
], NULL
, NULL
, &localError
);
2165 ctx
.query_ctx
.q
= q
;
2166 q
->q_limit
= kSecMatchUnlimited
;
2168 bool ok
= s3dl_query(check_generation
, &ctx
, &localError
);
2169 query_destroy(q
, NULL
);
2170 CFReleaseNull(ctx
.query_ctx
.result
);
2172 if (!ok
&& localError
&& (CFErrorGetCode(localError
) == errSecItemNotFound
)) {
2173 CFReleaseNull(localError
);
2176 secerror("Class %@ not up to date", classes
[class_ix
]->name
);
2182 bool s3dl_dbt_update_keys(SecDbConnectionRef dbt
, SecurityClient
*client
, CFErrorRef
*error
) {
2183 return SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
2184 __block
bool ok
= false;
2185 uint32_t keystore_generation_status
;
2187 /* can we migrate to new class keys right now? */
2188 if (!aks_generation(KEYBAG_DEVICE
, generation_noop
, &keystore_generation_status
) &&
2189 (keystore_generation_status
& generation_change_in_progress
)) {
2191 /* take a lock assertion */
2192 bool operated_while_unlocked
= SecAKSDoWithUserBagLockAssertion(error
, ^{
2193 CFErrorRef localError
= NULL
;
2194 CFDictionaryRef backup
= SecServerCopyKeychainPlist(dbt
, NULL
,
2195 KEYBAG_DEVICE
, KEYBAG_NONE
, kSecNoItemFilter
, &localError
);
2198 secerror("Ignoring export error: %@ during roll export", localError
);
2199 CFReleaseNull(localError
);
2201 // 'true' argument: we're replacing everything with newly wrapped entries so remove the old stuff
2202 ok
= SecServerImportKeychainInPlist(dbt
, client
, KEYBAG_NONE
,
2203 KEYBAG_DEVICE
, backup
, kSecNoItemFilter
, true, &localError
);
2205 secerror("Ignoring export error: %@ during roll export", localError
);
2206 CFReleaseNull(localError
);
2211 if (!operated_while_unlocked
)
2214 ok
= SecError(errSecBadReq
, error
, CFSTR("No key roll in progress."));