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 <securityd/SecItemDb.h>
37 #include <utilities/SecAKSWrappers.h>
39 #include <securityd/SecDbKeychainItem.h>
40 #include <securityd/SecItemSchema.h>
41 #include <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 <securityd/SOSCloudCircleServer.h>
49 #include <utilities/array_size.h>
50 #include <utilities/SecIOFormat.h>
51 #include <utilities/SecCFCCWrappers.h>
52 #include <uuid/uuid.h>
53 #include "sec_action.h"
55 #include "keychain/ckks/CKKS.h"
57 #define kSecBackupKeybagUUIDKey CFSTR("keybag-uuid")
59 const SecDbAttr
*SecDbAttrWithKey(const SecDbClass
*c
,
62 /* Special case: identites can have all attributes of either cert
64 if (c
== identity_class()) {
65 const SecDbAttr
*desc
;
66 if (!(desc
= SecDbAttrWithKey(cert_class(), key
, 0)))
67 desc
= SecDbAttrWithKey(keys_class(), key
, error
);
72 SecDbForEachAttr(c
, a
) {
73 if (CFEqual(a
->name
, key
))
76 if (CFEqual(kSecUseDataProtectionKeychain
, key
)) {
77 return NULL
; /* results in no ops for this attribute */
81 SecError(errSecNoSuchAttr
, error
, CFSTR("attribute %@ not found in class %@"), key
, c
->name
);
86 bool kc_transaction(SecDbConnectionRef dbt
, CFErrorRef
*error
, bool(^perform
)(void)) {
87 return kc_transaction_type(dbt
, kSecDbExclusiveTransactionType
, error
, perform
);
90 bool kc_transaction_type(SecDbConnectionRef dbt
, SecDbTransactionType type
, CFErrorRef
*error
, bool(^perform
)(void)) {
91 __block
bool ok
= true;
92 return ok
&& SecDbTransaction(dbt
, type
, error
, ^(bool *commit
) {
93 ok
= *commit
= perform();
97 static CFStringRef
SecDbGetKindSQL(SecDbAttrKind kind
) {
103 case kSecDbPrimaryKeyAttr
:
104 case kSecDbEncryptedDataAttr
:
105 return CFSTR("BLOB");
106 case kSecDbAccessAttr
:
107 case kSecDbStringAttr
:
108 return CFSTR("TEXT");
109 case kSecDbNumberAttr
:
112 return CFSTR("INTEGER");
114 case kSecDbCreationDateAttr
:
115 case kSecDbModificationDateAttr
:
116 return CFSTR("REAL");
117 case kSecDbRowIdAttr
:
118 return CFSTR("INTEGER PRIMARY KEY AUTOINCREMENT");
119 case kSecDbAccessControlAttr
:
120 case kSecDbUTombAttr
:
121 /* This attribute does not exist in the DB. */
126 static void SecDbAppendUnique(CFMutableStringRef sql
, CFStringRef value
, bool *haveUnique
) {
129 CFStringAppend(sql
, CFSTR("UNIQUE("));
131 SecDbAppendElement(sql
, value
, haveUnique
);
134 static void SecDbAppendCreateTableWithClass(CFMutableStringRef sql
, const SecDbClass
*c
) {
135 CFStringAppendFormat(sql
, 0, CFSTR("CREATE TABLE %@("), c
->name
);
136 SecDbForEachAttrWithMask(c
,desc
,kSecDbInFlag
) {
137 CFStringAppendFormat(sql
, 0, CFSTR("%@ %@"), desc
->name
, SecDbGetKindSQL(desc
->kind
));
138 if (desc
->flags
& kSecDbNotNullFlag
)
139 CFStringAppend(sql
, CFSTR(" NOT NULL"));
140 if (desc
->flags
& kSecDbDefault0Flag
)
141 CFStringAppend(sql
, CFSTR(" DEFAULT 0"));
142 if (desc
->flags
& kSecDbDefaultEmptyFlag
)
143 CFStringAppend(sql
, CFSTR(" DEFAULT ''"));
144 CFStringAppend(sql
, CFSTR(","));
147 bool haveUnique
= false;
148 SecDbForEachAttrWithMask(c
,desc
,kSecDbPrimaryKeyFlag
| kSecDbInFlag
) {
149 SecDbAppendUnique(sql
, desc
->name
, &haveUnique
);
152 CFStringAppend(sql
, CFSTR(")"));
154 CFStringAppend(sql
, CFSTR(");"));
157 SecDbForEachAttrWithMask(c
,desc
, kSecDbIndexFlag
| kSecDbInFlag
) {
158 if (desc
->kind
== kSecDbSyncAttr
) {
159 CFStringAppendFormat(sql
, 0, CFSTR("CREATE INDEX %@%@0 ON %@(%@) WHERE %@=0;"), c
->name
, desc
->name
, c
->name
, desc
->name
, desc
->name
);
161 CFStringAppendFormat(sql
, 0, CFSTR("CREATE INDEX %@%@ ON %@(%@);"), c
->name
, desc
->name
, c
->name
, desc
->name
);
166 static void SecDbAppendDropTableWithClass(CFMutableStringRef sql
, const SecDbClass
*c
) {
167 CFStringAppendFormat(sql
, 0, CFSTR("DROP TABLE %@;"), c
->name
);
170 static CFDataRef
SecPersistentRefCreateWithItem(SecDbItemRef item
, CFErrorRef
*error
) {
171 sqlite3_int64 row_id
= SecDbItemGetRowId(item
, error
);
173 return _SecItemCreatePersistentRef(SecDbItemGetClass(item
)->name
, row_id
, item
->attributes
);
177 bool SecItemDbCreateSchema(SecDbConnectionRef dbt
, const SecDbSchema
*schema
, CFArrayRef classIndexesForNewTables
, bool includeVersion
, CFErrorRef
*error
)
179 __block
bool ok
= true;
180 CFMutableStringRef sql
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
182 if (classIndexesForNewTables
) {
183 CFArrayForEach(classIndexesForNewTables
, ^(const void* index
) {
184 const SecDbClass
* class = schema
->classes
[(int)index
];
185 SecDbAppendCreateTableWithClass(sql
, class);
189 for (const SecDbClass
* const *pclass
= schema
->classes
; *pclass
; ++pclass
) {
190 SecDbAppendCreateTableWithClass(sql
, *pclass
);
194 if (includeVersion
) {
195 CFStringAppendFormat(sql
, NULL
, CFSTR("INSERT INTO tversion(version,minor) VALUES(%d, %d);"),
196 schema
->majorVersion
, schema
->minorVersion
);
198 CFStringPerformWithCString(sql
, ^(const char *sql_string
) {
199 ok
= SecDbErrorWithDb(sqlite3_exec(SecDbHandle(dbt
), sql_string
, NULL
, NULL
, NULL
),
200 SecDbHandle(dbt
), error
, CFSTR("sqlite3_exec: %s"), sql_string
);
206 bool SecItemDbDeleteSchema(SecDbConnectionRef dbt
, const SecDbSchema
*schema
, CFErrorRef
*error
)
208 __block
bool ok
= true;
209 CFMutableStringRef sql
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
210 for (const SecDbClass
* const *pclass
= schema
->classes
; *pclass
; ++pclass
) {
211 SecDbAppendDropTableWithClass(sql
, *pclass
);
213 CFStringPerformWithCString(sql
, ^(const char *sql_string
) {
214 ok
= SecDbErrorWithDb(sqlite3_exec(SecDbHandle(dbt
), sql_string
, NULL
, NULL
, NULL
),
215 SecDbHandle(dbt
), error
, CFSTR("sqlite3_exec: %s"), sql_string
);
221 CFTypeRef
SecDbItemCopyResult(SecDbItemRef item
, ReturnTypeMask return_type
, CFErrorRef
*error
) {
224 if (return_type
== 0) {
225 /* Caller isn't interested in any results at all. */
227 } else if (return_type
== kSecReturnDataMask
) {
228 a_result
= SecDbItemGetCachedValueWithName(item
, kSecValueData
);
230 CFRetainSafe(a_result
);
232 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
234 } else if (return_type
== kSecReturnPersistentRefMask
) {
235 a_result
= SecPersistentRefCreateWithItem(item
, error
);
237 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableForCFTypes(CFGetAllocator(item
));
238 /* We need to return more than one value. */
239 if (return_type
& kSecReturnRefMask
) {
240 CFDictionarySetValue(dict
, kSecClass
, SecDbItemGetClass(item
)->name
);
242 CFOptionFlags mask
= (((return_type
& kSecReturnDataMask
|| return_type
& kSecReturnRefMask
) ? kSecDbReturnDataFlag
: 0) |
243 ((return_type
& kSecReturnAttributesMask
|| return_type
& kSecReturnRefMask
) ? kSecDbReturnAttrFlag
: 0));
244 SecDbForEachAttr(SecDbItemGetClass(item
), desc
) {
245 if ((desc
->flags
& mask
) != 0) {
246 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
247 if (value
&& !CFEqual(kCFNull
, value
)) {
248 CFDictionarySetValue(dict
, desc
->name
, value
);
249 } else if (value
== NULL
) {
255 CFDictionaryRemoveValue(dict
, kSecAttrUUID
);
257 if (return_type
& kSecReturnPersistentRefMask
) {
258 CFDataRef pref
= SecPersistentRefCreateWithItem(item
, error
);
259 CFDictionarySetValue(dict
, kSecValuePersistentRef
, pref
);
269 /* AUDIT[securityd](done):
270 attributes (ok) is a caller provided dictionary, only its cf type has
274 s3dl_query_add(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
, CFErrorRef
*error
)
276 if (query_match_count(q
) != 0)
277 return errSecItemMatchUnsupported
;
279 /* Add requires a class to be specified unless we are adding a ref. */
280 if (q
->q_use_item_list
)
281 return errSecUseItemListUnsupported
;
283 /* Actual work here. */
284 SecDbItemRef item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, q
->q_class
, q
->q_item
, KEYBAG_DEVICE
, error
);
287 if (SecDbItemIsTombstone(item
))
288 SecDbItemSetValue(item
, &v7utomb
, q
->q_use_tomb
? q
->q_use_tomb
: kCFBooleanTrue
, NULL
);
292 ok
= SecDbItemSetValueWithName(item
, CFSTR("v_Data"), q
->q_data
, error
);
294 ok
= SecDbItemSetRowId(item
, q
->q_row_id
, error
);
296 ok
= SecDbItemSetValueWithName(item
, CFSTR("musr"), q
->q_musrView
, error
);
297 SecDbItemSetCredHandle(item
, q
->q_use_cred_handle
);
300 if(SecCKKSIsEnabled() && !SecCKKSTestDisableAutomaticUUID()) {
301 s3dl_item_make_new_uuid(item
, q
->q_uuid_from_primary_key
, error
);
303 if(q
->q_add_sync_callback
) {
304 CFTypeRef uuid
= SecDbItemGetValue(item
, &v10itemuuid
, error
);
306 CKKSRegisterSyncStatusCallback(uuid
, q
->q_add_sync_callback
);
308 secerror("Couldn't fetch UUID from item; can't call callback");
315 ok
= SecDbItemInsert(item
, dbt
, error
);
318 if (result
&& q
->q_return_type
) {
319 *result
= SecDbItemCopyResult(item
, q
->q_return_type
, error
);
322 if (!ok
&& error
&& *error
) {
323 if (CFEqual(CFErrorGetDomain(*error
), kSecDbErrorDomain
) && CFErrorGetCode(*error
) == SQLITE_CONSTRAINT
) {
324 CFReleaseNull(*error
);
325 SecError(errSecDuplicateItem
, error
, CFSTR("duplicate item %@"), item
);
326 } else if (CFEqual(CFErrorGetDomain(*error
), kSecErrorDomain
) && CFErrorGetCode(*error
) == errSecDecode
) { //handle situation when item have pdmn=akpu but passcode is not set
327 CFTypeRef value
= SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbAccessAttr
, error
), error
);
328 if (value
&& CFEqual(value
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
)) {
329 CFReleaseNull(*error
);
330 SecError(errSecAuthFailed
, error
, CFSTR("authentication failed"));
337 if (SecDbItemIsSyncable(item
))
338 q
->q_sync_changed
= true;
341 secdebug("dbitem", "inserting item %@%s%@", item
, ok
? "" : "failed: ", ok
|| error
== NULL
? (CFErrorRef
)CFSTR("") : *error
);
348 bool s3dl_item_make_new_uuid(SecDbItemRef item
, bool uuid_from_primary_key
, CFErrorRef
* error
) {
353 // Set the item UUID.
354 CFUUIDRef uuid
= NULL
;
355 // Were we asked to make the UUID static?
356 if (uuid_from_primary_key
) {
357 // This UUID isn't used in any security mechanism, so we can
358 // just use the first bits of the SHA256 hash.
359 CFDataRef pkhash
= SecDbKeychainItemCopySHA256PrimaryKey(item
, error
);
360 if(CFDataGetLength(pkhash
) >= 16) {
362 CFRange range
= CFRangeMake(0, 16);
363 CFDataGetBytes(pkhash
, range
, uuidBytes
);
365 uuid
= CFUUIDCreateWithBytes(NULL
,
383 CFReleaseNull(pkhash
);
386 uuid
= CFUUIDCreate(NULL
);
388 SecDbItemSetValueWithName(item
, kSecAttrUUID
, uuid
, error
);
393 typedef void (*s3dl_handle_row
)(sqlite3_stmt
*stmt
, void *context
);
396 s3dl_copy_data_from_col(sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
397 return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt
, col
),
398 sqlite3_column_bytes(stmt
, col
),
403 s3dl_item_from_col(sqlite3_stmt
*stmt
, Query
*q
, int col
, CFArrayRef accessGroups
,
404 CFMutableDictionaryRef
*item
, SecAccessControlRef
*access_control
, keyclass_t
* keyclass
, CFErrorRef
*error
) {
405 CFDataRef edata
= NULL
;
407 require(edata
= s3dl_copy_data_from_col(stmt
, col
, error
), out
);
408 ok
= s3dl_item_from_data(edata
, q
, accessGroups
, item
, access_control
, keyclass
, error
);
411 CFReleaseSafe(edata
);
415 struct s3dl_query_ctx
{
417 CFArrayRef accessGroups
;
418 SecDbConnectionRef dbt
;
423 /* Return whatever the caller requested based on the value of q->q_return_type.
424 keys and values must be 3 larger than attr_count in size to accomadate the
425 optional data, class and persistent ref results. This is so we can use
426 the CFDictionaryCreate() api here rather than appending to a
427 mutable dictionary. */
428 static CF_RETURNS_RETAINED CFTypeRef
429 handle_result(Query
*q
,
430 CFMutableDictionaryRef item
,
435 data
= CFDictionaryGetValue(item
, kSecValueData
);
436 CFDataRef pref
= NULL
;
437 if (q
->q_return_type
& kSecReturnPersistentRefMask
) {
438 pref
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, item
);
440 if (q
->q_return_type
== 0) {
441 /* Caller isn't interested in any results at all. */
443 } else if (q
->q_return_type
== kSecReturnDataMask
) {
448 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
450 } else if (q
->q_return_type
== kSecReturnPersistentRefMask
) {
451 a_result
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, item
);
453 /* We need to return more than one value. */
454 if (q
->q_return_type
& kSecReturnRefMask
) {
455 CFDictionarySetValue(item
, kSecClass
, q
->q_class
->name
);
456 } else if ((q
->q_return_type
& kSecReturnAttributesMask
)) {
457 if (!(q
->q_return_type
& kSecReturnDataMask
)) {
458 CFDictionaryRemoveValue(item
, kSecValueData
);
461 // Add any attributes which are supposed to be returned, are not present in the decrypted blob,
462 // and have a way to generate themselves.
463 SecDbItemRef itemRef
= NULL
;
464 SecDbForEachAttrWithMask(q
->q_class
, attr
, kSecDbReturnAttrFlag
) {
465 if(!CFDictionaryGetValue(item
, attr
->name
) && attr
->copyValue
) {
466 CFErrorRef cferror
= NULL
;
468 itemRef
= SecDbItemCreateWithAttributes(NULL
, q
->q_class
, item
, KEYBAG_DEVICE
, &cferror
);
470 if(!cferror
&& itemRef
) {
471 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
472 CFTypeRef attrValue
= attr
->copyValue(itemRef
, attr
, &cferror
);
473 if (!cferror
&& attrValue
) {
474 CFDictionarySetValue(item
, attr
->name
, attrValue
);
476 CFReleaseNull(attrValue
);
479 CFReleaseNull(cferror
);
482 CFReleaseNull(itemRef
);
484 CFDictionaryRemoveValue(item
, kSecAttrUUID
);
487 CFDictionaryRemoveAllValues(item
);
488 if ((q
->q_return_type
& kSecReturnDataMask
) && data
) {
489 CFDictionarySetValue(item
, kSecValueData
, data
);
493 if (q
->q_return_type
& kSecReturnPersistentRefMask
&& pref
!= NULL
) {
494 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
504 static void s3dl_merge_into_dict(const void *key
, const void *value
, void *context
) {
505 CFDictionarySetValue(context
, key
, value
);
508 static bool checkTokenObjectID(CFDataRef token_object_id
, CFDataRef value_data
) {
509 bool equalOID
= false;
510 require_quiet(value_data
, out
);
511 CFDictionaryRef itemValue
= SecTokenItemValueCopy(value_data
, NULL
);
512 require_quiet(itemValue
, out
);
513 CFDataRef oID
= CFDictionaryGetValue(itemValue
, kSecTokenValueObjectIDKey
);
514 equalOID
= CFEqualSafe(token_object_id
, oID
);
515 CFRelease(itemValue
);
520 static void s3dl_query_row(sqlite3_stmt
*stmt
, void *context
) {
521 struct s3dl_query_ctx
*c
= context
;
523 ReturnTypeMask saved_mask
= q
->q_return_type
;
525 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
526 CFMutableDictionaryRef item
= NULL
;
530 ok
= s3dl_item_from_col(stmt
, q
, 1, c
->accessGroups
, &item
, NULL
, NULL
, &q
->q_error
);
532 OSStatus status
= SecErrorGetOSStatus(q
->q_error
);
533 // errSecDecode means the item is corrupted, stash it for delete.
534 if (status
== errSecDecode
) {
535 secwarning("ignoring corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, rowid
, q
->q_error
);
537 CFStringRef tablename
= CFStringCreateCopy(kCFAllocatorDefault
, q
->q_class
->name
);
538 // Can't get rid of this item on the read path. Let's come back from elsewhere.
539 dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT
, 0), ^{
540 __block CFErrorRef localErr
= NULL
;
541 __block
bool ok
= true;
542 ok
&= kc_with_dbt(true, &localErr
, ^bool(SecDbConnectionRef dbt
) {
543 CFStringRef sql
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("DELETE FROM %@ WHERE rowid=%lli"), tablename
, rowid
);
544 ok
&= SecDbPrepare(dbt
, sql
, &localErr
, ^(sqlite3_stmt
*stmt
) {
545 ok
&= SecDbStep(dbt
, stmt
, &localErr
, NULL
);
548 if (!ok
|| localErr
) {
549 secerror("Failed to delete corrupt item, %@ row %lli: %@", tablename
, rowid
, localErr
);
551 secnotice("item", "Deleted corrupt rowid %lli from table %@", rowid
, tablename
);
553 CFReleaseNull(localErr
);
555 CFReleaseSafe(tablename
);
560 CFDataRef edata
= s3dl_copy_data_from_col(stmt
, 1, NULL
);
561 CFMutableStringRef edatastring
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
563 CFStringAppendEncryptedData(edatastring
, edata
);
564 secnotice("item", "corrupted edata=%@", edatastring
);
566 CFReleaseSafe(edata
);
567 CFReleaseSafe(edatastring
);
569 CFReleaseNull(q
->q_error
); // This item was never here, keep going
570 } else if (status
== errSecAuthNeeded
) {
571 secwarning("Authentication is needed for %@,rowid=%" PRId64
" (%" PRIdOSStatus
"): %@", q
->q_class
->name
, rowid
, status
, q
->q_error
);
572 } else if (status
== errSecInteractionNotAllowed
) {
573 static dispatch_once_t kclockedtoken
;
574 static sec_action_t kclockedaction
;
575 dispatch_once(&kclockedtoken
, ^{
576 kclockedaction
= sec_action_create("ratelimiterdisabledlogevent", 1);
577 sec_action_set_handler(kclockedaction
, ^{
578 secerror("decode item failed, keychain is locked (%d)", (int)errSecInteractionNotAllowed
);
581 sec_action_perform(kclockedaction
);
582 } else if (status
== errSecMissingEntitlement
) {
583 // That's fine, let's pretend the item never existed for this query.
584 // We may find other, better items for the caller!
585 CFReleaseNull(q
->q_error
);
587 secerror("decode %@,rowid=%" PRId64
" failed (%" PRIdOSStatus
"): %@", q
->q_class
->name
, rowid
, status
, q
->q_error
);
589 // q->q_error will be released appropriately by a call to query_error
596 if (CFDictionaryContainsKey(item
, kSecAttrTokenID
) && (q
->q_return_type
& kSecReturnDataMask
) == 0) {
597 // For token-based items, to get really meaningful set of attributes we must provide also data field, so augment mask
598 // and restart item decoding cycle.
599 q
->q_return_type
|= kSecReturnDataMask
;
604 if (q
->q_token_object_id
!= NULL
&& !checkTokenObjectID(q
->q_token_object_id
, CFDictionaryGetValue(item
, kSecValueData
)))
607 if (q
->q_class
== identity_class()) {
608 // TODO: Use col 2 for key rowid and use both rowids in persistent ref.
610 CFMutableDictionaryRef key
;
611 /* TODO : if there is a errSecDecode error here, we should cleanup */
612 if (!s3dl_item_from_col(stmt
, q
, 3, c
->accessGroups
, &key
, NULL
, NULL
, &q
->q_error
) || !key
)
615 CFDataRef certData
= CFDictionaryGetValue(item
, kSecValueData
);
617 CFDictionarySetValue(key
, kSecAttrIdentityCertificateData
, certData
);
618 CFDictionaryRemoveValue(item
, kSecValueData
);
621 CFDataRef certTokenID
= CFDictionaryGetValue(item
, kSecAttrTokenID
);
623 CFDictionarySetValue(key
, kSecAttrIdentityCertificateTokenID
, certTokenID
);
624 CFDictionaryRemoveValue(item
, kSecAttrTokenID
);
626 CFDictionaryApplyFunction(item
, s3dl_merge_into_dict
, key
);
631 if (!match_item(c
->dbt
, q
, c
->accessGroups
, item
))
634 CFTypeRef a_result
= handle_result(q
, item
, rowid
);
636 if (a_result
== kCFNull
) {
637 /* Caller wasn't interested in a result, but we still
638 count this row as found. */
639 CFRelease(a_result
); // Help shut up clang
640 } else if (q
->q_limit
== 1) {
641 c
->result
= a_result
;
643 CFArrayAppendValue((CFMutableArrayRef
)c
->result
, a_result
);
650 q
->q_return_type
= saved_mask
;
655 SecDbAppendWhereROWID(CFMutableStringRef sql
,
656 CFStringRef col
, sqlite_int64 row_id
,
659 SecDbAppendWhereOrAnd(sql
, needWhere
);
660 CFStringAppendFormat(sql
, NULL
, CFSTR("%@=%lld"), col
, row_id
);
665 SecDbAppendWhereAttrs(CFMutableStringRef sql
, const Query
*q
, bool *needWhere
) {
666 CFIndex ix
, attr_count
= query_attr_count(q
);
667 for (ix
= 0; ix
< attr_count
; ++ix
) {
668 SecDbAppendWhereOrAndEquals(sql
, query_attr_at(q
, ix
).key
, needWhere
);
673 SecDbAppendWhereAccessGroups(CFMutableStringRef sql
,
675 CFArrayRef accessGroups
,
677 CFIndex ix
, ag_count
;
678 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
))) {
682 SecDbAppendWhereOrAnd(sql
, needWhere
);
683 CFStringAppend(sql
, col
);
684 CFStringAppend(sql
, CFSTR(" IN (?"));
685 for (ix
= 1; ix
< ag_count
; ++ix
) {
686 CFStringAppend(sql
, CFSTR(",?"));
688 CFStringAppend(sql
, CFSTR(")"));
692 isQueryOverAllMUSRViews(CFTypeRef musrView
)
694 return SecMUSRIsViewAllViews(musrView
);
698 isQueryOverSingleUserView(CFTypeRef musrView
)
700 return isNull(musrView
);
705 isQueryOverBothUserAndSystem(CFTypeRef musrView
, uid_t
*uid
)
707 return SecMUSRGetBothUserAndSystemUUID(musrView
, uid
);
712 SecDbAppendWhereMusr(CFMutableStringRef sql
,
716 SecDbAppendWhereOrAnd(sql
, needWhere
);
719 if (isQueryOverBothUserAndSystem(q
->q_musrView
, NULL
)) {
720 CFStringAppend(sql
, CFSTR("(musr = ? OR musr = ?)"));
723 if (isQueryOverAllMUSRViews(q
->q_musrView
)) {
724 /* query over all items, regardless of view */
725 } else if (isQueryOverSingleUserView(q
->q_musrView
)) {
726 CFStringAppend(sql
, CFSTR("musr = ?"));
728 CFStringAppend(sql
, CFSTR("musr = ?"));
732 static void SecDbAppendWhereClause(CFMutableStringRef sql
, const Query
*q
,
733 CFArrayRef accessGroups
) {
734 bool needWhere
= true;
735 SecDbAppendWhereROWID(sql
, CFSTR("ROWID"), q
->q_row_id
, &needWhere
);
736 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
737 SecDbAppendWhereMusr(sql
, q
, &needWhere
);
738 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
741 static void SecDbAppendLimit(CFMutableStringRef sql
, CFIndex limit
) {
742 if (limit
!= kSecMatchUnlimited
)
743 CFStringAppendFormat(sql
, NULL
, CFSTR(" LIMIT %" PRIdCFIndex
), limit
);
746 static CFStringRef
s3dl_create_select_sql(Query
*q
, CFArrayRef accessGroups
) {
747 CFMutableStringRef sql
= CFStringCreateMutable(NULL
, 0);
748 if (q
->q_class
== identity_class()) {
749 CFStringAppendFormat(sql
, NULL
, CFSTR("SELECT crowid, %@"
751 "(SELECT cert.rowid AS crowid, cert.labl AS labl,"
752 " cert.issr AS issr, cert.slnr AS slnr, cert.skid AS skid,"
753 " keys.*,cert.data AS %@"
755 " WHERE keys.priv == 1 AND cert.pkhh == keys.klbl"),
756 kSecAttrIdentityCertificateData
, kSecAttrIdentityCertificateData
);
757 SecDbAppendWhereAccessGroups(sql
, CFSTR("cert.agrp"), accessGroups
, 0);
758 /* The next 3 SecDbAppendWhere calls are in the same order as in
759 SecDbAppendWhereClause(). This makes sqlBindWhereClause() work,
760 as long as we do an extra sqlBindAccessGroups first. */
761 SecDbAppendWhereROWID(sql
, CFSTR("crowid"), q
->q_row_id
, 0);
762 CFStringAppend(sql
, CFSTR(")"));
763 bool needWhere
= true;
764 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
765 SecDbAppendWhereMusr(sql
, q
, &needWhere
);
766 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
768 CFStringAppend(sql
, CFSTR("SELECT rowid, data FROM "));
769 CFStringAppend(sql
, q
->q_class
->name
);
770 SecDbAppendWhereClause(sql
, q
, accessGroups
);
772 //do not append limit for all queries which needs filtering
773 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
) {
774 SecDbAppendLimit(sql
, q
->q_limit
);
780 static bool sqlBindMusr(sqlite3_stmt
*stmt
, const Query
*q
, int *pParam
, CFErrorRef
*error
) {
786 if (isQueryOverBothUserAndSystem(q
->q_musrView
, &uid
)) {
787 /* network extensions are special and get to query both user and system views */
788 CFDataRef systemUUID
= SecMUSRGetSystemKeychainUUID();
789 result
= SecDbBindObject(stmt
, param
++, systemUUID
, error
);
791 CFDataRef activeUser
= SecMUSRCreateActiveUserUUID(uid
);
792 result
= SecDbBindObject(stmt
, param
++, activeUser
, error
);
793 CFReleaseNull(activeUser
);
797 if (isQueryOverAllMUSRViews(q
->q_musrView
)) {
798 /* query over all items, regardless of view */
799 } else if (isQueryOverSingleUserView(q
->q_musrView
)) {
800 CFDataRef singleUUID
= SecMUSRGetSingleUserKeychainUUID();
801 result
= SecDbBindObject(stmt
, param
++, singleUUID
, error
);
803 result
= SecDbBindObject(stmt
, param
++, q
->q_musrView
, error
);
811 static bool sqlBindAccessGroups(sqlite3_stmt
*stmt
, CFArrayRef accessGroups
,
812 int *pParam
, CFErrorRef
*error
) {
815 CFIndex ix
, count
= accessGroups
? CFArrayGetCount(accessGroups
) : 0;
816 for (ix
= 0; ix
< count
; ++ix
) {
817 result
= SecDbBindObject(stmt
, param
++,
818 CFArrayGetValueAtIndex(accessGroups
, ix
),
827 static bool sqlBindWhereClause(sqlite3_stmt
*stmt
, const Query
*q
,
828 CFArrayRef accessGroups
, int *pParam
, CFErrorRef
*error
) {
831 CFIndex ix
, attr_count
= query_attr_count(q
);
832 for (ix
= 0; ix
< attr_count
; ++ix
) {
833 result
= SecDbBindObject(stmt
, param
++, query_attr_at(q
, ix
).value
, error
);
839 result
= sqlBindMusr(stmt
, q
, ¶m
, error
);
842 /* Bind the access group to the sql. */
844 result
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
851 bool SecDbItemQuery(SecDbQueryRef query
, CFArrayRef accessGroups
, SecDbConnectionRef dbconn
, CFErrorRef
*error
,
852 void (^handle_row
)(SecDbItemRef item
, bool *stop
)) {
853 __block
bool ok
= true;
854 /* Sanity check the query. */
856 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
858 bool (^return_attr
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
859 // The attributes here must match field list hardcoded in s3dl_select_sql used below, which is
861 return attr
->kind
== kSecDbRowIdAttr
|| attr
->kind
== kSecDbEncryptedDataAttr
;
864 CFStringRef sql
= s3dl_create_select_sql(query
, accessGroups
);
867 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
868 /* Bind the values being searched for to the SELECT statement. */
870 if (query
->q_class
== identity_class()) {
871 /* Bind the access groups to cert.agrp. */
872 ok
&= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
875 ok
&= sqlBindWhereClause(stmt
, query
, accessGroups
, ¶m
, error
);
877 SecDbStep(dbconn
, stmt
, error
, ^(bool *stop
) {
878 SecDbItemRef itemFromStatement
= SecDbItemCreateWithStatement(kCFAllocatorDefault
, query
->q_class
, stmt
, query
->q_keybag
, error
, return_attr
);
879 if (itemFromStatement
) {
880 CFTransferRetained(itemFromStatement
->credHandle
, query
->q_use_cred_handle
);
881 if (match_item(dbconn
, query
, accessGroups
, itemFromStatement
->attributes
))
882 handle_row(itemFromStatement
, stop
);
883 CFReleaseNull(itemFromStatement
);
885 secerror("failed to create item from stmt: %@", error
? *error
: (CFErrorRef
)"no error");
887 CFReleaseNull(*error
);
902 s3dl_query(s3dl_handle_row handle_row
,
903 void *context
, CFErrorRef
*error
)
905 struct s3dl_query_ctx
*c
= context
;
906 SecDbConnectionRef dbt
= c
->dbt
;
908 CFArrayRef accessGroups
= c
->accessGroups
;
910 /* Sanity check the query. */
912 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
914 /* Actual work here. */
915 if (q
->q_limit
== 1) {
918 c
->result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
920 CFStringRef sql
= s3dl_create_select_sql(q
, accessGroups
);
921 bool ok
= SecDbWithSQL(dbt
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
923 /* Bind the values being searched for to the SELECT statement. */
925 if (q
->q_class
== identity_class()) {
926 /* Bind the access groups to cert.agrp. */
927 sql_ok
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
930 sql_ok
= sqlBindWhereClause(stmt
, q
, accessGroups
, ¶m
, error
);
932 SecDbForEach(dbt
, stmt
, error
, ^bool (int row_index
) {
933 handle_row(stmt
, context
);
935 bool needs_auth
= q
->q_error
&& CFErrorGetCode(q
->q_error
) == errSecAuthNeeded
;
936 if (q
->q_skip_acl_items
&& needs_auth
)
937 // Skip items needing authentication if we are told to do so.
938 CFReleaseNull(q
->q_error
);
940 bool stop
= q
->q_limit
!= kSecMatchUnlimited
&& c
->found
>= q
->q_limit
;
941 stop
= stop
|| (q
->q_error
&& !needs_auth
);
950 // First get the error from the query, since errSecDuplicateItem from an
951 // update query should superceed the errSecItemNotFound below.
952 if (!query_error(q
, error
))
954 if (ok
&& c
->found
== 0) {
955 ok
= SecError(errSecItemNotFound
, error
, CFSTR("no matching items found"));
956 if (q
->q_spindump_on_failure
) {
957 __security_stackshotreport(CFSTR("ItemNotFound"), __sec_exception_code_LostInMist
);
965 s3dl_copy_matching(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
,
966 CFArrayRef accessGroups
, CFErrorRef
*error
)
968 struct s3dl_query_ctx ctx
= {
969 .q
= q
, .accessGroups
= accessGroups
, .dbt
= dbt
,
971 if (q
->q_row_id
&& query_attr_count(q
))
972 return SecError(errSecItemIllegalQuery
, error
,
973 CFSTR("attributes to query illegal; both row_id and other attributes can't be searched at the same time"));
974 if (q
->q_token_object_id
&& query_attr_count(q
) != 1)
975 return SecError(errSecItemIllegalQuery
, error
,
976 CFSTR("attributes to query illegal; both token persitent ref and other attributes can't be searched at the same time"));
978 // Only copy things that aren't tombstones unless the client explicitly asks otherwise.
979 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
980 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
981 bool ok
= s3dl_query(s3dl_query_row
, &ctx
, error
);
983 *result
= ctx
.result
;
985 CFReleaseSafe(ctx
.result
);
990 typedef void (^s3dl_item_digest_callback
)(CFDataRef persistantReference
, CFDataRef encryptedData
);
992 struct s3dl_digest_ctx
{
994 SecDbConnectionRef dbt
;
995 s3dl_item_digest_callback item_callback
;
998 static void s3dl_query_row_digest(sqlite3_stmt
*stmt
, void *context
) {
999 struct s3dl_query_ctx
*c
= context
;
1002 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
1003 CFDataRef edata
= s3dl_copy_data_from_col(stmt
, 1, NULL
);
1004 CFDataRef persistant_reference
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, NULL
);
1005 CFDataRef digest
= NULL
;
1008 digest
= CFDataCopySHA256Digest(edata
, NULL
);
1011 if (digest
&& persistant_reference
) {
1012 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(NULL
,
1013 kSecValuePersistentRef
, persistant_reference
,
1014 kSecValueData
, digest
,
1017 CFArrayAppendValue((CFMutableArrayRef
)c
->result
, item
);
1018 CFReleaseNull(item
);
1021 secinfo("item", "rowid %lu in %@ failed to create pref/digest", (unsigned long)rowid
, q
->q_class
->name
);
1023 CFReleaseNull(digest
);
1024 CFReleaseNull(edata
);
1025 CFReleaseNull(persistant_reference
);
1030 s3dl_copy_digest(SecDbConnectionRef dbt
, Query
*q
, CFArrayRef
*result
, CFArrayRef accessGroups
, CFErrorRef
*error
)
1032 struct s3dl_query_ctx ctx
= {
1033 .q
= q
, .dbt
= dbt
, .accessGroups
= accessGroups
,
1035 // Force to always return an array
1036 q
->q_limit
= kSecMatchUnlimited
;
1037 // This interface only queries live data
1038 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
1039 bool ok
= s3dl_query(s3dl_query_row_digest
, &ctx
, error
);
1041 *result
= (CFArrayRef
)ctx
.result
;
1043 CFReleaseSafe(ctx
.result
);
1048 /* First remove key from q->q_pairs if it's present, then add the attribute again. */
1049 static void query_set_attribute_with_desc(const SecDbAttr
*desc
, const void *value
, Query
*q
) {
1050 if (CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1052 for (ix
= 0; ix
< q
->q_attr_end
; ++ix
) {
1053 if (CFEqual(desc
->name
, q
->q_pairs
[ix
].key
)) {
1054 CFReleaseSafe(q
->q_pairs
[ix
].value
);
1056 for (; ix
< q
->q_attr_end
; ++ix
) {
1057 q
->q_pairs
[ix
] = q
->q_pairs
[ix
+ 1];
1059 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
1064 query_add_attribute_with_desc(desc
, value
, q
);
1067 /* Update modification_date if needed. */
1068 static void query_pre_update(Query
*q
) {
1069 SecDbForEachAttr(q
->q_class
, desc
) {
1070 if (desc
->kind
== kSecDbModificationDateAttr
) {
1071 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
1072 query_set_attribute_with_desc(desc
, now
, q
);
1078 /* Make sure all attributes that are marked as not_null have a value. If
1079 force_date is false, only set mdat and cdat if they aren't already set. */
1080 void query_pre_add(Query
*q
, bool force_date
) {
1081 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
1082 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInFlag
) {
1083 if (desc
->kind
== kSecDbCreationDateAttr
||
1084 desc
->kind
== kSecDbModificationDateAttr
) {
1086 query_set_attribute_with_desc(desc
, now
, q
);
1087 } else if (!CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1088 query_add_attribute_with_desc(desc
, now
, q
);
1090 } else if ((desc
->flags
& kSecDbNotNullFlag
) &&
1091 !CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1092 CFTypeRef value
= NULL
;
1093 if (desc
->flags
& kSecDbDefault0Flag
) {
1094 if (desc
->kind
== kSecDbDateAttr
)
1095 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
1098 value
= CFNumberCreate(0, kCFNumberSInt32Type
, &vzero
);
1100 } else if (desc
->flags
& kSecDbDefaultEmptyFlag
) {
1101 if (desc
->kind
== kSecDbDataAttr
|| desc
->kind
== kSecDbUUIDAttr
)
1102 value
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
1109 /* Safe to use query_add_attribute here since the attr wasn't
1111 query_add_attribute_with_desc(desc
, value
, q
);
1119 // Return a tri state value false->never make a tombstone, true->always make a
1120 // tombstone, NULL->make a tombstone, but delete it if the tombstone itself is not currently being synced.
1121 static CFBooleanRef
s3dl_should_make_tombstone(Query
*q
, bool item_is_syncable
, SecDbItemRef item
) {
1123 return q
->q_use_tomb
;
1124 else if (item_is_syncable
&& !SecDbItemIsTombstone(item
))
1127 return kCFBooleanFalse
;
1129 /* AUDIT[securityd](done):
1130 attributesToUpdate (ok) is a caller provided dictionary,
1131 only its cf types have been checked.
1134 s3dl_query_update(SecDbConnectionRef dbt
, Query
*q
,
1135 CFDictionaryRef attributesToUpdate
, CFArrayRef accessGroups
, CFErrorRef
*error
)
1137 /* Sanity check the query. */
1138 if (query_match_count(q
) != 0)
1139 return SecError(errSecItemMatchUnsupported
, error
, CFSTR("match not supported in attributes to update"));
1141 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported in attributes to update"));
1142 if (q
->q_row_id
&& query_attr_count(q
))
1143 return SecError(errSecItemIllegalQuery
, error
, CFSTR("attributes to update illegal; both row_id and other attributes can't be updated at the same time"));
1144 if (q
->q_token_object_id
&& query_attr_count(q
) != 1)
1145 return SecError(errSecItemIllegalQuery
, error
, CFSTR("attributes to update illegal; both token persistent ref and other attributes can't be updated at the same time"));
1147 __block
bool result
= true;
1148 Query
*u
= query_create(q
->q_class
, NULL
, attributesToUpdate
, error
);
1149 if (u
== NULL
) return false;
1150 require_action_quiet(query_update_parse(u
, attributesToUpdate
, error
), errOut
, result
= false);
1151 query_pre_update(u
);
1152 result
&= SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
1153 // Make sure we only update real items, not tombstones, unless the client explicitly asks otherwise.
1154 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
1155 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
1156 result
&= SecDbItemQuery(q
, accessGroups
, dbt
, error
, ^(SecDbItemRef item
, bool *stop
) {
1157 // We always need to know the error here.
1158 CFErrorRef localError
= NULL
;
1159 if (q
->q_token_object_id
) {
1160 const SecDbAttr
*valueDataAttr
= SecDbClassAttrWithKind(item
->class, kSecDbDataAttr
, NULL
);
1161 CFDataRef valueData
= SecDbItemGetValue(item
, valueDataAttr
, NULL
);
1162 if (q
->q_token_object_id
!= NULL
&& !checkTokenObjectID(q
->q_token_object_id
, valueData
))
1165 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1166 const SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
);
1167 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, NULL
));
1168 SecDbItemRef new_item
= SecDbItemCopyWithUpdates(item
, u
->q_item
, &localError
);
1169 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
1170 CFReleaseSafe(storedSHA1
);
1171 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
1172 // We just ignore this, and treat as if item is not found.
1173 secwarning("deleting corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, SecDbItemGetRowId(item
, NULL
), localError
);
1174 CFReleaseNull(localError
);
1175 if (!SecDbItemDelete(item
, dbt
, false, &localError
)) {
1176 secerror("failed to delete corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, SecDbItemGetRowId(item
, NULL
), localError
);
1177 CFReleaseNull(localError
);
1179 CFReleaseNull(new_item
);
1182 if (new_item
!= NULL
&& u
->q_access_control
!= NULL
)
1183 SecDbItemSetAccessControl(new_item
, u
->q_access_control
, &localError
);
1184 result
= SecErrorPropagate(localError
, error
) && new_item
;
1186 bool item_is_sync
= SecDbItemIsSyncable(item
);
1187 result
= SecDbItemUpdate(item
, new_item
, dbt
, s3dl_should_make_tombstone(q
, item_is_sync
, item
), q
->q_uuid_from_primary_key
, error
);
1189 q
->q_changed
= true;
1190 if (item_is_sync
|| SecDbItemIsSyncable(new_item
))
1191 q
->q_sync_changed
= true;
1193 CFRelease(new_item
);
1199 if (result
&& !q
->q_changed
)
1200 result
= SecError(errSecItemNotFound
, error
, CFSTR("No items updated"));
1202 if (!query_destroy(u
, error
))
1207 static bool SecDbItemNeedAuth(SecDbItemRef item
, CFErrorRef
*error
)
1209 CFErrorRef localError
= NULL
;
1210 if (!SecDbItemEnsureDecrypted(item
, true, &localError
) && localError
&& CFErrorGetCode(localError
) == errSecAuthNeeded
) {
1212 *error
= localError
;
1216 CFReleaseSafe(localError
);
1221 s3dl_query_delete(SecDbConnectionRef dbt
, Query
*q
, CFArrayRef accessGroups
, CFErrorRef
*error
)
1223 __block
bool ok
= true;
1224 __block
bool needAuth
= false;
1225 // Only delete things that aren't tombstones, unless the client explicitly asks otherwise.
1226 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
1227 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
1228 ok
&= SecDbItemSelect(q
, dbt
, error
, NULL
, ^bool(const SecDbAttr
*attr
) {
1230 },^bool(CFMutableStringRef sql
, bool *needWhere
) {
1231 SecDbAppendWhereClause(sql
, q
, accessGroups
);
1233 },^bool(sqlite3_stmt
* stmt
, int col
) {
1234 return sqlBindWhereClause(stmt
, q
, accessGroups
, &col
, error
);
1235 }, ^(SecDbItemRef item
, bool *stop
) {
1236 // Check if item for token persitence ref
1237 if (q
->q_token_object_id
) {
1238 const SecDbAttr
*valueDataAttr
= SecDbClassAttrWithKind(item
->class, kSecDbDataAttr
, NULL
);
1239 CFDataRef valueData
= SecDbItemGetValue(item
, valueDataAttr
, NULL
);
1240 if (q
->q_token_object_id
!= NULL
&& !checkTokenObjectID(q
->q_token_object_id
, valueData
))
1243 // Check if item need to be authenticated by LocalAuthentication
1244 item
->cryptoOp
= kAKSKeyOpDelete
;
1245 if (SecDbItemNeedAuth(item
, error
)) {
1249 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1250 const SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
);
1251 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, NULL
));
1252 bool item_is_sync
= SecDbItemIsSyncable(item
);
1253 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
1254 CFReleaseSafe(storedSHA1
);
1255 ok
= SecDbItemDelete(item
, dbt
, s3dl_should_make_tombstone(q
, item_is_sync
, item
), error
);
1257 q
->q_changed
= true;
1259 q
->q_sync_changed
= true;
1262 if (ok
&& !q
->q_changed
&& !needAuth
) {
1263 ok
= SecError(errSecItemNotFound
, error
, CFSTR("Delete failed to delete anything"));
1265 return ok
&& !needAuth
;
1269 matchAnyString(CFStringRef needle
, CFStringRef
*haystack
)
1272 if (CFEqual(needle
, *haystack
))
1279 /* Return true iff the item in question should not be backed up, nor restored,
1280 but when restoring a backup the original version of the item should be
1281 added back to the keychain again after the restore completes. */
1282 bool SecItemIsSystemBound(CFDictionaryRef item
, const SecDbClass
*cls
, bool multiUser
) {
1283 CFNumberRef sysb
= CFDictionaryGetValue(item
, kSecAttrSysBound
);
1284 if (isNumber(sysb
)) {
1286 if (!CFNumberGetValue(sysb
, kCFNumberSInt32Type
, &num
))
1288 if (num
== kSecSecAttrSysBoundNot
) {
1290 } else if (num
== kSecSecAttrSysBoundPreserveDuringRestore
) {
1296 CFStringRef agrp
= CFDictionaryGetValue(item
, kSecAttrAccessGroup
);
1297 if (!isString(agrp
))
1300 if (CFEqualSafe(agrp
, kSOSInternalAccessGroup
)) {
1301 secdebug("backup", "found sysbound item: %@", item
);
1305 if (CFEqual(agrp
, CFSTR("lockdown-identities"))) {
1306 secdebug("backup", "found sys_bound item: %@", item
);
1310 if (CFEqual(agrp
, CFSTR("apple")) && cls
== genp_class()) {
1311 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1312 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
1314 if (isString(service
) && isString(account
)) {
1315 static CFStringRef mcAccounts
[] = {
1321 if (CFEqual(service
, CFSTR("com.apple.managedconfiguration"))
1322 && matchAnyString(account
, mcAccounts
))
1324 secdebug("backup", "found sys_bound item: %@", item
);
1329 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.account.CloudKit.token"))) {
1330 secdebug("backup", "found sys_bound item: %@", item
);
1334 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.account.idms.continuation-key"))) {
1335 secdebug("backup", "found sys_bound item: %@", item
);
1340 if (multiUser
&& CFEqual(agrp
, CFSTR("com.apple.apsd")) && cls
== genp_class()) {
1341 static CFStringRef pushServices
[] = {
1342 CFSTR("push.apple.com"),
1343 CFSTR("push.apple.com,PerAppToken.v0"),
1346 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1348 if (isString(service
) && matchAnyString(service
, pushServices
)) {
1349 secdebug("backup", "found sys_bound item: %@", item
);
1354 if (multiUser
&& CFEqual(agrp
, CFSTR("appleaccount")) && cls
== genp_class()) {
1355 static CFStringRef accountServices
[] = {
1356 CFSTR("com.apple.appleaccount.fmf.token"), /* temporary tokens while accout is being setup */
1357 CFSTR("com.apple.appleaccount.fmf.apptoken"),
1358 CFSTR("com.apple.appleaccount.fmip.siritoken"),
1359 CFSTR("com.apple.appleaccount.cloudkit.token"),
1362 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1364 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1365 secdebug("backup", "found exact sys_bound item: %@", item
);
1370 if (multiUser
&& CFEqual(agrp
, CFSTR("apple")) && cls
== genp_class()) {
1371 static CFStringRef accountServices
[] = {
1372 /* accounts, remove with rdar://37595482 */
1373 CFSTR("com.apple.account.AppleAccount.token"),
1374 CFSTR("com.apple.account.AppleAccount.password"),
1375 CFSTR("com.apple.account.AppleAccount.rpassword"),
1376 CFSTR("com.apple.account.idms.token"),
1377 CFSTR("com.apple.account.idms.heartbeat-token"),
1378 CFSTR("com.apple.account.idms.continuation-key"),
1379 CFSTR("com.apple.account.CloudKit.token"),
1380 CFSTR("com.apple.account.IdentityServices.password"), /* accountsd for ids */
1381 CFSTR("com.apple.account.IdentityServices.rpassword"),
1382 CFSTR("com.apple.account.IdentityServices.token"),
1384 CFSTR("BackupIDSAccountToken"),
1385 CFSTR("com.apple.ids"),
1390 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1392 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1393 secdebug("backup", "found exact sys_bound item: %@", item
);
1396 if (isString(service
) && CFStringHasPrefix(service
, CFSTR("com.apple.gs."))) {
1397 secdebug("backup", "found exact sys_bound item: %@", item
);
1400 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.facetime"))) {
1401 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
1402 if (isString(account
) && CFEqual(account
, CFSTR("registrationV1"))) {
1403 secdebug("backup", "found exact sys_bound item: %@", item
);
1409 /* accounts, remove with rdar://37595482 */
1410 if (multiUser
&& CFEqual(agrp
, CFSTR("com.apple.ind")) && cls
== genp_class()) {
1411 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1412 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.ind.registration"))) {
1413 secdebug("backup", "found exact sys_bound item: %@", item
);
1418 if (multiUser
&& CFEqual(agrp
, CFSTR("ichat")) && cls
== genp_class()) {
1419 static CFStringRef accountServices
[] = {
1423 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1425 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1426 secdebug("backup", "found exact sys_bound item: %@", item
);
1431 if (multiUser
&& CFEqual(agrp
, CFSTR("ichat")) && cls
== keys_class()) {
1432 static CFStringRef exactMatchingLabel
[] = {
1433 CFSTR("iMessage Encryption Key"),
1434 CFSTR("iMessage Signing Key"),
1436 CFStringRef label
= CFDictionaryGetValue(item
, kSecAttrLabel
);
1437 if (isString(label
)) {
1438 if (matchAnyString(label
, exactMatchingLabel
)) {
1439 secdebug("backup", "found exact sys_bound item: %@", item
);
1445 if (multiUser
&& CFEqual(agrp
, CFSTR("com.apple.rapport")) && cls
== genp_class()) {
1446 secdebug("backup", "found exact sys_bound item: %@", item
);
1450 secdebug("backup", "found non sys_bound item: %@", item
);
1454 /* Delete all items from the current keychain. If this is not an in
1455 place upgrade we don't delete items in the 'lockdown-identities'
1456 access group, this ensures that an import or restore of a backup
1457 will never overwrite an existing activation record. */
1458 static bool SecServerDeleteAll(SecDbConnectionRef dbt
, CFErrorRef
*error
) {
1459 secwarning("SecServerDeleteAll");
1461 return kc_transaction(dbt
, error
, ^{
1463 bool ok
= (SecDbExec(dbt
, CFSTR("DELETE from genp;"), error
) &&
1464 SecDbExec(dbt
, CFSTR("DELETE from inet;"), error
) &&
1465 SecDbExec(dbt
, CFSTR("DELETE from cert;"), error
) &&
1466 SecDbExec(dbt
, CFSTR("DELETE from keys;"), error
));
1471 #if TARGET_OS_IPHONE
1473 static bool DeleteAllFromTableForMUSRView(SecDbConnectionRef dbt
,
1479 sqlite3_stmt
*stmt
= NULL
;
1480 CFStringRef sql2
= NULL
;
1484 sql2
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@ AND pdmn NOT IN ('aku','akpu','cku','dku')"), sql
);
1486 sql2
= CFRetain(sql
);
1488 require(sql2
, fail
);
1490 stmt
= SecDbCopyStmt(dbt
, sql2
, NULL
, error
);
1491 require(stmt
, fail
);
1493 ok
= SecDbBindObject(stmt
, 1, musr
, error
);
1496 ok
= SecDbStep(dbt
, stmt
, error
, ^(bool *stop
) { });
1501 ok
= SecDbFinalize(stmt
, error
);
1504 secwarning("DeleteAllFromTableForMUSRView failed for %@ for musr: %@: %@", sql2
, musr
, error
? *error
: NULL
);
1506 CFReleaseNull(sql2
);
1511 bool SecServerDeleteAllForUser(SecDbConnectionRef dbt
, CFDataRef musrView
, bool keepU
, CFErrorRef
*error
) {
1512 secwarning("SecServerDeleteAllForUser for user: %@ keepU %s", musrView
, keepU
? "yes" : "no");
1514 return kc_transaction(dbt
, error
, ^{
1517 ok
= (DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM genp WHERE musr = ?"), musrView
, keepU
, error
) &&
1518 DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM inet WHERE musr = ?"), musrView
, keepU
, error
) &&
1519 DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM cert WHERE musr = ?"), musrView
, keepU
, error
) &&
1520 DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM keys WHERE musr = ?"), musrView
, keepU
, error
));
1528 struct s3dl_export_row_ctx
{
1529 struct s3dl_query_ctx qc
;
1530 keybag_handle_t dest_keybag
;
1531 enum SecItemFilter filter
;
1535 static void s3dl_export_row(sqlite3_stmt
*stmt
, void *context
) {
1536 struct s3dl_export_row_ctx
*c
= context
;
1538 SecAccessControlRef access_control
= NULL
;
1539 CFErrorRef localError
= NULL
;
1541 /* Skip akpu items when backing up, those are intentionally lost across restores. The same applies to SEP-based keys */
1542 bool skip_akpu_or_token
= c
->filter
== kSecBackupableItemFilter
;
1544 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
1545 CFMutableDictionaryRef allAttributes
= NULL
;
1546 CFMutableDictionaryRef metadataAttributes
= NULL
;
1547 CFMutableDictionaryRef secretStuff
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1548 keyclass_t keyclass
= 0;
1549 bool ok
= s3dl_item_from_col(stmt
, q
, 1, c
->qc
.accessGroups
, &allAttributes
, &access_control
, &keyclass
, &localError
);
1552 metadataAttributes
= CFDictionaryCreateMutableCopy(NULL
, 0, allAttributes
);
1553 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbReturnDataFlag
) {
1554 CFTypeRef value
= CFDictionaryGetValue(metadataAttributes
, desc
->name
);
1556 CFDictionarySetValue(secretStuff
, desc
->name
, value
);
1557 CFDictionaryRemoveValue(metadataAttributes
, desc
->name
);
1562 bool is_akpu
= access_control
? CFEqualSafe(SecAccessControlGetProtection(access_control
), kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
)
1563 // Mask generation, only look at class per se
1564 : (keyclass
& key_class_last
) == key_class_akpu
;
1565 bool is_token
= (ok
&& allAttributes
!= NULL
) ? CFDictionaryContainsKey(allAttributes
, kSecAttrTokenID
) : false;
1567 if (ok
&& allAttributes
&& !(skip_akpu_or_token
&& (is_akpu
|| is_token
))) {
1568 /* Only export sysbound items if do_sys_bound is true, only export non sysbound items otherwise. */
1569 bool do_sys_bound
= c
->filter
== kSecSysBoundItemFilter
;
1570 if (c
->filter
== kSecNoItemFilter
||
1571 SecItemIsSystemBound(allAttributes
, q
->q_class
, c
->multiUser
) == do_sys_bound
) {
1572 /* Re-encode the item. */
1573 secdebug("item", "export rowid %llu item: %@", rowid
, allAttributes
);
1574 /* The code below could be moved into handle_row. */
1575 CFDataRef pref
= _SecItemCreatePersistentRef(q
->q_class
->name
, rowid
, allAttributes
);
1577 if (c
->dest_keybag
!= KEYBAG_NONE
) {
1578 CFMutableDictionaryRef auth_attribs
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1579 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInAuthenticatedDataFlag
) {
1580 CFTypeRef value
= CFDictionaryGetValue(metadataAttributes
, desc
->name
);
1582 CFDictionaryAddValue(auth_attribs
, desc
->name
, value
);
1583 CFDictionaryRemoveValue(metadataAttributes
, desc
->name
);
1587 /* Encode and encrypt the item to the specified keybag. */
1588 CFDataRef edata
= NULL
;
1589 bool encrypted
= ks_encrypt_data(c
->dest_keybag
, access_control
, q
->q_use_cred_handle
, secretStuff
, metadataAttributes
, auth_attribs
, &edata
, false, &q
->q_error
);
1590 CFDictionaryRemoveAllValues(allAttributes
);
1591 CFRelease(auth_attribs
);
1593 CFDictionarySetValue(allAttributes
, kSecValueData
, edata
);
1594 CFReleaseSafe(edata
);
1596 seccritical("ks_encrypt_data %@,rowid=%" PRId64
": failed: %@", q
->q_class
->name
, rowid
, q
->q_error
);
1597 CFReleaseNull(q
->q_error
);
1600 if (CFDictionaryGetCount(allAttributes
)) {
1601 CFDictionarySetValue(allAttributes
, kSecValuePersistentRef
, pref
);
1602 CFArrayAppendValue((CFMutableArrayRef
)c
->qc
.result
, allAttributes
);
1605 CFReleaseSafe(pref
);
1609 OSStatus status
= SecErrorGetOSStatus(localError
);
1611 if (status
== errSecInteractionNotAllowed
&& is_akpu
) {
1612 if (skip_akpu_or_token
) {
1613 secdebug("item", "Skipping akpu item for backup");
1614 } else { // Probably failed to decrypt sysbound item. Should never be an akpu item in backup.
1615 secerror("Encountered akpu item we cannot export (filter %d), skipping. %@", c
->filter
, localError
);
1616 // TODO: Find out who is doing this somehow and make them not do this
1618 // We expect akpu items to be inaccessible when the device is locked.
1619 CFReleaseNull(localError
);
1621 /* This happens a lot when trying to migrate keychain before first unlock, so only a notice */
1622 /* If the error is "corrupted item" then we just ignore it, otherwise we save it in the query */
1623 secinfo("item","Could not export item for rowid %llu: %@", rowid
, localError
);
1625 if (status
== errSecDecode
) {
1626 CFReleaseNull(localError
);
1628 CFReleaseSafe(q
->q_error
);
1629 q
->q_error
= localError
;
1633 CFReleaseNull(access_control
);
1634 CFReleaseNull(allAttributes
);
1635 CFReleaseNull(metadataAttributes
);
1636 CFReleaseNull(secretStuff
);
1640 SecCreateKeybagUUID(keybag_handle_t keybag
)
1642 #if !TARGET_HAS_KEYSTORE
1647 if (aks_get_bag_uuid(keybag
, uuid
) != KERN_SUCCESS
)
1649 uuid_unparse_lower(uuid
, uuidstr
);
1650 return CFStringCreateWithCString(NULL
, uuidstr
, kCFStringEncodingUTF8
);
1656 SecServerCopyKeychainPlist(SecDbConnectionRef dbt
,
1657 SecurityClient
*client
,
1658 keybag_handle_t src_keybag
,
1659 keybag_handle_t dest_keybag
,
1660 enum SecItemFilter filter
,
1661 CFErrorRef
*error
) {
1662 CFMutableDictionaryRef keychain
;
1663 keychain
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1664 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1666 bool inMultiUser
= false;
1667 CFStringRef keybaguuid
= NULL
;
1668 Query q
= { .q_keybag
= src_keybag
,
1673 if (error
&& !*error
)
1674 SecError(errSecAllocate
, error
, CFSTR("Can't create keychain dictionary"));
1679 kSecReturnDataMask
|
1680 kSecReturnAttributesMask
|
1681 kSecReturnPersistentRefMask
;
1682 q
.q_limit
= kSecMatchUnlimited
;
1683 q
.q_skip_acl_items
= true;
1686 #if TARGET_OS_IPHONE
1687 if (client
&& client
->inMultiUser
) {
1688 q
.q_musrView
= SecMUSRCreateActiveUserUUID(client
->uid
);
1693 q
.q_musrView
= SecMUSRGetSingleUserKeychainUUID();
1694 CFRetain(q
.q_musrView
);
1696 keybaguuid
= SecCreateKeybagUUID(dest_keybag
);
1698 CFDictionarySetValue(keychain
, kSecBackupKeybagUUIDKey
, keybaguuid
);
1702 /* Get rid of this duplicate. */
1703 const SecDbClass
*SecDbClasses
[] = {
1710 for (class_ix
= 0; class_ix
< array_size(SecDbClasses
);
1712 q
.q_class
= SecDbClasses
[class_ix
];
1713 struct s3dl_export_row_ctx ctx
= {
1714 .qc
= { .q
= &q
, .dbt
= dbt
},
1715 .dest_keybag
= dest_keybag
, .filter
= filter
,
1716 .multiUser
= inMultiUser
,
1719 secnotice("item", "exporting %ssysbound class '%@'", filter
!= kSecSysBoundItemFilter
? "non-" : "", q
.q_class
->name
);
1721 CFErrorRef localError
= NULL
;
1722 if (s3dl_query(s3dl_export_row
, &ctx
, &localError
)) {
1723 secnotice("item", "exporting class '%@' complete", q
.q_class
->name
);
1724 if (CFArrayGetCount(ctx
.qc
.result
)) {
1725 SecSignpostBackupCount(SecSignpostImpulseBackupClassCount
, q
.q_class
->name
, CFArrayGetCount(ctx
.qc
.result
), filter
);
1726 CFDictionaryAddValue(keychain
, q
.q_class
->name
, ctx
.qc
.result
);
1730 OSStatus status
= (OSStatus
)CFErrorGetCode(localError
);
1731 if (status
== errSecItemNotFound
) {
1732 secnotice("item", "exporting class '%@' complete (no items)", q
.q_class
->name
);
1733 CFRelease(localError
);
1735 secerror("exporting class '%@' failed: %@", q
.q_class
->name
, localError
);
1737 CFReleaseSafe(*error
);
1738 *error
= localError
;
1740 CFRelease(localError
);
1742 CFReleaseNull(keychain
);
1743 CFReleaseNull(ctx
.qc
.result
);
1747 CFReleaseNull(ctx
.qc
.result
);
1751 CFReleaseNull(q
.q_musrView
);
1752 CFReleaseNull(keybaguuid
);
1757 struct SecServerImportClassState
{
1758 SecDbConnectionRef dbt
;
1760 keybag_handle_t src_keybag
;
1761 keybag_handle_t dest_keybag
;
1762 SecurityClient
*client
;
1763 enum SecItemFilter filter
;
1766 struct SecServerImportItemState
{
1767 const SecDbClass
*class;
1768 struct SecServerImportClassState
*s
;
1772 SecServerImportItem(const void *value
, void *context
)
1774 struct SecServerImportItemState
*state
= (struct SecServerImportItemState
*)context
;
1775 bool inMultiUser
= false;
1776 #if TARGET_OS_IPHONE
1777 if (state
->s
->client
->inMultiUser
)
1781 if (state
->s
->error
)
1784 if (!isDictionary(value
)) {
1785 SecError(errSecParam
, &state
->s
->error
, CFSTR("value %@ is not a dictionary"), value
);
1789 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
1791 secdebug("item", "Import Item : %@", dict
);
1793 SecDbItemRef item
= NULL
;
1795 /* This is sligthly confusing:
1796 - During upgrade all items are exported with KEYBAG_NONE.
1797 - During restore from backup, existing sys_bound items are exported with KEYBAG_NONE, and are exported as dictionary of attributes.
1798 - Item in the actual backup are export with a real keybag, and are exported as encrypted v_Data and v_PersistentRef
1800 if (state
->s
->src_keybag
== KEYBAG_NONE
) {
1801 item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, state
->class, dict
, state
->s
->dest_keybag
, &state
->s
->error
);
1803 item
= SecDbItemCreateWithBackupDictionary(kCFAllocatorDefault
, state
->class, dict
, state
->s
->src_keybag
, state
->s
->dest_keybag
, &state
->s
->error
);
1806 /* If item is NULL here, control flow ends up at the end where error is cleared. */
1807 if (item
&& !SecDbItemEnsureDecrypted(item
, true, &state
->s
->error
)) {
1808 secdebug("item", "Failed to import item because of decryption failure: %@", state
->s
->error
);
1809 CFReleaseNull(item
);
1810 /* No early return; as just above, go to the end where error is cleared. */
1813 /* We use the kSecSysBoundItemFilter to indicate that we don't
1814 * preserve rowid's during import.
1816 if (item
&& item
->attributes
&& state
->s
->filter
== kSecBackupableItemFilter
) {
1819 /* We don't filter non sys_bound items during import since we know we
1820 * will never have any in this case.
1822 if (SecItemIsSystemBound(item
->attributes
, state
->class, inMultiUser
)) {
1823 secdebug("item", "skipping backup of item: %@", dict
);
1824 CFReleaseNull(item
);
1829 * Don't bother with u items when in edu mode since our current backup system
1830 * don't keep track of items that blongs to the device (u) but rather just
1831 * merge them into one blob.
1833 if (inMultiUser
&& (pdmu
= CFDictionaryGetValue(item
->attributes
, kSecAttrAccessible
))) {
1834 if (CFEqual(pdmu
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) ||
1835 CFEqual(pdmu
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
) ||
1836 CFEqual(pdmu
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) ||
1837 CFEqual(pdmu
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
))
1839 secdebug("item", "Skipping KU item : %@", dict
);
1840 CFReleaseNull(item
);
1845 /* Avoid importing token-based items. Although newer backups should not have them,
1846 * older (iOS9, iOS10.0) produced backups with token-based items.
1848 if (CFDictionaryContainsKey(item
->attributes
, kSecAttrTokenID
)) {
1849 secdebug("item", "Skipping token-based item : %@", dict
);
1850 CFReleaseNull(item
);
1859 if (item
&& item
->attributes
) {
1860 CFDataRef musr
= NULL
;
1861 CFDataRef musrBackup
= CFDictionaryGetValue(item
->attributes
, kSecAttrMultiUser
);
1862 CFDataRef systemKeychainUUID
= SecMUSRGetSystemKeychainUUID();
1863 bool systemKeychain
= CFEqualSafe(musrBackup
, systemKeychainUUID
);
1865 #if TARGET_OS_IPHONE
1866 if (state
->s
->client
&& state
->s
->client
->inMultiUser
) {
1867 if (systemKeychain
) {
1868 secwarning("system keychain not allowed in multi user mode for item: %@", item
);
1870 musr
= SecMUSRCreateActiveUserUUID(state
->s
->client
->uid
);
1875 if (systemKeychain
) {
1876 musr
= SecMUSRCopySystemKeychainUUID();
1878 musr
= SecMUSRGetSingleUserKeychainUUID();
1883 CFReleaseNull(item
);
1885 SecDbItemSetValueWithName(item
, CFSTR("musr"), musr
, &state
->s
->error
);
1897 if(state
->s
->filter
!= kSecSysBoundItemFilter
) {
1898 SecDbItemExtractRowIdFromBackupDictionary(item
, dict
, &state
->s
->error
);
1900 SecDbItemInferSyncable(item
, &state
->s
->error
);
1901 insertStatus
= SecDbItemInsert(item
, state
->s
->dbt
, &state
->s
->error
);
1902 if (!insertStatus
) {
1904 When running in EduMode, multiple users share the same
1905 keychain and unfortionaly the rowid is used a
1906 persistant reference and is part of the contraints (its
1907 UNIQUE), so lets clear the rowid and try to insert the
1910 This even happens for normal operation because of
1911 SysBound entries, so in case of a failure, lets try
1912 again to insert the record.
1914 SecDbItemClearRowId(item
, NULL
);
1915 SecDbItemInsert(item
, state
->s
->dbt
, &state
->s
->error
);
1919 /* Reset error if we had one, since we just skip the current item
1920 and continue importing what we can. */
1921 if (state
->s
->error
) {
1922 secwarning("Failed to import an item (%@) of class '%@': %@ - ignoring error.",
1923 item
, state
->class->name
, state
->s
->error
);
1924 CFReleaseNull(state
->s
->error
);
1927 CFReleaseSafe(item
);
1930 static void SecServerImportClass(const void *key
, const void *value
,
1932 struct SecServerImportClassState
*state
=
1933 (struct SecServerImportClassState
*)context
;
1936 if (!isString(key
)) {
1937 SecError(errSecParam
, &state
->error
, CFSTR("class name %@ is not a string"), key
);
1940 /* ignore the Keybag UUID */
1941 if (CFEqual(key
, kSecBackupKeybagUUIDKey
))
1943 const SecDbClass
*class = kc_class_with_name(key
);
1945 secwarning("Ignoring unknown key class '%@'", key
);
1948 if (class == identity_class()) {
1949 SecError(errSecParam
, &state
->error
, CFSTR("attempt to import an identity"));
1952 struct SecServerImportItemState item_state
= {
1953 .class = class, .s
= state
,
1955 if (isArray(value
)) {
1956 CFArrayRef items
= (CFArrayRef
)value
;
1957 secwarning("Import %ld items of class %@ (filter %d)", (long)CFArrayGetCount(items
), key
, state
->filter
);
1958 SecSignpostBackupCount(SecSignpostImpulseRestoreClassCount
, class->name
, CFArrayGetCount(items
), state
->filter
);
1959 CFArrayApplyFunction(items
, CFRangeMake(0, CFArrayGetCount(items
)),
1960 SecServerImportItem
, &item_state
);
1961 } else if (isDictionary(value
)) {
1962 CFDictionaryRef item
= (CFDictionaryRef
)value
;
1963 secwarning("Import %ld items of class %@ (filter %d)", (long)1, key
, state
->filter
);
1964 SecSignpostBackupCount(SecSignpostImpulseRestoreClassCount
, class->name
, 1, state
->filter
);
1965 SecServerImportItem(item
, &item_state
);
1967 secwarning("Unknown value type for class %@ (filter %d)", key
, state
->filter
);
1971 bool SecServerImportKeychainInPlist(SecDbConnectionRef dbt
, SecurityClient
*client
,
1972 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
1973 CFDictionaryRef keychain
, enum SecItemFilter filter
,
1974 bool removeKeychainContent
, CFErrorRef
*error
) {
1975 CFStringRef keybaguuid
= NULL
;
1978 CFDictionaryRef sys_bound
= NULL
;
1979 if (filter
== kSecBackupableItemFilter
) {
1980 /* Grab a copy of all the items for which SecItemIsSystemBound()
1982 require(sys_bound
= SecServerCopyKeychainPlist(dbt
, client
, KEYBAG_DEVICE
,
1983 KEYBAG_NONE
, kSecSysBoundItemFilter
,
1988 * Validate the uuid of the source keybag matches what we have in the backup
1990 keybaguuid
= SecCreateKeybagUUID(src_keybag
);
1992 CFStringRef uuid
= CFDictionaryGetValue(keychain
, kSecBackupKeybagUUIDKey
);
1993 if (isString(uuid
)) {
1994 require_action(CFEqual(keybaguuid
, uuid
), errOut
,
1995 SecError(errSecDecode
, error
, CFSTR("Keybag UUID (%@) mismatch with backup (%@)"),
2002 * Shared iPad is very special, it always delete's the user keychain, and never merge content
2004 if (client
->inMultiUser
) {
2005 CFDataRef musrView
= SecMUSRCreateActiveUserUUID(client
->uid
);
2006 require_action(musrView
, errOut
, ok
= false);
2007 require_action(ok
= SecServerDeleteAllForUser(dbt
, musrView
, true, error
), errOut
, CFReleaseNull(musrView
));
2008 CFReleaseNull(musrView
);
2013 * Delete everything in the keychain.
2014 * We don't want this if we're restoring backups because we probably already synced stuff over
2016 if (removeKeychainContent
) {
2017 require(ok
= SecServerDeleteAll(dbt
, error
), errOut
);
2019 // Custom hack to support bluetooth's workflow for 11.3. Should be removed in a future release.
2020 __block CFErrorRef btError
= NULL
;
2021 bool deletedBT
= kc_transaction(dbt
, &btError
, ^bool{
2023 #define EXCLUDE_AGRPS "'com.apple.security.sos', 'com.apple.security.sos-usercredential', 'com.apple.security.ckks', 'com.apple.security.egoIdentities', 'com.apple.security.octagon'"
2025 bool tok
= SecDbExec(dbt
, CFSTR("DELETE FROM genp WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS
");"), &btError
);
2026 tok
&= SecDbExec(dbt
, CFSTR("DELETE FROM inet WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS
");"), &btError
);
2027 tok
&= SecDbExec(dbt
, CFSTR("DELETE FROM cert WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS
");"), &btError
);
2028 tok
&= SecDbExec(dbt
, CFSTR("DELETE FROM keys WHERE sync = 0 AND NOT agrp IN (" EXCLUDE_AGRPS
");"), &btError
);
2030 #undef EXCLUDE_AGRPS
2034 secerror("Unable to delete nonsyncable items prior to keychain restore: %@", btError
);
2036 secnotice("restore", "Successfully deleted nonsyncable items");
2038 CFReleaseNull(btError
);
2042 struct SecServerImportClassState state
= {
2044 .src_keybag
= src_keybag
,
2045 .dest_keybag
= dest_keybag
,
2049 /* Import the provided items, preserving rowids. */
2050 secwarning("Restoring backup items '%ld'", (long)CFDictionaryGetCount(keychain
));
2051 CFDictionaryApplyFunction(keychain
, SecServerImportClass
, &state
);
2054 state
.src_keybag
= KEYBAG_NONE
;
2055 /* Import the items we preserved with random rowids. */
2056 state
.filter
= kSecSysBoundItemFilter
;
2057 secwarning("Restoring sysbound items '%ld'", (long)CFDictionaryGetCount(sys_bound
));
2058 CFDictionaryApplyFunction(sys_bound
, SecServerImportClass
, &state
);
2062 CFReleaseSafe(*error
);
2063 *error
= state
.error
;
2065 CFRelease(state
.error
);
2070 // If CKKS had spun up, it's very likely that we just deleted its data.
2071 // Tell it to perform a local resync.
2073 SecCKKSPerformLocalResync();
2077 CFReleaseSafe(sys_bound
);
2078 CFReleaseSafe(keybaguuid
);
2084 SecServerBackupGetKeybagUUID(CFDictionaryRef keychain
, CFErrorRef
*error
)
2086 CFStringRef uuid
= CFDictionaryGetValue(keychain
, kSecBackupKeybagUUIDKey
);
2087 if (!isString(uuid
)) {
2088 SecError(errSecDecode
, error
, CFSTR("Missing or invalid %@ in backup dictionary"), kSecBackupKeybagUUIDKey
);
2094 #pragma mark - key rolling support
2097 struct check_generation_ctx
{
2098 struct s3dl_query_ctx query_ctx
;
2099 uint32_t current_generation
;
2102 static void check_generation(sqlite3_stmt
*stmt
, void *context
) {
2103 struct check_generation_ctx
*c
= context
;
2104 CFDataRef blob
= NULL
;
2106 const uint8_t *cursor
= NULL
;
2108 keyclass_t keyclass
;
2109 uint32_t current_generation
= c
->current_generation
;
2111 require(blob
= s3dl_copy_data_from_col(stmt
, 1, &c
->query_ctx
.q
->q_error
), out
);
2112 blobLen
= CFDataGetLength(blob
);
2113 cursor
= CFDataGetBytePtr(blob
);
2115 /* Check for underflow, ensuring we have at least one full AES block left. */
2116 if (blobLen
< sizeof(version
) + sizeof(keyclass
)) {
2117 SecError(errSecDecode
, &c
->query_ctx
.q
->q_error
, CFSTR("check_generation: Check for underflow"));
2121 version
= *((uint32_t *)cursor
);
2122 cursor
+= sizeof(version
);
2124 (void) version
; // TODO: do something with the version number.
2126 keyclass
= *((keyclass_t
*)cursor
);
2128 // TODO: export get_key_gen macro
2129 if (((keyclass
& ~key_class_last
) == 0) != (current_generation
== 0)) {
2130 c
->query_ctx
.found
++;
2133 CFReleaseSafe(blob
);
2137 c
->query_ctx
.found
++;
2138 CFReleaseSafe(blob
);
2141 bool s3dl_dbt_keys_current(SecDbConnectionRef dbt
, uint32_t current_generation
, CFErrorRef
*error
) {
2142 CFErrorRef localError
= NULL
;
2143 struct check_generation_ctx ctx
= { .query_ctx
= { .dbt
= dbt
}, .current_generation
= current_generation
};
2145 const SecDbClass
*classes
[] = {
2152 for (size_t class_ix
= 0; class_ix
< array_size(classes
); ++class_ix
) {
2153 Query
*q
= query_create(classes
[class_ix
], NULL
, NULL
, &localError
);
2157 ctx
.query_ctx
.q
= q
;
2158 q
->q_limit
= kSecMatchUnlimited
;
2160 bool ok
= s3dl_query(check_generation
, &ctx
, &localError
);
2161 query_destroy(q
, NULL
);
2162 CFReleaseNull(ctx
.query_ctx
.result
);
2164 if (!ok
&& localError
&& (CFErrorGetCode(localError
) == errSecItemNotFound
)) {
2165 CFReleaseNull(localError
);
2168 secerror("Class %@ not up to date", classes
[class_ix
]->name
);
2174 bool s3dl_dbt_update_keys(SecDbConnectionRef dbt
, SecurityClient
*client
, CFErrorRef
*error
) {
2175 return SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
2176 __block
bool ok
= false;
2177 uint32_t keystore_generation_status
;
2179 /* can we migrate to new class keys right now? */
2180 if (!aks_generation(KEYBAG_DEVICE
, generation_noop
, &keystore_generation_status
) &&
2181 (keystore_generation_status
& generation_change_in_progress
)) {
2183 /* take a lock assertion */
2184 bool operated_while_unlocked
= SecAKSDoWithUserBagLockAssertion(error
, ^{
2185 CFErrorRef localError
= NULL
;
2186 CFDictionaryRef backup
= SecServerCopyKeychainPlist(dbt
, NULL
,
2187 KEYBAG_DEVICE
, KEYBAG_NONE
, kSecNoItemFilter
, &localError
);
2190 secerror("Ignoring export error: %@ during roll export", localError
);
2191 CFReleaseNull(localError
);
2193 // 'true' argument: we're replacing everything with newly wrapped entries so remove the old stuff
2194 ok
= SecServerImportKeychainInPlist(dbt
, client
, KEYBAG_NONE
,
2195 KEYBAG_DEVICE
, backup
, kSecNoItemFilter
, true, &localError
);
2197 secerror("Ignoring export error: %@ during roll export", localError
);
2198 CFReleaseNull(localError
);
2203 if (!operated_while_unlocked
)
2206 ok
= SecError(errSecBadReq
, error
, CFSTR("No key roll in progress."));