2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecItemDb.c - CoreFoundation-based constants and functions for
26 access to Security items (certificates, keys, identities, and
30 #include <securityd/SecItemDb.h>
31 #include <utilities/SecAKSWrappers.h>
33 #include <securityd/SecDbKeychainItem.h>
34 #include <securityd/SecItemSchema.h>
35 #include <securityd/SecItemServer.h>
36 #include <Security/SecAccessControlPriv.h>
37 #include <Security/SecBasePriv.h>
38 #include <Security/SecItem.h>
39 #include <Security/SecItemPriv.h>
40 #include <Security/SecItemInternal.h>
41 #include <securityd/SOSCloudCircleServer.h>
42 #include <utilities/array_size.h>
43 #include <utilities/SecIOFormat.h>
44 #include <SecAccessControlPriv.h>
45 #include <uuid/uuid.h>
47 #define kSecBackupKeybagUUIDKey CFSTR("keybag-uuid")
49 const SecDbAttr
*SecDbAttrWithKey(const SecDbClass
*c
,
52 /* Special case: identites can have all attributes of either cert
54 if (c
== &identity_class
) {
55 const SecDbAttr
*desc
;
56 if (!(desc
= SecDbAttrWithKey(&cert_class
, key
, 0)))
57 desc
= SecDbAttrWithKey(&keys_class
, key
, error
);
62 SecDbForEachAttr(c
, a
) {
63 if (CFEqual(a
->name
, key
))
66 if (CFEqual(kSecAttrNoLegacy
, key
)) {
67 return NULL
; /* results in no ops for this attribute */
71 SecError(errSecNoSuchAttr
, error
, CFSTR("attribute %@ not found in class %@"), key
, c
->name
);
76 bool kc_transaction(SecDbConnectionRef dbt
, CFErrorRef
*error
, bool(^perform
)()) {
77 __block
bool ok
= true;
78 return ok
&& SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
79 ok
= *commit
= perform();
83 static CFStringRef
SecDbGetKindSQL(SecDbAttrKind kind
) {
89 case kSecDbPrimaryKeyAttr
:
90 case kSecDbEncryptedDataAttr
:
92 case kSecDbAccessAttr
:
93 case kSecDbStringAttr
:
95 case kSecDbNumberAttr
:
98 return CFSTR("INTEGER");
100 case kSecDbCreationDateAttr
:
101 case kSecDbModificationDateAttr
:
102 return CFSTR("REAL");
103 case kSecDbRowIdAttr
:
104 return CFSTR("INTEGER PRIMARY KEY AUTOINCREMENT");
105 case kSecDbAccessControlAttr
:
106 case kSecDbUTombAttr
:
107 /* This attribute does not exist in the DB. */
112 static void SecDbAppendUnique(CFMutableStringRef sql
, CFStringRef value
, bool *haveUnique
) {
115 CFStringAppend(sql
, CFSTR("UNIQUE("));
117 SecDbAppendElement(sql
, value
, haveUnique
);
120 static void SecDbAppendCreateTableWithClass(CFMutableStringRef sql
, const SecDbClass
*c
) {
121 CFStringAppendFormat(sql
, 0, CFSTR("CREATE TABLE %@("), c
->name
);
122 SecDbForEachAttrWithMask(c
,desc
,kSecDbInFlag
) {
123 CFStringAppendFormat(sql
, 0, CFSTR("%@ %@"), desc
->name
, SecDbGetKindSQL(desc
->kind
));
124 if (desc
->flags
& kSecDbNotNullFlag
)
125 CFStringAppend(sql
, CFSTR(" NOT NULL"));
126 if (desc
->flags
& kSecDbDefault0Flag
)
127 CFStringAppend(sql
, CFSTR(" DEFAULT 0"));
128 if (desc
->flags
& kSecDbDefaultEmptyFlag
)
129 CFStringAppend(sql
, CFSTR(" DEFAULT ''"));
130 CFStringAppend(sql
, CFSTR(","));
133 bool haveUnique
= false;
134 SecDbForEachAttrWithMask(c
,desc
,kSecDbPrimaryKeyFlag
| kSecDbInFlag
) {
135 SecDbAppendUnique(sql
, desc
->name
, &haveUnique
);
138 CFStringAppend(sql
, CFSTR(")"));
140 CFStringAppend(sql
, CFSTR(");"));
143 SecDbForEachAttrWithMask(c
,desc
, kSecDbIndexFlag
| kSecDbInFlag
) {
144 CFStringAppendFormat(sql
, 0, CFSTR("CREATE INDEX %@%@ ON %@(%@);"), c
->name
, desc
->name
, c
->name
, desc
->name
);
148 static void SecDbAppendDropTableWithClass(CFMutableStringRef sql
, const SecDbClass
*c
) {
149 CFStringAppendFormat(sql
, 0, CFSTR("DROP TABLE %@;"), c
->name
);
152 static CFDataRef
SecPersistentRefCreateWithItem(SecDbItemRef item
, CFErrorRef
*error
) {
153 sqlite3_int64 row_id
= SecDbItemGetRowId(item
, error
);
155 return _SecItemMakePersistentRef(SecDbItemGetClass(item
)->name
, row_id
);
159 bool SecItemDbCreateSchema(SecDbConnectionRef dbt
, const SecDbSchema
*schema
, bool includeVersion
, CFErrorRef
*error
)
161 __block
bool ok
= true;
162 CFMutableStringRef sql
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
163 for (const SecDbClass
* const *pclass
= schema
->classes
; *pclass
; ++pclass
) {
164 SecDbAppendCreateTableWithClass(sql
, *pclass
);
166 if (includeVersion
) {
167 CFStringAppendFormat(sql
, NULL
, CFSTR("INSERT INTO tversion(version,minor) VALUES(%d, %d);"),
168 schema
->majorVersion
, schema
->minorVersion
);
170 CFStringPerformWithCString(sql
, ^(const char *sql_string
) {
171 ok
= SecDbErrorWithDb(sqlite3_exec(SecDbHandle(dbt
), sql_string
, NULL
, NULL
, NULL
),
172 SecDbHandle(dbt
), error
, CFSTR("sqlite3_exec: %s"), sql_string
);
178 bool SecItemDbDeleteSchema(SecDbConnectionRef dbt
, const SecDbSchema
*schema
, CFErrorRef
*error
)
180 __block
bool ok
= true;
181 CFMutableStringRef sql
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
182 for (const SecDbClass
* const *pclass
= schema
->classes
; *pclass
; ++pclass
) {
183 SecDbAppendDropTableWithClass(sql
, *pclass
);
185 CFStringPerformWithCString(sql
, ^(const char *sql_string
) {
186 ok
= SecDbErrorWithDb(sqlite3_exec(SecDbHandle(dbt
), sql_string
, NULL
, NULL
, NULL
),
187 SecDbHandle(dbt
), error
, CFSTR("sqlite3_exec: %s"), sql_string
);
193 CFTypeRef
SecDbItemCopyResult(SecDbItemRef item
, ReturnTypeMask return_type
, CFErrorRef
*error
) {
196 if (return_type
== 0) {
197 /* Caller isn't interested in any results at all. */
199 } else if (return_type
== kSecReturnDataMask
) {
200 a_result
= SecDbItemGetCachedValueWithName(item
, kSecValueData
);
202 CFRetainSafe(a_result
);
204 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
206 } else if (return_type
== kSecReturnPersistentRefMask
) {
207 a_result
= SecPersistentRefCreateWithItem(item
, error
);
209 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableForCFTypes(CFGetAllocator(item
));
210 /* We need to return more than one value. */
211 if (return_type
& kSecReturnRefMask
) {
212 CFDictionarySetValue(dict
, kSecClass
, SecDbItemGetClass(item
)->name
);
214 CFOptionFlags mask
= (((return_type
& kSecReturnDataMask
|| return_type
& kSecReturnRefMask
) ? kSecDbReturnDataFlag
: 0) |
215 ((return_type
& kSecReturnAttributesMask
|| return_type
& kSecReturnRefMask
) ? kSecDbReturnAttrFlag
: 0));
216 SecDbForEachAttr(SecDbItemGetClass(item
), desc
) {
217 if ((desc
->flags
& mask
) != 0) {
218 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
219 if (value
&& !CFEqual(kCFNull
, value
)) {
220 CFDictionarySetValue(dict
, desc
->name
, value
);
221 } else if (value
== NULL
) {
227 if (return_type
& kSecReturnPersistentRefMask
) {
228 CFDataRef pref
= SecPersistentRefCreateWithItem(item
, error
);
229 CFDictionarySetValue(dict
, kSecValuePersistentRef
, pref
);
239 /* AUDIT[securityd](done):
240 attributes (ok) is a caller provided dictionary, only its cf type has
244 s3dl_query_add(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
, CFErrorRef
*error
)
246 if (query_match_count(q
) != 0)
247 return errSecItemMatchUnsupported
;
249 /* Add requires a class to be specified unless we are adding a ref. */
250 if (q
->q_use_item_list
)
251 return errSecUseItemListUnsupported
;
253 /* Actual work here. */
254 SecDbItemRef item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, q
->q_class
, q
->q_item
, KEYBAG_DEVICE
, error
);
257 if (SecDbItemIsTombstone(item
))
258 SecDbItemSetValue(item
, &v7utomb
, q
->q_use_tomb
? q
->q_use_tomb
: kCFBooleanTrue
, NULL
);
262 ok
= SecDbItemSetValueWithName(item
, CFSTR("v_Data"), q
->q_data
, error
);
264 ok
= SecDbItemSetRowId(item
, q
->q_row_id
, error
);
266 ok
= SecDbItemSetValueWithName(item
, CFSTR("musr"), q
->q_musrView
, error
);
267 SecDbItemSetCredHandle(item
, q
->q_use_cred_handle
);
270 ok
= SecDbItemInsert(item
, dbt
, error
);
273 if (result
&& q
->q_return_type
) {
274 *result
= SecDbItemCopyResult(item
, q
->q_return_type
, error
);
277 if (!ok
&& error
&& *error
) {
278 if (CFEqual(CFErrorGetDomain(*error
), kSecDbErrorDomain
) && CFErrorGetCode(*error
) == SQLITE_CONSTRAINT
) {
279 CFReleaseNull(*error
);
280 SecError(errSecDuplicateItem
, error
, CFSTR("duplicate item %@"), item
);
281 } else if (CFEqual(CFErrorGetDomain(*error
), kSecErrorDomain
) && CFErrorGetCode(*error
) == errSecDecode
) { //handle situation when item have pdmn=akpu but passcode is not set
282 CFTypeRef value
= SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbAccessAttr
, error
), error
);
283 if (value
&& CFEqual(value
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
)) {
284 CFReleaseNull(*error
);
285 SecError(errSecAuthFailed
, error
, CFSTR("authentication failed"));
292 if (SecDbItemIsSyncable(item
))
293 q
->q_sync_changed
= true;
296 secdebug("dbitem", "inserting item %@%s%@", item
, ok
? "" : "failed: ", ok
|| error
== NULL
? (CFErrorRef
)CFSTR("") : *error
);
303 typedef void (*s3dl_handle_row
)(sqlite3_stmt
*stmt
, void *context
);
306 s3dl_copy_data_from_col(sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
307 return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt
, col
),
308 sqlite3_column_bytes(stmt
, col
),
313 s3dl_item_from_col(sqlite3_stmt
*stmt
, Query
*q
, int col
, CFArrayRef accessGroups
,
314 CFMutableDictionaryRef
*item
, SecAccessControlRef
*access_control
, CFErrorRef
*error
) {
315 CFDataRef edata
= NULL
;
317 require(edata
= s3dl_copy_data_from_col(stmt
, col
, error
), out
);
318 ok
= s3dl_item_from_data(edata
, q
, accessGroups
, item
, access_control
, error
);
321 CFReleaseSafe(edata
);
325 struct s3dl_query_ctx
{
327 CFArrayRef accessGroups
;
328 SecDbConnectionRef dbt
;
333 /* Return whatever the caller requested based on the value of q->q_return_type.
334 keys and values must be 3 larger than attr_count in size to accomadate the
335 optional data, class and persistent ref results. This is so we can use
336 the CFDictionaryCreate() api here rather than appending to a
337 mutable dictionary. */
338 static CF_RETURNS_RETAINED CFTypeRef
handle_result(Query
*q
, CFMutableDictionaryRef item
,
339 sqlite_int64 rowid
) {
342 data
= CFDictionaryGetValue(item
, kSecValueData
);
343 if (q
->q_return_type
== 0) {
344 /* Caller isn't interested in any results at all. */
346 } else if (q
->q_return_type
== kSecReturnDataMask
) {
351 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
353 } else if (q
->q_return_type
== kSecReturnPersistentRefMask
) {
354 a_result
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
356 /* We need to return more than one value. */
357 if (q
->q_return_type
& kSecReturnRefMask
) {
358 CFDictionarySetValue(item
, kSecClass
, q
->q_class
->name
);
359 } else if ((q
->q_return_type
& kSecReturnAttributesMask
)) {
360 if (!(q
->q_return_type
& kSecReturnDataMask
)) {
361 CFDictionaryRemoveValue(item
, kSecValueData
);
365 CFDictionaryRemoveAllValues(item
);
366 if ((q
->q_return_type
& kSecReturnDataMask
) && data
) {
367 CFDictionarySetValue(item
, kSecValueData
, data
);
371 if (q
->q_return_type
& kSecReturnPersistentRefMask
) {
372 CFDataRef pref
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
373 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
384 static void s3dl_merge_into_dict(const void *key
, const void *value
, void *context
) {
385 CFDictionarySetValue(context
, key
, value
);
388 static void s3dl_query_row(sqlite3_stmt
*stmt
, void *context
) {
389 struct s3dl_query_ctx
*c
= context
;
392 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
393 CFMutableDictionaryRef item
;
394 bool ok
= s3dl_item_from_col(stmt
, q
, 1, c
->accessGroups
, &item
, NULL
, &q
->q_error
);
396 OSStatus status
= SecErrorGetOSStatus(q
->q_error
);
397 // errSecDecode means the item is corrupted, stash it for delete.
398 if (status
== errSecDecode
) {
399 secwarning("ignoring corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, rowid
, q
->q_error
);
401 CFDataRef edata
= s3dl_copy_data_from_col(stmt
, 1, NULL
);
402 CFMutableStringRef edatastring
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
404 CFStringAppendEncryptedData(edatastring
, edata
);
405 secnotice("item", "corrupted edata=%@", edatastring
);
407 CFReleaseSafe(edata
);
408 CFReleaseSafe(edatastring
);
410 CFReleaseNull(q
->q_error
);
411 } else if (status
== errSecAuthNeeded
) {
412 secwarning("Authentication is needed for %@,rowid=%" PRId64
" (%" PRIdOSStatus
"): %@", q
->q_class
->name
, rowid
, status
, q
->q_error
);
414 secerror("decode %@,rowid=%" PRId64
" failed (%" PRIdOSStatus
"): %@", q
->q_class
->name
, rowid
, status
, q
->q_error
);
416 // q->q_error will be released appropriately by a call to query_error
423 if (q
->q_class
== &identity_class
) {
424 // TODO: Use col 2 for key rowid and use both rowids in persistent ref.
426 CFMutableDictionaryRef key
;
427 /* TODO : if there is a errSecDecode error here, we should cleanup */
428 if (!s3dl_item_from_col(stmt
, q
, 3, c
->accessGroups
, &key
, NULL
, &q
->q_error
) || !key
)
431 CFDataRef certData
= CFDictionaryGetValue(item
, kSecValueData
);
433 CFDictionarySetValue(key
, kSecAttrIdentityCertificateData
, certData
);
434 CFDictionaryRemoveValue(item
, kSecValueData
);
437 CFDataRef certTokenID
= CFDictionaryGetValue(item
, kSecAttrTokenID
);
439 CFDictionarySetValue(key
, kSecAttrIdentityCertificateTokenID
, certTokenID
);
440 CFDictionaryRemoveValue(item
, kSecAttrTokenID
);
442 CFDictionaryApplyFunction(item
, s3dl_merge_into_dict
, key
);
447 if (!match_item(c
->dbt
, q
, c
->accessGroups
, item
))
450 CFTypeRef a_result
= handle_result(q
, item
, rowid
);
452 if (a_result
== kCFNull
) {
453 /* Caller wasn't interested in a result, but we still
454 count this row as found. */
455 CFRelease(a_result
); // Help shut up clang
456 } else if (q
->q_limit
== 1) {
457 c
->result
= a_result
;
459 CFArrayAppendValue((CFMutableArrayRef
)c
->result
, a_result
);
470 SecDbAppendWhereROWID(CFMutableStringRef sql
,
471 CFStringRef col
, sqlite_int64 row_id
,
474 SecDbAppendWhereOrAnd(sql
, needWhere
);
475 CFStringAppendFormat(sql
, NULL
, CFSTR("%@=%lld"), col
, row_id
);
480 SecDbAppendWhereAttrs(CFMutableStringRef sql
, const Query
*q
, bool *needWhere
) {
481 CFIndex ix
, attr_count
= query_attr_count(q
);
482 for (ix
= 0; ix
< attr_count
; ++ix
) {
483 SecDbAppendWhereOrAndEquals(sql
, query_attr_at(q
, ix
).key
, needWhere
);
488 SecDbAppendWhereAccessGroups(CFMutableStringRef sql
,
490 CFArrayRef accessGroups
,
492 CFIndex ix
, ag_count
;
493 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
))) {
497 SecDbAppendWhereOrAnd(sql
, needWhere
);
498 CFStringAppend(sql
, col
);
499 CFStringAppend(sql
, CFSTR(" IN (?"));
500 for (ix
= 1; ix
< ag_count
; ++ix
) {
501 CFStringAppend(sql
, CFSTR(",?"));
503 CFStringAppend(sql
, CFSTR(")"));
507 isQueryOverAllMUSRViews(CFTypeRef musrView
)
509 return SecMUSRIsViewAllViews(musrView
);
513 isQueryOverSingleUserView(CFTypeRef musrView
)
515 return isNull(musrView
);
520 isQueryOverBothUserAndSystem(CFTypeRef musrView
, uid_t
*uid
)
522 return SecMUSRGetBothUserAndSystemUUID(musrView
, uid
);
527 SecDbAppendWhereMusr(CFMutableStringRef sql
,
531 SecDbAppendWhereOrAnd(sql
, needWhere
);
534 if (isQueryOverBothUserAndSystem(q
->q_musrView
, NULL
)) {
535 CFStringAppend(sql
, CFSTR("(musr = ? OR musr = ?)"));
538 if (isQueryOverAllMUSRViews(q
->q_musrView
)) {
539 /* query over all items, regardless of view */
540 } else if (isQueryOverSingleUserView(q
->q_musrView
)) {
541 CFStringAppend(sql
, CFSTR("musr = ?"));
543 CFStringAppend(sql
, CFSTR("musr = ?"));
547 static void SecDbAppendWhereClause(CFMutableStringRef sql
, const Query
*q
,
548 CFArrayRef accessGroups
) {
549 bool needWhere
= true;
550 SecDbAppendWhereROWID(sql
, CFSTR("ROWID"), q
->q_row_id
, &needWhere
);
551 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
552 SecDbAppendWhereMusr(sql
, q
, &needWhere
);
553 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
556 static void SecDbAppendLimit(CFMutableStringRef sql
, CFIndex limit
) {
557 if (limit
!= kSecMatchUnlimited
)
558 CFStringAppendFormat(sql
, NULL
, CFSTR(" LIMIT %" PRIdCFIndex
), limit
);
561 static CFStringRef
s3dl_select_sql(Query
*q
, CFArrayRef accessGroups
) {
562 CFMutableStringRef sql
= CFStringCreateMutable(NULL
, 0);
563 if (q
->q_class
== &identity_class
) {
564 CFStringAppendFormat(sql
, NULL
, CFSTR("SELECT crowid, %@"
566 "(SELECT cert.rowid AS crowid, cert.labl AS labl,"
567 " cert.issr AS issr, cert.slnr AS slnr, cert.skid AS skid,"
568 " keys.*,cert.data AS %@"
570 " WHERE keys.priv == 1 AND cert.pkhh == keys.klbl"),
571 kSecAttrIdentityCertificateData
, kSecAttrIdentityCertificateData
);
572 SecDbAppendWhereAccessGroups(sql
, CFSTR("cert.agrp"), accessGroups
, 0);
573 /* The next 3 SecDbAppendWhere calls are in the same order as in
574 SecDbAppendWhereClause(). This makes sqlBindWhereClause() work,
575 as long as we do an extra sqlBindAccessGroups first. */
576 SecDbAppendWhereROWID(sql
, CFSTR("crowid"), q
->q_row_id
, 0);
577 CFStringAppend(sql
, CFSTR(")"));
578 bool needWhere
= true;
579 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
580 SecDbAppendWhereMusr(sql
, q
, &needWhere
);
581 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
583 CFStringAppend(sql
, CFSTR("SELECT rowid, data FROM "));
584 CFStringAppend(sql
, q
->q_class
->name
);
585 SecDbAppendWhereClause(sql
, q
, accessGroups
);
587 SecDbAppendLimit(sql
, q
->q_limit
);
592 static bool sqlBindMusr(sqlite3_stmt
*stmt
, const Query
*q
, int *pParam
, CFErrorRef
*error
) {
598 if (isQueryOverBothUserAndSystem(q
->q_musrView
, &uid
)) {
599 /* network extensions are special and get to query both user and system views */
600 CFDataRef systemUUID
= SecMUSRGetSystemKeychainUUID();
601 result
= SecDbBindObject(stmt
, param
++, systemUUID
, error
);
603 CFDataRef activeUser
= SecMUSRCreateActiveUserUUID(uid
);
604 result
= SecDbBindObject(stmt
, param
++, activeUser
, error
);
605 CFReleaseNull(activeUser
);
609 if (isQueryOverAllMUSRViews(q
->q_musrView
)) {
610 /* query over all items, regardless of view */
611 } else if (isQueryOverSingleUserView(q
->q_musrView
)) {
612 CFDataRef singleUUID
= SecMUSRGetSingleUserKeychainUUID();
613 result
= SecDbBindObject(stmt
, param
++, singleUUID
, error
);
615 result
= SecDbBindObject(stmt
, param
++, q
->q_musrView
, error
);
623 static bool sqlBindAccessGroups(sqlite3_stmt
*stmt
, CFArrayRef accessGroups
,
624 int *pParam
, CFErrorRef
*error
) {
627 CFIndex ix
, count
= accessGroups
? CFArrayGetCount(accessGroups
) : 0;
628 for (ix
= 0; ix
< count
; ++ix
) {
629 result
= SecDbBindObject(stmt
, param
++,
630 CFArrayGetValueAtIndex(accessGroups
, ix
),
639 static bool sqlBindWhereClause(sqlite3_stmt
*stmt
, const Query
*q
,
640 CFArrayRef accessGroups
, int *pParam
, CFErrorRef
*error
) {
643 CFIndex ix
, attr_count
= query_attr_count(q
);
644 for (ix
= 0; ix
< attr_count
; ++ix
) {
645 result
= SecDbBindObject(stmt
, param
++, query_attr_at(q
, ix
).value
, error
);
651 result
= sqlBindMusr(stmt
, q
, ¶m
, error
);
654 /* Bind the access group to the sql. */
656 result
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
663 bool SecDbItemQuery(SecDbQueryRef query
, CFArrayRef accessGroups
, SecDbConnectionRef dbconn
, CFErrorRef
*error
,
664 void (^handle_row
)(SecDbItemRef item
, bool *stop
)) {
665 __block
bool ok
= true;
666 /* Sanity check the query. */
668 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
670 bool (^return_attr
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
671 // The attributes here must match field list hardcoded in s3dl_select_sql used below, which is
673 return attr
->kind
== kSecDbRowIdAttr
|| attr
->kind
== kSecDbEncryptedDataAttr
;
676 CFStringRef sql
= s3dl_select_sql(query
, accessGroups
);
679 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
680 /* Bind the values being searched for to the SELECT statement. */
682 if (query
->q_class
== &identity_class
) {
683 /* Bind the access groups to cert.agrp. */
684 ok
&= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
687 ok
&= sqlBindWhereClause(stmt
, query
, accessGroups
, ¶m
, error
);
689 SecDbStep(dbconn
, stmt
, error
, ^(bool *stop
) {
690 SecDbItemRef itemFromStatement
= SecDbItemCreateWithStatement(kCFAllocatorDefault
, query
->q_class
, stmt
, query
->q_keybag
, error
, return_attr
);
691 if (itemFromStatement
) {
692 CFTransferRetained(itemFromStatement
->credHandle
, query
->q_use_cred_handle
);
693 if (match_item(dbconn
, query
, accessGroups
, itemFromStatement
->attributes
))
694 handle_row(itemFromStatement
, stop
);
695 CFReleaseNull(itemFromStatement
);
697 secerror("failed to create item from stmt: %@", error
? *error
: (CFErrorRef
)"no error");
699 CFReleaseNull(*error
);
714 s3dl_query(s3dl_handle_row handle_row
,
715 void *context
, CFErrorRef
*error
)
717 struct s3dl_query_ctx
*c
= context
;
718 SecDbConnectionRef dbt
= c
->dbt
;
720 CFArrayRef accessGroups
= c
->accessGroups
;
722 /* Sanity check the query. */
724 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
726 /* Actual work here. */
727 if (q
->q_limit
== 1) {
730 c
->result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
732 CFStringRef sql
= s3dl_select_sql(q
, accessGroups
);
733 bool ok
= SecDbWithSQL(dbt
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
735 /* Bind the values being searched for to the SELECT statement. */
737 if (q
->q_class
== &identity_class
) {
738 /* Bind the access groups to cert.agrp. */
739 sql_ok
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
742 sql_ok
= sqlBindWhereClause(stmt
, q
, accessGroups
, ¶m
, error
);
744 SecDbForEach(stmt
, error
, ^bool (int row_index
) {
745 handle_row(stmt
, context
);
747 bool needs_auth
= q
->q_error
&& CFErrorGetCode(q
->q_error
) == errSecAuthNeeded
;
748 if (q
->q_skip_acl_items
&& needs_auth
)
749 // Skip items needing authentication if we are told to do so.
750 CFReleaseNull(q
->q_error
);
752 bool stop
= q
->q_limit
!= kSecMatchUnlimited
&& c
->found
>= q
->q_limit
;
753 stop
= stop
|| (q
->q_error
&& !needs_auth
);
762 // First get the error from the query, since errSecDuplicateItem from an
763 // update query should superceed the errSecItemNotFound below.
764 if (!query_error(q
, error
))
766 if (ok
&& c
->found
== 0) {
767 ok
= SecError(errSecItemNotFound
, error
, CFSTR("no matching items found"));
768 if (q
->q_spindump_on_failure
) {
769 __security_stackshotreport(CFSTR("ItemNotFound"), __sec_exception_code_LostInMist
);
777 s3dl_copy_matching(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
,
778 CFArrayRef accessGroups
, CFErrorRef
*error
)
780 struct s3dl_query_ctx ctx
= {
781 .q
= q
, .accessGroups
= accessGroups
, .dbt
= dbt
,
783 if (q
->q_row_id
&& query_attr_count(q
))
784 return SecError(errSecItemIllegalQuery
, error
,
785 CFSTR("attributes to query illegal; both row_id and other attributes can't be searched at the same time"));
787 // Only copy things that aren't tombstones unless the client explicitly asks otherwise.
788 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
789 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
790 bool ok
= s3dl_query(s3dl_query_row
, &ctx
, error
);
792 *result
= ctx
.result
;
794 CFReleaseSafe(ctx
.result
);
799 /* First remove key from q->q_pairs if it's present, then add the attribute again. */
800 static void query_set_attribute_with_desc(const SecDbAttr
*desc
, const void *value
, Query
*q
) {
801 if (CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
803 for (ix
= 0; ix
< q
->q_attr_end
; ++ix
) {
804 if (CFEqual(desc
->name
, q
->q_pairs
[ix
].key
)) {
805 CFReleaseSafe(q
->q_pairs
[ix
].value
);
807 for (; ix
< q
->q_attr_end
; ++ix
) {
808 q
->q_pairs
[ix
] = q
->q_pairs
[ix
+ 1];
810 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
815 query_add_attribute_with_desc(desc
, value
, q
);
818 /* Update modification_date if needed. */
819 static void query_pre_update(Query
*q
) {
820 SecDbForEachAttr(q
->q_class
, desc
) {
821 if (desc
->kind
== kSecDbModificationDateAttr
) {
822 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
823 query_set_attribute_with_desc(desc
, now
, q
);
829 /* Make sure all attributes that are marked as not_null have a value. If
830 force_date is false, only set mdat and cdat if they aren't already set. */
831 void query_pre_add(Query
*q
, bool force_date
) {
832 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
833 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInFlag
) {
834 if (desc
->kind
== kSecDbCreationDateAttr
||
835 desc
->kind
== kSecDbModificationDateAttr
) {
837 query_set_attribute_with_desc(desc
, now
, q
);
838 } else if (!CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
839 query_add_attribute_with_desc(desc
, now
, q
);
841 } else if ((desc
->flags
& kSecDbNotNullFlag
) &&
842 !CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
843 CFTypeRef value
= NULL
;
844 if (desc
->flags
& kSecDbDefault0Flag
) {
845 if (desc
->kind
== kSecDbDateAttr
)
846 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
849 value
= CFNumberCreate(0, kCFNumberSInt32Type
, &vzero
);
851 } else if (desc
->flags
& kSecDbDefaultEmptyFlag
) {
852 if (desc
->kind
== kSecDbDataAttr
|| desc
->kind
== kSecDbUUIDAttr
)
853 value
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
860 /* Safe to use query_add_attribute here since the attr wasn't
862 query_add_attribute_with_desc(desc
, value
, q
);
870 // Return a tri state value false->never make a tombstone, true->always make a
871 // tombstone, NULL->make a tombstone, but delete it if the tombstone itself is not currently being synced.
872 static CFBooleanRef
s3dl_should_make_tombstone(Query
*q
, bool item_is_syncable
, SecDbItemRef item
) {
874 return q
->q_use_tomb
;
875 else if (item_is_syncable
&& !SecDbItemIsTombstone(item
))
878 return kCFBooleanFalse
;
880 /* AUDIT[securityd](done):
881 attributesToUpdate (ok) is a caller provided dictionary,
882 only its cf types have been checked.
885 s3dl_query_update(SecDbConnectionRef dbt
, Query
*q
,
886 CFDictionaryRef attributesToUpdate
, CFArrayRef accessGroups
, CFErrorRef
*error
)
888 /* Sanity check the query. */
889 if (query_match_count(q
) != 0)
890 return SecError(errSecItemMatchUnsupported
, error
, CFSTR("match not supported in attributes to update"));
892 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported in attributes to update"));
893 if (q
->q_row_id
&& query_attr_count(q
))
894 return SecError(errSecItemIllegalQuery
, error
, CFSTR("attributes to update illegal; both row_id and other attributes can't be updated at the same time"));
896 __block
bool result
= true;
897 Query
*u
= query_create(q
->q_class
, NULL
, attributesToUpdate
, error
);
898 if (u
== NULL
) return false;
899 require_action_quiet(query_update_parse(u
, attributesToUpdate
, error
), errOut
, result
= false);
901 result
&= SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
902 // Make sure we only update real items, not tombstones, unless the client explicitly asks otherwise.
903 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
904 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
905 result
&= SecDbItemQuery(q
, accessGroups
, dbt
, error
, ^(SecDbItemRef item
, bool *stop
) {
906 // We always need to know the error here.
907 CFErrorRef localError
= NULL
;
908 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
909 const SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
);
910 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, NULL
));
911 SecDbItemRef new_item
= SecDbItemCopyWithUpdates(item
, u
->q_item
, &localError
);
912 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
913 CFReleaseSafe(storedSHA1
);
914 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
915 // We just ignore this, and treat as if item is not found.
916 secwarning("deleting corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, SecDbItemGetRowId(item
, NULL
), localError
);
917 CFReleaseNull(localError
);
918 if (!SecDbItemDelete(item
, dbt
, false, &localError
)) {
919 secerror("failed to delete corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, SecDbItemGetRowId(item
, NULL
), localError
);
920 CFReleaseNull(localError
);
924 if (new_item
!= NULL
&& u
->q_access_control
!= NULL
)
925 SecDbItemSetAccessControl(new_item
, u
->q_access_control
, &localError
);
926 result
= SecErrorPropagate(localError
, error
) && new_item
;
928 bool item_is_sync
= SecDbItemIsSyncable(item
);
929 result
= SecDbItemUpdate(item
, new_item
, dbt
, s3dl_should_make_tombstone(q
, item_is_sync
, item
), error
);
932 if (item_is_sync
|| SecDbItemIsSyncable(new_item
))
933 q
->q_sync_changed
= true;
941 if (result
&& !q
->q_changed
)
942 result
= SecError(errSecItemNotFound
, error
, CFSTR("No items updated"));
944 if (!query_destroy(u
, error
))
949 static bool SecDbItemNeedAuth(SecDbItemRef item
, CFErrorRef
*error
)
951 CFErrorRef localError
= NULL
;
952 if (!SecDbItemEnsureDecrypted(item
, &localError
) && localError
&& CFErrorGetCode(localError
) == errSecAuthNeeded
) {
958 CFReleaseSafe(localError
);
963 s3dl_query_delete(SecDbConnectionRef dbt
, Query
*q
, CFArrayRef accessGroups
, CFErrorRef
*error
)
965 __block
bool ok
= true;
966 __block
bool needAuth
= false;
967 // Only delete things that aren't tombstones, unless the client explicitly asks otherwise.
968 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
969 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
970 ok
&= SecDbItemSelect(q
, dbt
, error
, NULL
, ^bool(const SecDbAttr
*attr
) {
972 },^bool(CFMutableStringRef sql
, bool *needWhere
) {
973 SecDbAppendWhereClause(sql
, q
, accessGroups
);
975 },^bool(sqlite3_stmt
* stmt
, int col
) {
976 return sqlBindWhereClause(stmt
, q
, accessGroups
, &col
, error
);
977 }, ^(SecDbItemRef item
, bool *stop
) {
978 // Check if item need to be authenticated by LocalAuthentication
979 item
->cryptoOp
= kAKSKeyOpDelete
;
980 if (SecDbItemNeedAuth(item
, error
)) {
984 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
985 const SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
);
986 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, NULL
));
987 bool item_is_sync
= SecDbItemIsSyncable(item
);
988 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
989 CFReleaseSafe(storedSHA1
);
990 ok
= SecDbItemDelete(item
, dbt
, s3dl_should_make_tombstone(q
, item_is_sync
, item
), error
);
994 q
->q_sync_changed
= true;
997 if (ok
&& !q
->q_changed
&& !needAuth
) {
998 ok
= SecError(errSecItemNotFound
, error
, CFSTR("Delete failed to delete anything"));
1000 return ok
&& !needAuth
;
1004 matchAnyString(CFStringRef needle
, CFStringRef
*haystack
)
1007 if (CFEqual(needle
, *haystack
))
1014 /* Return true iff the item in question should not be backed up, nor restored,
1015 but when restoring a backup the original version of the item should be
1016 added back to the keychain again after the restore completes. */
1017 static bool SecItemIsSystemBound(CFDictionaryRef item
, const SecDbClass
*cls
, bool multiUser
) {
1018 CFStringRef agrp
= CFDictionaryGetValue(item
, kSecAttrAccessGroup
);
1019 if (!isString(agrp
))
1022 if (CFEqualSafe(agrp
, kSOSInternalAccessGroup
)) {
1023 secdebug("backup", "found sysbound item: %@", item
);
1027 if (CFEqual(agrp
, CFSTR("lockdown-identities"))) {
1028 secdebug("backup", "found sys_bound item: %@", item
);
1032 if (CFEqual(agrp
, CFSTR("apple")) && cls
== &genp_class
) {
1033 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1034 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
1036 if (isString(service
) && isString(account
)) {
1037 static CFStringRef mcAccounts
[] = {
1043 if (CFEqual(service
, CFSTR("com.apple.managedconfiguration"))
1044 && matchAnyString(account
, mcAccounts
))
1046 secdebug("backup", "found sys_bound item: %@", item
);
1052 if (multiUser
&& CFEqual(agrp
, CFSTR("com.apple.apsd")) && cls
== &genp_class
) {
1053 static CFStringRef pushServices
[] = {
1054 CFSTR("push.apple.com"),
1055 CFSTR("push.apple.com,PerAppToken.v0"),
1058 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1060 if (isString(service
) && matchAnyString(service
, pushServices
)) {
1061 secdebug("backup", "found sys_bound item: %@", item
);
1066 if (multiUser
&& CFEqual(agrp
, CFSTR("appleaccount")) && cls
== &genp_class
) {
1067 static CFStringRef accountServices
[] = {
1068 CFSTR("com.apple.appleaccount.fmf.token"), /* temporary tokens while accout is being setup */
1069 CFSTR("com.apple.appleaccount.fmf.apptoken"),
1070 CFSTR("com.apple.appleaccount.fmip.siritoken"),
1071 CFSTR("com.apple.appleaccount.cloudkit.token"),
1074 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1076 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1077 secdebug("backup", "found exact sys_bound item: %@", item
);
1082 if (multiUser
&& CFEqual(agrp
, CFSTR("apple")) && cls
== &genp_class
) {
1083 static CFStringRef accountServices
[] = {
1084 CFSTR("com.apple.account.AppleAccount.token"),
1085 CFSTR("com.apple.account.AppleAccount.password"),
1086 CFSTR("com.apple.account.AppleAccount.rpassword"),
1087 CFSTR("com.apple.account.idms.token"),
1088 CFSTR("com.apple.account.idms.heartbeat-token"),
1089 CFSTR("com.apple.account.idms.continuation-key"),
1090 CFSTR("com.apple.account.CloudKit.token"),
1091 CFSTR("com.apple.account.IdentityServices.password"), /* accountsd for ids */
1092 CFSTR("com.apple.account.IdentityServices.rpassword"),
1093 CFSTR("com.apple.account.IdentityServices.token"),
1094 CFSTR("BackupIDSAccountToken"),
1095 CFSTR("com.apple.ids"),
1100 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1102 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1103 secdebug("backup", "found exact sys_bound item: %@", item
);
1106 if (isString(service
) && CFEqual(service
, CFSTR("com.apple.facetime"))) {
1107 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
1108 if (isString(account
) && CFEqual(account
, CFSTR("registrationV1"))) {
1109 secdebug("backup", "found exact sys_bound item: %@", item
);
1115 if (multiUser
&& CFEqual(agrp
, CFSTR("ichat")) && cls
== &genp_class
) {
1116 static CFStringRef accountServices
[] = {
1120 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
1122 if (isString(service
) && matchAnyString(service
, accountServices
)) {
1123 secdebug("backup", "found exact sys_bound item: %@", item
);
1128 if (multiUser
&& CFEqual(agrp
, CFSTR("ichat")) && cls
== &keys_class
) {
1129 static CFStringRef exactMatchingLabel
[] = {
1130 CFSTR("iMessage Encryption Key"),
1131 CFSTR("iMessage Signing Key"),
1133 CFStringRef label
= CFDictionaryGetValue(item
, kSecAttrLabel
);
1134 if (isString(label
)) {
1135 if (matchAnyString(label
, exactMatchingLabel
)) {
1136 secdebug("backup", "found exact sys_bound item: %@", item
);
1143 secdebug("backup", "found non sys_bound item: %@", item
);
1147 /* Delete all items from the current keychain. If this is not an in
1148 place upgrade we don't delete items in the 'lockdown-identities'
1149 access group, this ensures that an import or restore of a backup
1150 will never overwrite an existing activation record. */
1151 static bool SecServerDeleteAll(SecDbConnectionRef dbt
, CFErrorRef
*error
) {
1152 secwarning("SecServerDeleteAll");
1154 return kc_transaction(dbt
, error
, ^{
1156 bool ok
= (SecDbExec(dbt
, CFSTR("DELETE from genp;"), error
) &&
1157 SecDbExec(dbt
, CFSTR("DELETE from inet;"), error
) &&
1158 SecDbExec(dbt
, CFSTR("DELETE from cert;"), error
) &&
1159 SecDbExec(dbt
, CFSTR("DELETE from keys;"), error
));
1164 #if TARGET_OS_IPHONE
1166 static bool DeleteAllFromTableForMUSRView(SecDbConnectionRef dbt
,
1172 sqlite3_stmt
*stmt
= NULL
;
1173 CFStringRef sql2
= NULL
;
1177 sql2
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@ AND pdmn NOT IN ('aku','akpu','cku','dku')"), sql
);
1179 sql2
= CFRetain(sql
);
1181 require(sql2
, fail
);
1183 stmt
= SecDbCopyStmt(dbt
, sql2
, NULL
, error
);
1184 require(stmt
, fail
);
1186 ok
= SecDbBindObject(stmt
, 1, musr
, error
);
1189 ok
= SecDbStep(dbt
, stmt
, error
, ^(bool *stop
) { });
1194 ok
= SecDbFinalize(stmt
, error
);
1197 secwarning("DeleteAllFromTableForMUSRView failed for %@ for musr: %@: %@", sql2
, musr
, error
? *error
: NULL
);
1199 CFReleaseNull(sql2
);
1204 bool SecServerDeleteAllForUser(SecDbConnectionRef dbt
, CFDataRef musrView
, bool keepU
, CFErrorRef
*error
) {
1205 secwarning("SecServerDeleteAllForUser for user: %@ keepU %s", musrView
, keepU
? "yes" : "no");
1207 return kc_transaction(dbt
, error
, ^{
1210 ok
= (DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM genp WHERE musr = ?"), musrView
, keepU
, error
) &&
1211 DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM inet WHERE musr = ?"), musrView
, keepU
, error
) &&
1212 DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM cert WHERE musr = ?"), musrView
, keepU
, error
) &&
1213 DeleteAllFromTableForMUSRView(dbt
, CFSTR("DELETE FROM keys WHERE musr = ?"), musrView
, keepU
, error
));
1221 struct s3dl_export_row_ctx
{
1222 struct s3dl_query_ctx qc
;
1223 keybag_handle_t dest_keybag
;
1224 enum SecItemFilter filter
;
1228 static void s3dl_export_row(sqlite3_stmt
*stmt
, void *context
) {
1229 struct s3dl_export_row_ctx
*c
= context
;
1231 SecAccessControlRef access_control
= NULL
;
1232 CFErrorRef localError
= NULL
;
1234 /* Skip akpu items when backing up, those are intentionally lost across restores. The same applies to SEP-based keys */
1235 bool skip_akpu_or_token
= c
->filter
== kSecBackupableItemFilter
;
1237 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
1238 CFMutableDictionaryRef item
= NULL
;
1239 bool ok
= s3dl_item_from_col(stmt
, q
, 1, c
->qc
.accessGroups
, &item
, &access_control
, &localError
);
1241 bool is_akpu
= access_control
? CFEqualSafe(SecAccessControlGetProtection(access_control
),
1242 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
) : false;
1243 bool is_token
= (ok
&& item
!= NULL
) ? CFDictionaryContainsKey(item
, kSecAttrTokenID
) : false;
1245 if (ok
&& item
&& !(skip_akpu_or_token
&& (is_akpu
|| is_token
))) {
1246 /* Only export sysbound items if do_sys_bound is true, only export non sysbound items otherwise. */
1247 bool do_sys_bound
= c
->filter
== kSecSysBoundItemFilter
;
1248 if (c
->filter
== kSecNoItemFilter
||
1249 SecItemIsSystemBound(item
, q
->q_class
, c
->multiUser
) == do_sys_bound
) {
1250 /* Re-encode the item. */
1251 secdebug("item", "export rowid %llu item: %@", rowid
, item
);
1252 /* The code below could be moved into handle_row. */
1253 CFDataRef pref
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
1255 if (c
->dest_keybag
!= KEYBAG_NONE
) {
1256 CFMutableDictionaryRef auth_attribs
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1257 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInAuthenticatedDataFlag
) {
1258 CFTypeRef value
= CFDictionaryGetValue(item
, desc
->name
);
1260 CFDictionaryAddValue(auth_attribs
, desc
->name
, value
);
1261 CFDictionaryRemoveValue(item
, desc
->name
);
1265 /* Encode and encrypt the item to the specified keybag. */
1266 CFDataRef edata
= NULL
;
1267 bool encrypted
= ks_encrypt_data(c
->dest_keybag
, access_control
, q
->q_use_cred_handle
, item
, auth_attribs
, &edata
, false, &q
->q_error
);
1268 CFDictionaryRemoveAllValues(item
);
1269 CFRelease(auth_attribs
);
1271 CFDictionarySetValue(item
, kSecValueData
, edata
);
1272 CFReleaseSafe(edata
);
1274 seccritical("ks_encrypt_data %@,rowid=%" PRId64
": failed: %@", q
->q_class
->name
, rowid
, q
->q_error
);
1275 CFReleaseNull(q
->q_error
);
1278 if (CFDictionaryGetCount(item
)) {
1279 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
1280 CFArrayAppendValue((CFMutableArrayRef
)c
->qc
.result
, item
);
1283 CFReleaseSafe(pref
);
1288 OSStatus status
= SecErrorGetOSStatus(localError
);
1290 if (status
== errSecInteractionNotAllowed
&& is_akpu
&& skip_akpu_or_token
) {
1291 // We expect akpu items to be inaccessible when the device is locked.
1292 CFReleaseNull(localError
);
1294 /* This happens a lot when trying to migrate keychain before first unlock, so only a notice */
1295 /* If the error is "corrupted item" then we just ignore it, otherwise we save it in the query */
1296 secinfo("item","Could not export item for rowid %llu: %@", rowid
, localError
);
1298 if(status
== errSecDecode
) {
1299 CFReleaseNull(localError
);
1301 CFReleaseSafe(q
->q_error
);
1302 q
->q_error
=localError
;
1306 CFReleaseSafe(access_control
);
1310 SecCreateKeybagUUID(keybag_handle_t keybag
)
1312 #if !TARGET_HAS_KEYSTORE
1317 if (aks_get_bag_uuid(keybag
, uuid
) != KERN_SUCCESS
)
1319 uuid_unparse_lower(uuid
, uuidstr
);
1320 return CFStringCreateWithCString(NULL
, uuidstr
, kCFStringEncodingUTF8
);
1326 SecServerCopyKeychainPlist(SecDbConnectionRef dbt
,
1327 SecurityClient
*client
,
1328 keybag_handle_t src_keybag
,
1329 keybag_handle_t dest_keybag
,
1330 enum SecItemFilter filter
,
1331 CFErrorRef
*error
) {
1332 CFMutableDictionaryRef keychain
;
1333 keychain
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1334 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1336 bool inMultiUser
= false;
1337 CFStringRef keybaguuid
= NULL
;
1338 Query q
= { .q_keybag
= src_keybag
,
1343 if (error
&& !*error
)
1344 SecError(errSecAllocate
, error
, CFSTR("Can't create keychain dictionary"));
1349 kSecReturnDataMask
|
1350 kSecReturnAttributesMask
|
1351 kSecReturnPersistentRefMask
;
1352 q
.q_limit
= kSecMatchUnlimited
;
1353 q
.q_skip_acl_items
= true;
1356 #if TARGET_OS_IPHONE
1357 if (client
&& client
->inMultiUser
) {
1358 q
.q_musrView
= SecMUSRCreateActiveUserUUID(client
->uid
);
1363 q
.q_musrView
= SecMUSRGetSingleUserKeychainUUID();
1364 CFRetain(q
.q_musrView
);
1366 keybaguuid
= SecCreateKeybagUUID(dest_keybag
);
1368 CFDictionarySetValue(keychain
, kSecBackupKeybagUUIDKey
, keybaguuid
);
1371 /* Get rid of this duplicate. */
1372 const SecDbClass
*SecDbClasses
[] = {
1379 for (class_ix
= 0; class_ix
< array_size(SecDbClasses
);
1381 q
.q_class
= SecDbClasses
[class_ix
];
1382 struct s3dl_export_row_ctx ctx
= {
1383 .qc
= { .q
= &q
, .dbt
= dbt
},
1384 .dest_keybag
= dest_keybag
, .filter
= filter
,
1385 .multiUser
= inMultiUser
,
1388 secnotice("item", "exporting class '%@'", q
.q_class
->name
);
1390 CFErrorRef localError
= NULL
;
1391 if (s3dl_query(s3dl_export_row
, &ctx
, &localError
)) {
1392 if (CFArrayGetCount(ctx
.qc
.result
))
1393 CFDictionaryAddValue(keychain
, q
.q_class
->name
, ctx
.qc
.result
);
1396 OSStatus status
= (OSStatus
)CFErrorGetCode(localError
);
1397 if (status
== errSecItemNotFound
) {
1398 CFRelease(localError
);
1400 secerror("Export failed: %@", localError
);
1402 CFReleaseSafe(*error
);
1403 *error
= localError
;
1405 CFRelease(localError
);
1407 CFReleaseNull(keychain
);
1408 CFReleaseNull(ctx
.qc
.result
);
1412 CFReleaseNull(ctx
.qc
.result
);
1416 CFReleaseNull(q
.q_musrView
);
1417 CFReleaseNull(keybaguuid
);
1422 struct SecServerImportClassState
{
1423 SecDbConnectionRef dbt
;
1425 keybag_handle_t src_keybag
;
1426 keybag_handle_t dest_keybag
;
1427 SecurityClient
*client
;
1428 enum SecItemFilter filter
;
1431 struct SecServerImportItemState
{
1432 const SecDbClass
*class;
1433 struct SecServerImportClassState
*s
;
1437 SecServerImportItem(const void *value
, void *context
)
1439 struct SecServerImportItemState
*state
= (struct SecServerImportItemState
*)context
;
1440 bool inMultiUser
= false;
1441 #if TARGET_OS_IPHONE
1442 if (state
->s
->client
->inMultiUser
)
1446 if (state
->s
->error
)
1449 if (!isDictionary(value
)) {
1450 SecError(errSecParam
, &state
->s
->error
, CFSTR("value %@ is not a dictionary"), value
);
1454 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
1456 secdebug("item", "Import Item : %@", dict
);
1458 /* We use the kSecSysBoundItemFilter to indicate that we don't
1459 * preserve rowid's during import.
1461 if (state
->s
->filter
== kSecBackupableItemFilter
) {
1464 /* We don't filter non sys_bound items during import since we know we
1465 * will never have any in this case.
1467 if (SecItemIsSystemBound(dict
, state
->class, inMultiUser
))
1471 * Don't bother with u items when in edu mode since our current backup system
1472 * don't keep track of items that blongs to the device (u) but rather just
1473 * merge them into one blob.
1475 if (inMultiUser
&& (pdmu
= CFDictionaryGetValue(dict
, kSecAttrAccessible
))) {
1476 if (CFEqual(pdmu
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) ||
1477 CFEqual(pdmu
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
) ||
1478 CFEqual(pdmu
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) ||
1479 CFEqual(pdmu
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
))
1481 secdebug("item", "Skipping KU item : %@", dict
);
1486 /* Avoid importing token-based items. Although newer backups should not have them,
1487 * older (iOS9, iOS10.0) produced backups with token-based items.
1489 if (CFDictionaryContainsKey(dict
, kSecAttrTokenID
)) {
1490 secdebug("item", "Skipping token-based item : %@", dict
);
1497 /* This is sligthly confusing:
1498 - During upgrade all items are exported with KEYBAG_NONE.
1499 - During restore from backup, existing sys_bound items are exported with KEYBAG_NONE, and are exported as dictionary of attributes.
1500 - Item in the actual backup are export with a real keybag, and are exported as encrypted v_Data and v_PersistentRef
1502 if (state
->s
->src_keybag
== KEYBAG_NONE
) {
1503 item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, state
->class, dict
, state
->s
->dest_keybag
, &state
->s
->error
);
1505 item
= SecDbItemCreateWithBackupDictionary(kCFAllocatorDefault
, state
->class, dict
, state
->s
->src_keybag
, state
->s
->dest_keybag
, &state
->s
->error
);
1513 CFDataRef musr
= NULL
;
1514 CFDataRef musrBackup
= CFDictionaryGetValue(dict
, kSecAttrMultiUser
);
1515 CFDataRef systemKeychainUUID
= SecMUSRGetSystemKeychainUUID();
1516 bool systemKeychain
= CFEqualSafe(musrBackup
, systemKeychainUUID
);
1518 #if TARGET_OS_IPHONE
1519 if (state
->s
->client
&& state
->s
->client
->inMultiUser
) {
1520 if (systemKeychain
) {
1521 secwarning("system keychain not allowed in multi user mode for item: %@", item
);
1523 musr
= SecMUSRCreateActiveUserUUID(state
->s
->client
->uid
);
1528 if (systemKeychain
) {
1529 musr
= SecMUSRCopySystemKeychainUUID();
1531 musr
= SecMUSRGetSingleUserKeychainUUID();
1536 CFReleaseNull(item
);
1538 SecDbItemSetValueWithName(item
, CFSTR("musr"), musr
, &state
->s
->error
);
1550 if(state
->s
->filter
!= kSecSysBoundItemFilter
) {
1551 SecDbItemExtractRowIdFromBackupDictionary(item
, dict
, &state
->s
->error
);
1553 SecDbItemInferSyncable(item
, &state
->s
->error
);
1554 insertStatus
= SecDbItemInsert(item
, state
->s
->dbt
, &state
->s
->error
);
1555 if (!insertStatus
) {
1557 When running in EduMode, multiple users share the same
1558 keychain and unfortionaly the rowid is used a
1559 persistant reference and is part of the contraints (its
1560 UNIQUE), so lets clear the rowid and try to insert the
1563 This even happens for normal operation because of
1564 SysBound entries, so in case of a failure, lets try
1565 again to insert the record.
1567 SecDbItemClearRowId(item
, NULL
);
1568 SecDbItemInsert(item
, state
->s
->dbt
, &state
->s
->error
);
1572 /* Reset error if we had one, since we just skip the current item
1573 and continue importing what we can. */
1574 if (state
->s
->error
) {
1575 secwarning("Failed to import an item (%@) of class '%@': %@ - ignoring error.",
1576 item
, state
->class->name
, state
->s
->error
);
1577 CFReleaseNull(state
->s
->error
);
1580 CFReleaseSafe(item
);
1583 static void SecServerImportClass(const void *key
, const void *value
,
1585 struct SecServerImportClassState
*state
=
1586 (struct SecServerImportClassState
*)context
;
1589 if (!isString(key
)) {
1590 SecError(errSecParam
, &state
->error
, CFSTR("class name %@ is not a string"), key
);
1593 /* ignore the Keybag UUID */
1594 if (CFEqual(key
, kSecBackupKeybagUUIDKey
))
1596 const SecDbClass
*class = kc_class_with_name(key
);
1598 secwarning("Ignoring unknown key class '%@'", key
);
1601 if (class == &identity_class
) {
1602 SecError(errSecParam
, &state
->error
, CFSTR("attempt to import an identity"));
1605 struct SecServerImportItemState item_state
= {
1606 .class = class, .s
= state
,
1608 if (isArray(value
)) {
1609 CFArrayRef items
= (CFArrayRef
)value
;
1610 secwarning("Import %ld items of class %@ (filter %d)", (long)CFArrayGetCount(items
), key
, state
->filter
);
1611 CFArrayApplyFunction(items
, CFRangeMake(0, CFArrayGetCount(items
)),
1612 SecServerImportItem
, &item_state
);
1613 } else if (isDictionary(value
)) {
1614 CFDictionaryRef item
= (CFDictionaryRef
)value
;
1615 secwarning("Import %ld items of class %@ (filter %d)", (long)CFDictionaryGetCount(item
), key
, state
->filter
);
1616 SecServerImportItem(item
, &item_state
);
1618 secwarning("Unknown value type for class %@ (filter %d)", key
, state
->filter
);
1622 bool SecServerImportKeychainInPlist(SecDbConnectionRef dbt
, SecurityClient
*client
,
1623 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
1624 CFDictionaryRef keychain
, enum SecItemFilter filter
, CFErrorRef
*error
) {
1625 CFStringRef keybaguuid
= NULL
;
1628 CFDictionaryRef sys_bound
= NULL
;
1629 if (filter
== kSecBackupableItemFilter
) {
1630 /* Grab a copy of all the items for which SecItemIsSystemBound()
1632 require(sys_bound
= SecServerCopyKeychainPlist(dbt
, client
, KEYBAG_DEVICE
,
1633 KEYBAG_NONE
, kSecSysBoundItemFilter
,
1638 * Validate the uuid of the source keybag matches what we have in the backup
1640 keybaguuid
= SecCreateKeybagUUID(src_keybag
);
1642 CFStringRef uuid
= CFDictionaryGetValue(keychain
, kSecBackupKeybagUUIDKey
);
1643 if (isString(uuid
)) {
1644 require_action(CFEqual(keybaguuid
, uuid
), errOut
,
1645 SecError(errSecDecode
, error
, CFSTR("Keybag UUID (%@) mismatch with backup (%@)"),
1650 /* Delete everything in the keychain. */
1651 #if TARGET_OS_IPHONE
1652 if (client
->inMultiUser
) {
1653 CFDataRef musrView
= SecMUSRCreateActiveUserUUID(client
->uid
);
1654 require_action(musrView
, errOut
, ok
= false);
1655 require_action(ok
= SecServerDeleteAllForUser(dbt
, musrView
, true, error
), errOut
, CFReleaseNull(musrView
));
1656 CFReleaseNull(musrView
);
1660 require(ok
= SecServerDeleteAll(dbt
, error
), errOut
);
1663 struct SecServerImportClassState state
= {
1665 .src_keybag
= src_keybag
,
1666 .dest_keybag
= dest_keybag
,
1670 /* Import the provided items, preserving rowids. */
1671 secwarning("Restoring backup items '%ld'", (long)CFDictionaryGetCount(keychain
));
1672 CFDictionaryApplyFunction(keychain
, SecServerImportClass
, &state
);
1675 state
.src_keybag
= KEYBAG_NONE
;
1676 /* Import the items we preserved with random rowids. */
1677 state
.filter
= kSecSysBoundItemFilter
;
1678 secwarning("Restoring sysbound items '%ld'", (long)CFDictionaryGetCount(sys_bound
));
1679 CFDictionaryApplyFunction(sys_bound
, SecServerImportClass
, &state
);
1683 CFReleaseSafe(*error
);
1684 *error
= state
.error
;
1686 CFRelease(state
.error
);
1692 CFReleaseSafe(sys_bound
);
1693 CFReleaseSafe(keybaguuid
);
1699 SecServerBackupGetKeybagUUID(CFDictionaryRef keychain
)
1701 CFStringRef uuid
= CFDictionaryGetValue(keychain
, kSecBackupKeybagUUIDKey
);
1702 if (!isString(uuid
))
1707 #pragma mark - key rolling support
1710 struct check_generation_ctx
{
1711 struct s3dl_query_ctx query_ctx
;
1712 uint32_t current_generation
;
1715 static void check_generation(sqlite3_stmt
*stmt
, void *context
) {
1716 struct check_generation_ctx
*c
= context
;
1717 CFDataRef blob
= NULL
;
1719 const uint8_t *cursor
= NULL
;
1721 keyclass_t keyclass
;
1722 uint32_t current_generation
= c
->current_generation
;
1724 require(blob
= s3dl_copy_data_from_col(stmt
, 1, &c
->query_ctx
.q
->q_error
), out
);
1725 blobLen
= CFDataGetLength(blob
);
1726 cursor
= CFDataGetBytePtr(blob
);
1728 /* Check for underflow, ensuring we have at least one full AES block left. */
1729 if (blobLen
< sizeof(version
) + sizeof(keyclass
)) {
1730 SecError(errSecDecode
, &c
->query_ctx
.q
->q_error
, CFSTR("check_generation: Check for underflow"));
1734 version
= *((uint32_t *)cursor
);
1735 cursor
+= sizeof(version
);
1737 (void) version
; // TODO: do something with the version number.
1739 keyclass
= *((keyclass_t
*)cursor
);
1741 // TODO: export get_key_gen macro
1742 if (((keyclass
& ~key_class_last
) == 0) != (current_generation
== 0)) {
1743 c
->query_ctx
.found
++;
1746 CFReleaseSafe(blob
);
1750 c
->query_ctx
.found
++;
1751 CFReleaseSafe(blob
);
1754 bool s3dl_dbt_keys_current(SecDbConnectionRef dbt
, uint32_t current_generation
, CFErrorRef
*error
) {
1755 CFErrorRef localError
= NULL
;
1756 struct check_generation_ctx ctx
= { .query_ctx
= { .dbt
= dbt
}, .current_generation
= current_generation
};
1758 const SecDbClass
*classes
[] = {
1765 for (size_t class_ix
= 0; class_ix
< array_size(classes
); ++class_ix
) {
1766 Query
*q
= query_create(classes
[class_ix
], NULL
, NULL
, &localError
);
1770 ctx
.query_ctx
.q
= q
;
1771 q
->q_limit
= kSecMatchUnlimited
;
1773 bool ok
= s3dl_query(check_generation
, &ctx
, &localError
);
1774 query_destroy(q
, NULL
);
1775 CFReleaseNull(ctx
.query_ctx
.result
);
1777 if (!ok
&& localError
&& (CFErrorGetCode(localError
) == errSecItemNotFound
)) {
1778 CFReleaseNull(localError
);
1781 secerror("Class %@ not up to date", classes
[class_ix
]->name
);
1787 bool s3dl_dbt_update_keys(SecDbConnectionRef dbt
, SecurityClient
*client
, CFErrorRef
*error
) {
1788 return SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
1789 __block
bool ok
= false;
1790 uint32_t keystore_generation_status
;
1792 /* can we migrate to new class keys right now? */
1793 if (!aks_generation(KEYBAG_DEVICE
, generation_noop
, &keystore_generation_status
) &&
1794 (keystore_generation_status
& generation_change_in_progress
)) {
1796 /* take a lock assertion */
1797 bool operated_while_unlocked
= SecAKSDoWhileUserBagLocked(error
, ^{
1798 CFErrorRef localError
= NULL
;
1799 CFDictionaryRef backup
= SecServerCopyKeychainPlist(dbt
, NULL
,
1800 KEYBAG_DEVICE
, KEYBAG_NONE
, kSecNoItemFilter
, &localError
);
1803 secerror("Ignoring export error: %@ during roll export", localError
);
1804 CFReleaseNull(localError
);
1806 ok
= SecServerImportKeychainInPlist(dbt
, client
, KEYBAG_NONE
,
1807 KEYBAG_DEVICE
, backup
, kSecNoItemFilter
, &localError
);
1809 secerror("Ignoring export error: %@ during roll export", localError
);
1810 CFReleaseNull(localError
);
1815 if (!operated_while_unlocked
)
1818 ok
= SecError(errSecBadReq
, error
, CFSTR("No key roll in progress."));