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. */
1235 bool skip_akpu
= c
->filter
== kSecBackupableItemFilter
;
1237 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
1238 CFMutableDictionaryRef item
;
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;
1244 if (ok
&& item
&& !(skip_akpu
&& is_akpu
)) {
1245 /* Only export sysbound items if do_sys_bound is true, only export non sysbound items otherwise. */
1246 bool do_sys_bound
= c
->filter
== kSecSysBoundItemFilter
;
1247 if (c
->filter
== kSecNoItemFilter
||
1248 SecItemIsSystemBound(item
, q
->q_class
, c
->multiUser
) == do_sys_bound
) {
1249 /* Re-encode the item. */
1250 secdebug("item", "export rowid %llu item: %@", rowid
, item
);
1251 /* The code below could be moved into handle_row. */
1252 CFDataRef pref
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
1254 if (c
->dest_keybag
!= KEYBAG_NONE
) {
1255 CFMutableDictionaryRef auth_attribs
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1256 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInAuthenticatedDataFlag
) {
1257 CFTypeRef value
= CFDictionaryGetValue(item
, desc
->name
);
1259 CFDictionaryAddValue(auth_attribs
, desc
->name
, value
);
1260 CFDictionaryRemoveValue(item
, desc
->name
);
1264 /* Encode and encrypt the item to the specified keybag. */
1265 CFDataRef edata
= NULL
;
1266 bool encrypted
= ks_encrypt_data(c
->dest_keybag
, access_control
, q
->q_use_cred_handle
, item
, auth_attribs
, &edata
, false, &q
->q_error
);
1267 CFDictionaryRemoveAllValues(item
);
1268 CFRelease(auth_attribs
);
1270 CFDictionarySetValue(item
, kSecValueData
, edata
);
1271 CFReleaseSafe(edata
);
1273 seccritical("ks_encrypt_data %@,rowid=%" PRId64
": failed: %@", q
->q_class
->name
, rowid
, q
->q_error
);
1274 CFReleaseNull(q
->q_error
);
1277 if (CFDictionaryGetCount(item
)) {
1278 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
1279 CFArrayAppendValue((CFMutableArrayRef
)c
->qc
.result
, item
);
1282 CFReleaseSafe(pref
);
1287 OSStatus status
= SecErrorGetOSStatus(localError
);
1289 if (status
== errSecInteractionNotAllowed
&& is_akpu
&& skip_akpu
) {
1290 // We expect akpu items to be inaccessible when the device is locked.
1291 CFReleaseNull(localError
);
1293 /* This happens a lot when trying to migrate keychain before first unlock, so only a notice */
1294 /* If the error is "corrupted item" then we just ignore it, otherwise we save it in the query */
1295 secinfo("item","Could not export item for rowid %llu: %@", rowid
, localError
);
1297 if(status
== errSecDecode
) {
1298 CFReleaseNull(localError
);
1300 CFReleaseSafe(q
->q_error
);
1301 q
->q_error
=localError
;
1305 CFReleaseSafe(access_control
);
1309 SecCreateKeybagUUID(keybag_handle_t keybag
)
1311 #if !TARGET_HAS_KEYSTORE
1316 if (aks_get_bag_uuid(keybag
, uuid
) != KERN_SUCCESS
)
1318 uuid_unparse_lower(uuid
, uuidstr
);
1319 return CFStringCreateWithCString(NULL
, uuidstr
, kCFStringEncodingUTF8
);
1325 SecServerCopyKeychainPlist(SecDbConnectionRef dbt
,
1326 SecurityClient
*client
,
1327 keybag_handle_t src_keybag
,
1328 keybag_handle_t dest_keybag
,
1329 enum SecItemFilter filter
,
1330 CFErrorRef
*error
) {
1331 CFMutableDictionaryRef keychain
;
1332 keychain
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1333 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1335 bool inMultiUser
= false;
1336 CFStringRef keybaguuid
= NULL
;
1337 Query q
= { .q_keybag
= src_keybag
,
1342 if (error
&& !*error
)
1343 SecError(errSecAllocate
, error
, CFSTR("Can't create keychain dictionary"));
1348 kSecReturnDataMask
|
1349 kSecReturnAttributesMask
|
1350 kSecReturnPersistentRefMask
;
1351 q
.q_limit
= kSecMatchUnlimited
;
1352 q
.q_skip_acl_items
= true;
1355 #if TARGET_OS_IPHONE
1356 if (client
&& client
->inMultiUser
) {
1357 q
.q_musrView
= SecMUSRCreateActiveUserUUID(client
->uid
);
1362 q
.q_musrView
= SecMUSRGetSingleUserKeychainUUID();
1363 CFRetain(q
.q_musrView
);
1365 keybaguuid
= SecCreateKeybagUUID(dest_keybag
);
1367 CFDictionarySetValue(keychain
, kSecBackupKeybagUUIDKey
, keybaguuid
);
1370 /* Get rid of this duplicate. */
1371 const SecDbClass
*SecDbClasses
[] = {
1378 for (class_ix
= 0; class_ix
< array_size(SecDbClasses
);
1380 q
.q_class
= SecDbClasses
[class_ix
];
1381 struct s3dl_export_row_ctx ctx
= {
1382 .qc
= { .q
= &q
, .dbt
= dbt
},
1383 .dest_keybag
= dest_keybag
, .filter
= filter
,
1384 .multiUser
= inMultiUser
,
1387 secnotice("item", "exporting class '%@'", q
.q_class
->name
);
1389 CFErrorRef localError
= NULL
;
1390 if (s3dl_query(s3dl_export_row
, &ctx
, &localError
)) {
1391 if (CFArrayGetCount(ctx
.qc
.result
))
1392 CFDictionaryAddValue(keychain
, q
.q_class
->name
, ctx
.qc
.result
);
1395 OSStatus status
= (OSStatus
)CFErrorGetCode(localError
);
1396 if (status
== errSecItemNotFound
) {
1397 CFRelease(localError
);
1399 secerror("Export failed: %@", localError
);
1401 CFReleaseSafe(*error
);
1402 *error
= localError
;
1404 CFRelease(localError
);
1406 CFReleaseNull(keychain
);
1407 CFReleaseNull(ctx
.qc
.result
);
1411 CFReleaseNull(ctx
.qc
.result
);
1415 CFReleaseNull(q
.q_musrView
);
1416 CFReleaseNull(keybaguuid
);
1421 struct SecServerImportClassState
{
1422 SecDbConnectionRef dbt
;
1424 keybag_handle_t src_keybag
;
1425 keybag_handle_t dest_keybag
;
1426 SecurityClient
*client
;
1427 enum SecItemFilter filter
;
1430 struct SecServerImportItemState
{
1431 const SecDbClass
*class;
1432 struct SecServerImportClassState
*s
;
1436 SecServerImportItem(const void *value
, void *context
)
1438 struct SecServerImportItemState
*state
= (struct SecServerImportItemState
*)context
;
1439 bool inMultiUser
= false;
1440 #if TARGET_OS_IPHONE
1441 if (state
->s
->client
->inMultiUser
)
1445 if (state
->s
->error
)
1448 if (!isDictionary(value
)) {
1449 SecError(errSecParam
, &state
->s
->error
, CFSTR("value %@ is not a dictionary"), value
);
1453 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
1455 secdebug("item", "Import Item : %@", dict
);
1457 /* We use the kSecSysBoundItemFilter to indicate that we don't
1458 * preserve rowid's during import.
1460 if (state
->s
->filter
== kSecBackupableItemFilter
) {
1463 /* We don't filter non sys_bound items during import since we know we
1464 * will never have any in this case.
1466 if (SecItemIsSystemBound(dict
, state
->class, inMultiUser
))
1470 * Don't bother with u items when in edu mode since our current backup system
1471 * don't keep track of items that blongs to the device (u) but rather just
1472 * merge them into one blob.
1474 if (inMultiUser
&& (pdmu
= CFDictionaryGetValue(dict
, kSecAttrAccessible
))) {
1475 if (CFEqual(pdmu
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) ||
1476 CFEqual(pdmu
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
) ||
1477 CFEqual(pdmu
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) ||
1478 CFEqual(pdmu
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
))
1480 secdebug("item", "Skipping KU item : %@", dict
);
1488 /* This is sligthly confusing:
1489 - During upgrade all items are exported with KEYBAG_NONE.
1490 - During restore from backup, existing sys_bound items are exported with KEYBAG_NONE, and are exported as dictionary of attributes.
1491 - Item in the actual backup are export with a real keybag, and are exported as encrypted v_Data and v_PersistentRef
1493 if (state
->s
->src_keybag
== KEYBAG_NONE
) {
1494 item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, state
->class, dict
, state
->s
->dest_keybag
, &state
->s
->error
);
1496 item
= SecDbItemCreateWithBackupDictionary(kCFAllocatorDefault
, state
->class, dict
, state
->s
->src_keybag
, state
->s
->dest_keybag
, &state
->s
->error
);
1504 CFDataRef musr
= NULL
;
1505 CFDataRef musrBackup
= CFDictionaryGetValue(dict
, kSecAttrMultiUser
);
1506 CFDataRef systemKeychainUUID
= SecMUSRGetSystemKeychainUUID();
1507 bool systemKeychain
= CFEqualSafe(musrBackup
, systemKeychainUUID
);
1509 #if TARGET_OS_IPHONE
1510 if (state
->s
->client
&& state
->s
->client
->inMultiUser
) {
1511 if (systemKeychain
) {
1512 secwarning("system keychain not allowed in multi user mode for item: %@", item
);
1514 musr
= SecMUSRCreateActiveUserUUID(state
->s
->client
->uid
);
1519 if (systemKeychain
) {
1520 musr
= SecMUSRCopySystemKeychainUUID();
1522 musr
= SecMUSRGetSingleUserKeychainUUID();
1527 CFReleaseNull(item
);
1529 SecDbItemSetValueWithName(item
, CFSTR("musr"), musr
, &state
->s
->error
);
1541 if(state
->s
->filter
!= kSecSysBoundItemFilter
) {
1542 SecDbItemExtractRowIdFromBackupDictionary(item
, dict
, &state
->s
->error
);
1544 SecDbItemInferSyncable(item
, &state
->s
->error
);
1545 insertStatus
= SecDbItemInsert(item
, state
->s
->dbt
, &state
->s
->error
);
1546 if (!insertStatus
) {
1548 When running in EduMode, multiple users share the same
1549 keychain and unfortionaly the rowid is used a
1550 persistant reference and is part of the contraints (its
1551 UNIQUE), so lets clear the rowid and try to insert the
1554 This even happens for normal operation because of
1555 SysBound entries, so in case of a failure, lets try
1556 again to insert the record.
1558 SecDbItemClearRowId(item
, NULL
);
1559 SecDbItemInsert(item
, state
->s
->dbt
, &state
->s
->error
);
1563 /* Reset error if we had one, since we just skip the current item
1564 and continue importing what we can. */
1565 if (state
->s
->error
) {
1566 secwarning("Failed to import an item (%@) of class '%@': %@ - ignoring error.",
1567 item
, state
->class->name
, state
->s
->error
);
1568 CFReleaseNull(state
->s
->error
);
1571 CFReleaseSafe(item
);
1574 static void SecServerImportClass(const void *key
, const void *value
,
1576 struct SecServerImportClassState
*state
=
1577 (struct SecServerImportClassState
*)context
;
1580 if (!isString(key
)) {
1581 SecError(errSecParam
, &state
->error
, CFSTR("class name %@ is not a string"), key
);
1584 /* ignore the Keybag UUID */
1585 if (CFEqual(key
, kSecBackupKeybagUUIDKey
))
1587 const SecDbClass
*class = kc_class_with_name(key
);
1589 secwarning("Ignoring unknown key class '%@'", key
);
1592 if (class == &identity_class
) {
1593 SecError(errSecParam
, &state
->error
, CFSTR("attempt to import an identity"));
1596 struct SecServerImportItemState item_state
= {
1597 .class = class, .s
= state
,
1599 if (isArray(value
)) {
1600 CFArrayRef items
= (CFArrayRef
)value
;
1601 secwarning("Import %ld items of class %@ (filter %d)", (long)CFArrayGetCount(items
), key
, state
->filter
);
1602 CFArrayApplyFunction(items
, CFRangeMake(0, CFArrayGetCount(items
)),
1603 SecServerImportItem
, &item_state
);
1604 } else if (isDictionary(value
)) {
1605 CFDictionaryRef item
= (CFDictionaryRef
)value
;
1606 secwarning("Import %ld items of class %@ (filter %d)", (long)CFDictionaryGetCount(item
), key
, state
->filter
);
1607 SecServerImportItem(item
, &item_state
);
1609 secwarning("Unknown value type for class %@ (filter %d)", key
, state
->filter
);
1613 bool SecServerImportKeychainInPlist(SecDbConnectionRef dbt
, SecurityClient
*client
,
1614 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
1615 CFDictionaryRef keychain
, enum SecItemFilter filter
, CFErrorRef
*error
) {
1616 CFStringRef keybaguuid
= NULL
;
1619 CFDictionaryRef sys_bound
= NULL
;
1620 if (filter
== kSecBackupableItemFilter
) {
1621 /* Grab a copy of all the items for which SecItemIsSystemBound()
1623 require(sys_bound
= SecServerCopyKeychainPlist(dbt
, client
, KEYBAG_DEVICE
,
1624 KEYBAG_NONE
, kSecSysBoundItemFilter
,
1629 * Validate the uuid of the source keybag matches what we have in the backup
1631 keybaguuid
= SecCreateKeybagUUID(src_keybag
);
1633 CFStringRef uuid
= CFDictionaryGetValue(keychain
, kSecBackupKeybagUUIDKey
);
1634 if (isString(uuid
)) {
1635 require_action(CFEqual(keybaguuid
, uuid
), errOut
,
1636 SecError(errSecDecode
, error
, CFSTR("Keybag UUID (%@) mismatch with backup (%@)"),
1641 /* Delete everything in the keychain. */
1642 #if TARGET_OS_IPHONE
1643 if (client
->inMultiUser
) {
1644 CFDataRef musrView
= SecMUSRCreateActiveUserUUID(client
->uid
);
1645 require_action(musrView
, errOut
, ok
= false);
1646 require_action(ok
= SecServerDeleteAllForUser(dbt
, musrView
, true, error
), errOut
, CFReleaseNull(musrView
));
1647 CFReleaseNull(musrView
);
1651 require(ok
= SecServerDeleteAll(dbt
, error
), errOut
);
1654 struct SecServerImportClassState state
= {
1656 .src_keybag
= src_keybag
,
1657 .dest_keybag
= dest_keybag
,
1661 /* Import the provided items, preserving rowids. */
1662 secwarning("Restoring backup items '%ld'", (long)CFDictionaryGetCount(keychain
));
1663 CFDictionaryApplyFunction(keychain
, SecServerImportClass
, &state
);
1666 state
.src_keybag
= KEYBAG_NONE
;
1667 /* Import the items we preserved with random rowids. */
1668 state
.filter
= kSecSysBoundItemFilter
;
1669 secwarning("Restoring sysbound items '%ld'", (long)CFDictionaryGetCount(sys_bound
));
1670 CFDictionaryApplyFunction(sys_bound
, SecServerImportClass
, &state
);
1674 CFReleaseSafe(*error
);
1675 *error
= state
.error
;
1677 CFRelease(state
.error
);
1683 CFReleaseSafe(sys_bound
);
1684 CFReleaseSafe(keybaguuid
);
1690 SecServerBackupGetKeybagUUID(CFDictionaryRef keychain
)
1692 CFStringRef uuid
= CFDictionaryGetValue(keychain
, kSecBackupKeybagUUIDKey
);
1693 if (!isString(uuid
))
1698 #pragma mark - key rolling support
1701 struct check_generation_ctx
{
1702 struct s3dl_query_ctx query_ctx
;
1703 uint32_t current_generation
;
1706 static void check_generation(sqlite3_stmt
*stmt
, void *context
) {
1707 struct check_generation_ctx
*c
= context
;
1708 CFDataRef blob
= NULL
;
1710 const uint8_t *cursor
= NULL
;
1712 keyclass_t keyclass
;
1713 uint32_t current_generation
= c
->current_generation
;
1715 require(blob
= s3dl_copy_data_from_col(stmt
, 1, &c
->query_ctx
.q
->q_error
), out
);
1716 blobLen
= CFDataGetLength(blob
);
1717 cursor
= CFDataGetBytePtr(blob
);
1719 /* Check for underflow, ensuring we have at least one full AES block left. */
1720 if (blobLen
< sizeof(version
) + sizeof(keyclass
)) {
1721 SecError(errSecDecode
, &c
->query_ctx
.q
->q_error
, CFSTR("check_generation: Check for underflow"));
1725 version
= *((uint32_t *)cursor
);
1726 cursor
+= sizeof(version
);
1728 (void) version
; // TODO: do something with the version number.
1730 keyclass
= *((keyclass_t
*)cursor
);
1732 // TODO: export get_key_gen macro
1733 if (((keyclass
& ~key_class_last
) == 0) != (current_generation
== 0)) {
1734 c
->query_ctx
.found
++;
1737 CFReleaseSafe(blob
);
1741 c
->query_ctx
.found
++;
1742 CFReleaseSafe(blob
);
1745 bool s3dl_dbt_keys_current(SecDbConnectionRef dbt
, uint32_t current_generation
, CFErrorRef
*error
) {
1746 CFErrorRef localError
= NULL
;
1747 struct check_generation_ctx ctx
= { .query_ctx
= { .dbt
= dbt
}, .current_generation
= current_generation
};
1749 const SecDbClass
*classes
[] = {
1756 for (size_t class_ix
= 0; class_ix
< array_size(classes
); ++class_ix
) {
1757 Query
*q
= query_create(classes
[class_ix
], NULL
, NULL
, &localError
);
1761 ctx
.query_ctx
.q
= q
;
1762 q
->q_limit
= kSecMatchUnlimited
;
1764 bool ok
= s3dl_query(check_generation
, &ctx
, &localError
);
1765 query_destroy(q
, NULL
);
1766 CFReleaseNull(ctx
.query_ctx
.result
);
1768 if (!ok
&& localError
&& (CFErrorGetCode(localError
) == errSecItemNotFound
)) {
1769 CFReleaseNull(localError
);
1772 secerror("Class %@ not up to date", classes
[class_ix
]->name
);
1778 bool s3dl_dbt_update_keys(SecDbConnectionRef dbt
, SecurityClient
*client
, CFErrorRef
*error
) {
1779 return SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
1780 __block
bool ok
= false;
1781 uint32_t keystore_generation_status
;
1783 /* can we migrate to new class keys right now? */
1784 if (!aks_generation(KEYBAG_DEVICE
, generation_noop
, &keystore_generation_status
) &&
1785 (keystore_generation_status
& generation_change_in_progress
)) {
1787 /* take a lock assertion */
1788 bool operated_while_unlocked
= SecAKSDoWhileUserBagLocked(error
, ^{
1789 CFErrorRef localError
= NULL
;
1790 CFDictionaryRef backup
= SecServerCopyKeychainPlist(dbt
, NULL
,
1791 KEYBAG_DEVICE
, KEYBAG_NONE
, kSecNoItemFilter
, &localError
);
1794 secerror("Ignoring export error: %@ during roll export", localError
);
1795 CFReleaseNull(localError
);
1797 ok
= SecServerImportKeychainInPlist(dbt
, client
, KEYBAG_NONE
,
1798 KEYBAG_DEVICE
, backup
, kSecNoItemFilter
, &localError
);
1800 secerror("Ignoring export error: %@ during roll export", localError
);
1801 CFReleaseNull(localError
);
1806 if (!operated_while_unlocked
)
1809 ok
= SecError(errSecBadReq
, error
, CFSTR("No key roll in progress."));