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>
32 #include <securityd/SecDbKeychainItem.h>
33 #include <securityd/SecItemSchema.h>
34 #include <securityd/SecItemServer.h>
35 #include <Security/SecBasePriv.h>
36 #include <Security/SecItem.h>
37 #include <Security/SecItemPriv.h>
38 #include <Security/SecItemInternal.h>
39 #include <securityd/SOSCloudCircleServer.h>
40 #include <utilities/array_size.h>
41 #include <utilities/SecIOFormat.h>
43 /* label when certificate data is joined with key data */
44 #define CERTIFICATE_DATA_COLUMN_LABEL "certdata"
46 const SecDbAttr
*SecDbAttrWithKey(const SecDbClass
*c
,
49 /* Special case: identites can have all attributes of either cert
51 if (c
== &identity_class
) {
52 const SecDbAttr
*desc
;
53 if (!(desc
= SecDbAttrWithKey(&cert_class
, key
, 0)))
54 desc
= SecDbAttrWithKey(&keys_class
, key
, error
);
59 SecDbForEachAttr(c
, a
) {
60 if (CFEqual(a
->name
, key
))
65 SecError(errSecNoSuchAttr
, error
, CFSTR("attribute %@ not found in class %@"), key
, c
->name
);
70 bool kc_transaction(SecDbConnectionRef dbt
, CFErrorRef
*error
, bool(^perform
)()) {
71 __block
bool ok
= true;
72 return ok
&& SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
73 ok
= *commit
= perform();
77 static CFStringRef
SecDbGetKindSQL(SecDbAttrKind kind
) {
82 case kSecDbPrimaryKeyAttr
:
83 case kSecDbEncryptedDataAttr
:
85 case kSecDbAccessAttr
:
86 case kSecDbStringAttr
:
88 case kSecDbNumberAttr
:
91 return CFSTR("INTEGER");
93 case kSecDbCreationDateAttr
:
94 case kSecDbModificationDateAttr
:
97 return CFSTR("INTEGER PRIMARY KEY AUTOINCREMENT");
98 case kSecDbAccessControlAttr
:
99 /* This attribute does not exist in the DB. */
104 static void SecDbAppendUnique(CFMutableStringRef sql
, CFStringRef value
, bool *haveUnique
) {
107 CFStringAppend(sql
, CFSTR("UNIQUE("));
109 SecDbAppendElement(sql
, value
, haveUnique
);
112 void SecDbAppendCreateTableWithClass(CFMutableStringRef sql
, const SecDbClass
*c
) {
113 CFStringAppendFormat(sql
, 0, CFSTR("CREATE TABLE %@("), c
->name
);
114 SecDbForEachAttrWithMask(c
,desc
,kSecDbInFlag
) {
115 CFStringAppendFormat(sql
, 0, CFSTR("%@ %@"), desc
->name
, SecDbGetKindSQL(desc
->kind
));
116 if (desc
->flags
& kSecDbNotNullFlag
)
117 CFStringAppend(sql
, CFSTR(" NOT NULL"));
118 if (desc
->flags
& kSecDbDefault0Flag
)
119 CFStringAppend(sql
, CFSTR(" DEFAULT 0"));
120 if (desc
->flags
& kSecDbDefaultEmptyFlag
)
121 CFStringAppend(sql
, CFSTR(" DEFAULT ''"));
122 CFStringAppend(sql
, CFSTR(","));
125 bool haveUnique
= false;
126 SecDbForEachAttrWithMask(c
,desc
,kSecDbPrimaryKeyFlag
| kSecDbInFlag
) {
127 SecDbAppendUnique(sql
, desc
->name
, &haveUnique
);
130 CFStringAppend(sql
, CFSTR(")"));
132 CFStringAppend(sql
, CFSTR(");"));
135 static CFDataRef
SecPersistentRefCreateWithItem(SecDbItemRef item
, CFErrorRef
*error
) {
136 sqlite3_int64 row_id
= SecDbItemGetRowId(item
, error
);
138 return _SecItemMakePersistentRef(SecDbItemGetClass(item
)->name
, row_id
);
142 CFTypeRef
SecDbItemCopyResult(SecDbItemRef item
, ReturnTypeMask return_type
, CFErrorRef
*error
) {
145 if (return_type
== 0) {
146 /* Caller isn't interested in any results at all. */
148 } else if (return_type
== kSecReturnDataMask
) {
149 a_result
= SecDbItemGetCachedValueWithName(item
, kSecValueData
);
151 CFRetainSafe(a_result
);
153 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
155 } else if (return_type
== kSecReturnPersistentRefMask
) {
156 a_result
= SecPersistentRefCreateWithItem(item
, error
);
158 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableForCFTypes(CFGetAllocator(item
));
159 /* We need to return more than one value. */
160 if (return_type
& kSecReturnRefMask
) {
161 CFDictionarySetValue(dict
, kSecClass
, SecDbItemGetClass(item
)->name
);
163 CFOptionFlags mask
= (((return_type
& kSecReturnDataMask
|| return_type
& kSecReturnRefMask
) ? kSecDbReturnDataFlag
: 0) |
164 ((return_type
& kSecReturnAttributesMask
|| return_type
& kSecReturnRefMask
) ? kSecDbReturnAttrFlag
: 0));
165 SecDbForEachAttr(SecDbItemGetClass(item
), desc
) {
166 if ((desc
->flags
& mask
) != 0) {
167 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
168 if (value
&& !CFEqual(kCFNull
, value
)) {
169 CFDictionarySetValue(dict
, desc
->name
, value
);
170 } else if (value
== NULL
) {
176 if (return_type
& kSecReturnPersistentRefMask
) {
177 CFDataRef pref
= SecPersistentRefCreateWithItem(item
, error
);
178 CFDictionarySetValue(dict
, kSecValuePersistentRef
, pref
);
188 /* AUDIT[securityd](done):
189 attributes (ok) is a caller provided dictionary, only its cf type has
193 s3dl_query_add(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
, CFErrorRef
*error
)
195 if (query_match_count(q
) != 0)
196 return errSecItemMatchUnsupported
;
198 /* Add requires a class to be specified unless we are adding a ref. */
199 if (q
->q_use_item_list
)
200 return errSecUseItemListUnsupported
;
202 /* Actual work here. */
203 SecDbItemRef item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, q
->q_class
, q
->q_item
, KEYBAG_DEVICE
, error
);
209 ok
= SecDbItemSetValueWithName(item
, CFSTR("v_Data"), q
->q_data
, error
);
211 ok
= SecDbItemSetRowId(item
, q
->q_row_id
, error
);
212 SecDbItemSetCredHandle(item
, q
->q_use_cred_handle
);
215 ok
= SecDbItemInsert(item
, dbt
, q
->q_required_access_controls
, error
);
216 CFRetainAssign(q
->q_use_cred_handle
, item
->credHandle
);
219 if (result
&& q
->q_return_type
) {
220 *result
= SecDbItemCopyResult(item
, q
->q_return_type
, error
);
223 if (!ok
&& error
&& *error
) {
224 if (CFEqual(CFErrorGetDomain(*error
), kSecDbErrorDomain
) && CFErrorGetCode(*error
) == SQLITE_CONSTRAINT
) {
225 CFReleaseNull(*error
);
226 SecError(errSecDuplicateItem
, error
, CFSTR("duplicate item %@"), item
);
232 if (SecDbItemIsSyncable(item
))
233 q
->q_sync_changed
= true;
236 secdebug("dbitem", "inserting item %@%s%@", item
, ok
? "" : "failed: ", ok
|| error
== NULL
? (CFErrorRef
)CFSTR("") : *error
);
243 typedef void (*s3dl_handle_row
)(sqlite3_stmt
*stmt
, void *context
);
246 s3dl_copy_data_from_col(sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
247 return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt
, col
),
248 sqlite3_column_bytes(stmt
, col
),
253 s3dl_item_from_col(sqlite3_stmt
*stmt
, Query
*q
, int col
, CFArrayRef accessGroups
,
254 CFMutableDictionaryRef
*item
, SecAccessControlRef
*access_control
, CFErrorRef
*error
) {
255 CFDataRef edata
= NULL
;
257 require(edata
= s3dl_copy_data_from_col(stmt
, col
, error
), out
);
258 ok
= s3dl_item_from_data(edata
, q
, accessGroups
, item
, access_control
, error
);
261 CFReleaseSafe(edata
);
265 struct s3dl_query_ctx
{
267 CFArrayRef accessGroups
;
268 SecDbConnectionRef dbt
;
273 /* Return whatever the caller requested based on the value of q->q_return_type.
274 keys and values must be 3 larger than attr_count in size to accomadate the
275 optional data, class and persistent ref results. This is so we can use
276 the CFDictionaryCreate() api here rather than appending to a
277 mutable dictionary. */
278 static CF_RETURNS_RETAINED CFTypeRef
handle_result(Query
*q
, CFMutableDictionaryRef item
,
279 sqlite_int64 rowid
) {
282 data
= CFDictionaryGetValue(item
, kSecValueData
);
283 if (q
->q_return_type
== 0) {
284 /* Caller isn't interested in any results at all. */
286 } else if (q
->q_return_type
== kSecReturnDataMask
) {
291 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
293 } else if (q
->q_return_type
== kSecReturnPersistentRefMask
) {
294 a_result
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
296 /* We need to return more than one value. */
297 if (q
->q_return_type
& kSecReturnRefMask
) {
298 CFDictionarySetValue(item
, kSecClass
, q
->q_class
->name
);
299 } else if ((q
->q_return_type
& kSecReturnAttributesMask
)) {
300 if (!(q
->q_return_type
& kSecReturnDataMask
)) {
301 CFDictionaryRemoveValue(item
, kSecValueData
);
305 CFDictionaryRemoveAllValues(item
);
306 if ((q
->q_return_type
& kSecReturnDataMask
) && data
) {
307 CFDictionarySetValue(item
, kSecValueData
, data
);
311 if (q
->q_return_type
& kSecReturnPersistentRefMask
) {
312 CFDataRef pref
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
313 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
324 static void s3dl_merge_into_dict(const void *key
, const void *value
, void *context
) {
325 CFDictionarySetValue(context
, key
, value
);
328 static void s3dl_query_row(sqlite3_stmt
*stmt
, void *context
) {
329 struct s3dl_query_ctx
*c
= context
;
332 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
333 CFMutableDictionaryRef item
;
334 bool ok
= s3dl_item_from_col(stmt
, q
, 1, c
->accessGroups
, &item
, NULL
, &q
->q_error
);
336 OSStatus status
= SecErrorGetOSStatus(q
->q_error
);
337 // errSecDecode means the item is corrupted, stash it for delete.
338 if (status
== errSecDecode
) {
339 secwarning("ignoring corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, rowid
, q
->q_error
);
341 CFDataRef edata
= s3dl_copy_data_from_col(stmt
, 1, NULL
);
342 CFMutableStringRef edatastring
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
344 CFStringAppendEncryptedData(edatastring
, edata
);
345 secnotice("item", "corrupted edata=%@", edatastring
);
347 CFReleaseSafe(edata
);
348 CFReleaseSafe(edatastring
);
350 CFReleaseNull(q
->q_error
);
352 secerror("decode %@,rowid=%" PRId64
" failed (%" PRIdOSStatus
"): %@", q
->q_class
->name
, rowid
, status
, q
->q_error
);
354 // q->q_error will be released appropriately by a call to query_error
361 if (q
->q_class
== &identity_class
) {
362 // TODO: Use col 2 for key rowid and use both rowids in persistent ref.
364 CFMutableDictionaryRef key
;
365 /* TODO : if there is a errSecDecode error here, we should cleanup */
366 if (!s3dl_item_from_col(stmt
, q
, 3, c
->accessGroups
, &key
, NULL
, &q
->q_error
) || !key
)
369 CFDataRef certData
= CFDictionaryGetValue(item
, kSecValueData
);
371 CFDictionarySetValue(key
, CFSTR(CERTIFICATE_DATA_COLUMN_LABEL
),
373 CFDictionaryRemoveValue(item
, kSecValueData
);
375 CFDictionaryApplyFunction(item
, s3dl_merge_into_dict
, key
);
380 if (!match_item(c
->dbt
, q
, c
->accessGroups
, item
))
383 CFTypeRef a_result
= handle_result(q
, item
, rowid
);
385 if (a_result
== kCFNull
) {
386 /* Caller wasn't interested in a result, but we still
387 count this row as found. */
388 CFRelease(a_result
); // Help shut up clang
389 } else if (q
->q_limit
== 1) {
390 c
->result
= a_result
;
392 CFArrayAppendValue((CFMutableArrayRef
)c
->result
, a_result
);
403 SecDbAppendWhereROWID(CFMutableStringRef sql
,
404 CFStringRef col
, sqlite_int64 row_id
,
407 SecDbAppendWhereOrAnd(sql
, needWhere
);
408 CFStringAppendFormat(sql
, NULL
, CFSTR("%@=%lld"), col
, row_id
);
413 SecDbAppendWhereAttrs(CFMutableStringRef sql
, const Query
*q
, bool *needWhere
) {
414 CFIndex ix
, attr_count
= query_attr_count(q
);
415 for (ix
= 0; ix
< attr_count
; ++ix
) {
416 SecDbAppendWhereOrAndEquals(sql
, query_attr_at(q
, ix
).key
, needWhere
);
421 SecDbAppendWhereAccessGroups(CFMutableStringRef sql
,
423 CFArrayRef accessGroups
,
425 CFIndex ix
, ag_count
;
426 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
))) {
430 SecDbAppendWhereOrAnd(sql
, needWhere
);
431 CFStringAppend(sql
, col
);
432 CFStringAppend(sql
, CFSTR(" IN (?"));
433 for (ix
= 1; ix
< ag_count
; ++ix
) {
434 CFStringAppend(sql
, CFSTR(",?"));
436 CFStringAppend(sql
, CFSTR(")"));
439 static void SecDbAppendWhereClause(CFMutableStringRef sql
, const Query
*q
,
440 CFArrayRef accessGroups
) {
441 bool needWhere
= true;
442 SecDbAppendWhereROWID(sql
, CFSTR("ROWID"), q
->q_row_id
, &needWhere
);
443 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
444 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
447 static void SecDbAppendLimit(CFMutableStringRef sql
, CFIndex limit
) {
448 if (limit
!= kSecMatchUnlimited
)
449 CFStringAppendFormat(sql
, NULL
, CFSTR(" LIMIT %" PRIdCFIndex
), limit
);
452 static CFStringRef
s3dl_select_sql(Query
*q
, CFArrayRef accessGroups
) {
453 CFMutableStringRef sql
= CFStringCreateMutable(NULL
, 0);
454 if (q
->q_class
== &identity_class
) {
455 CFStringAppendFormat(sql
, NULL
, CFSTR("SELECT crowid, "
456 CERTIFICATE_DATA_COLUMN_LABEL
", rowid,data FROM "
457 "(SELECT cert.rowid AS crowid, cert.labl AS labl,"
458 " cert.issr AS issr, cert.slnr AS slnr, cert.skid AS skid,"
459 " keys.*,cert.data AS " CERTIFICATE_DATA_COLUMN_LABEL
461 " WHERE keys.priv == 1 AND cert.pkhh == keys.klbl"));
462 SecDbAppendWhereAccessGroups(sql
, CFSTR("cert.agrp"), accessGroups
, 0);
463 /* The next 3 SecDbAppendWhere calls are in the same order as in
464 SecDbAppendWhereClause(). This makes sqlBindWhereClause() work,
465 as long as we do an extra sqlBindAccessGroups first. */
466 SecDbAppendWhereROWID(sql
, CFSTR("crowid"), q
->q_row_id
, 0);
467 CFStringAppend(sql
, CFSTR(")"));
468 bool needWhere
= true;
469 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
470 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
472 CFStringAppend(sql
, CFSTR("SELECT rowid, data FROM "));
473 CFStringAppend(sql
, q
->q_class
->name
);
474 SecDbAppendWhereClause(sql
, q
, accessGroups
);
476 SecDbAppendLimit(sql
, q
->q_limit
);
481 static bool sqlBindAccessGroups(sqlite3_stmt
*stmt
, CFArrayRef accessGroups
,
482 int *pParam
, CFErrorRef
*error
) {
485 CFIndex ix
, count
= accessGroups
? CFArrayGetCount(accessGroups
) : 0;
486 for (ix
= 0; ix
< count
; ++ix
) {
487 result
= SecDbBindObject(stmt
, param
++,
488 CFArrayGetValueAtIndex(accessGroups
, ix
),
497 static bool sqlBindWhereClause(sqlite3_stmt
*stmt
, const Query
*q
,
498 CFArrayRef accessGroups
, int *pParam
, CFErrorRef
*error
) {
501 CFIndex ix
, attr_count
= query_attr_count(q
);
502 for (ix
= 0; ix
< attr_count
; ++ix
) {
503 result
= SecDbBindObject(stmt
, param
++, query_attr_at(q
, ix
).value
, error
);
508 /* Bind the access group to the sql. */
510 result
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
517 bool SecDbItemQuery(SecDbQueryRef query
, CFArrayRef accessGroups
, SecDbConnectionRef dbconn
, CFErrorRef
*error
,
518 void (^handle_row
)(SecDbItemRef item
, bool *stop
)) {
519 __block
bool ok
= true;
520 __block
bool decrypted
= true;
521 /* Sanity check the query. */
523 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
525 bool (^return_attr
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
526 return attr
->kind
== kSecDbRowIdAttr
|| attr
->kind
== kSecDbEncryptedDataAttr
|| attr
->kind
== kSecDbSHA1Attr
;
529 CFStringRef sql
= s3dl_select_sql(query
, accessGroups
);
532 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
533 /* Bind the values being searched for to the SELECT statement. */
535 if (query
->q_class
== &identity_class
) {
536 /* Bind the access groups to cert.agrp. */
537 ok
&= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
540 ok
&= sqlBindWhereClause(stmt
, query
, accessGroups
, ¶m
, error
);
542 SecDbStep(dbconn
, stmt
, error
, ^(bool *stop
) {
543 SecDbItemRef item
= SecDbItemCreateWithStatement(kCFAllocatorDefault
, query
->q_class
, stmt
, query
->q_keybag
, error
, return_attr
);
545 if (query
->q_required_access_controls
) {
546 CFDataRef authNeeded
= NULL
;
547 item
->cryptoOp
= query
->q_crypto_op
;
548 SecDbItemSetCallerAccessGroups(item
, query
->q_caller_access_groups
);
549 decrypted
= SecDbItemEnsureDecrypted(item
, &authNeeded
, &query
->q_use_cred_handle
, NULL
);
550 if (decrypted
&& authNeeded
) {
551 // Do not process the item right now, just remember that it will need
552 // authentication to properly process it.
553 CFArrayAppendValue(query
->q_required_access_controls
, authNeeded
);
554 CFRelease(authNeeded
);
558 CFReleaseSafe(authNeeded
);
560 if (match_item(dbconn
, query
, accessGroups
, item
->attributes
))
561 handle_row(item
, stop
);
564 secerror("failed to create item from stmt: %@", error
? *error
: (CFErrorRef
)"no error");
566 CFReleaseNull(*error
);
581 s3dl_query(s3dl_handle_row handle_row
,
582 void *context
, CFErrorRef
*error
)
584 struct s3dl_query_ctx
*c
= context
;
585 SecDbConnectionRef dbt
= c
->dbt
;
587 CFArrayRef accessGroups
= c
->accessGroups
;
589 /* Sanity check the query. */
591 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
593 /* Actual work here. */
594 if (q
->q_limit
== 1) {
597 c
->result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
599 CFStringRef sql
= s3dl_select_sql(q
, accessGroups
);
600 bool ok
= SecDbWithSQL(dbt
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
602 /* Bind the values being searched for to the SELECT statement. */
604 if (q
->q_class
== &identity_class
) {
605 /* Bind the access groups to cert.agrp. */
606 sql_ok
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
609 sql_ok
= sqlBindWhereClause(stmt
, q
, accessGroups
, ¶m
, error
);
611 SecDbForEach(stmt
, error
, ^bool (int row_index
) {
612 handle_row(stmt
, context
);
613 return (!q
->q_error
) && (q
->q_limit
== kSecMatchUnlimited
|| c
->found
< q
->q_limit
);
621 // First get the error from the query, since errSecDuplicateItem from an
622 // update query should superceed the errSecItemNotFound below.
623 if (!query_error(q
, error
))
625 if (ok
&& c
->found
== 0)
626 ok
= SecError(errSecItemNotFound
, error
, CFSTR("no matching items found"));
632 s3dl_copy_matching(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
,
633 CFArrayRef accessGroups
, CFErrorRef
*error
)
635 struct s3dl_query_ctx ctx
= {
636 .q
= q
, .accessGroups
= accessGroups
, .dbt
= dbt
,
638 if (q
->q_row_id
&& query_attr_count(q
))
639 return SecError(errSecItemIllegalQuery
, error
,
640 CFSTR("attributes to query illegal; both row_id and other attributes can't be searched at the same time"));
642 // Only copy things that aren't tombstones unless the client explicitly asks otherwise.
643 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
644 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
645 bool ok
= s3dl_query(s3dl_query_row
, &ctx
, error
);
647 *result
= ctx
.result
;
649 CFReleaseSafe(ctx
.result
);
654 /* AUDIT[securityd](done):
655 attributesToUpdate (ok) is a caller provided dictionary,
656 only its cf types have been checked.
659 s3dl_query_update(SecDbConnectionRef dbt
, Query
*q
,
660 CFDictionaryRef attributesToUpdate
, CFArrayRef accessGroups
, CFErrorRef
*error
)
662 /* Sanity check the query. */
663 if (query_match_count(q
) != 0)
664 return SecError(errSecItemMatchUnsupported
, error
, CFSTR("match not supported in attributes to update"));
666 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported in attributes to update"));
667 if (q
->q_row_id
&& query_attr_count(q
))
668 return SecError(errSecItemIllegalQuery
, error
, CFSTR("attributes to update illegal; both row_id and other attributes can't be updated at the same time"));
670 __block
bool result
= true;
671 Query
*u
= query_create(q
->q_class
, attributesToUpdate
, error
);
672 if (u
== NULL
) return false;
673 require_action_quiet(query_update_parse(u
, attributesToUpdate
, error
), errOut
, result
= false);
675 result
&= SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
676 // Make sure we only update real items, not tombstones, unless the client explicitly asks otherwise.
677 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
678 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
679 result
&= SecDbItemQuery(q
, accessGroups
, dbt
, error
, ^(SecDbItemRef item
, bool *stop
) {
680 // We always need to know the error here.
681 CFErrorRef localError
= NULL
;
682 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
683 const SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
);
684 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, NULL
));
685 SecDbItemRef new_item
= SecDbItemCopyWithUpdates(item
, u
->q_item
, &localError
);
686 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
687 CFReleaseSafe(storedSHA1
);
688 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
689 // We just ignore this, and treat as if item is not found.
690 secwarning("deleting corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, SecDbItemGetRowId(item
, NULL
), localError
);
691 CFReleaseNull(localError
);
692 if (!SecDbItemDelete(item
, dbt
, false, &localError
)) {
693 secerror("failed to delete corrupt %@,rowid=%" PRId64
" %@", q
->q_class
->name
, SecDbItemGetRowId(item
, NULL
), localError
);
694 CFReleaseNull(localError
);
698 if (new_item
!= NULL
&& u
->q_access_control
!= NULL
)
699 SecDbItemSetAccessControl(new_item
, u
->q_access_control
, &localError
);
700 result
= CFErrorPropagate(localError
, error
) && new_item
;
702 bool item_is_sync
= SecDbItemIsSyncable(item
);
703 bool makeTombstone
= q
->q_use_tomb
? CFBooleanGetValue(q
->q_use_tomb
) : (item_is_sync
&& !SecDbItemIsTombstone(item
));
704 result
= SecDbItemUpdate(item
, new_item
, dbt
, makeTombstone
, error
);
707 if (item_is_sync
|| SecDbItemIsSyncable(new_item
))
708 q
->q_sync_changed
= true;
718 if (result
&& !q
->q_changed
)
719 result
= SecError(errSecItemNotFound
, error
, CFSTR("No items updated"));
721 if (!query_destroy(u
, error
))
727 s3dl_query_delete(SecDbConnectionRef dbt
, Query
*q
, CFArrayRef accessGroups
, CFErrorRef
*error
)
729 __block
bool ok
= true;
730 // Only delete things that aren't tombstones, unless the client explicitly asks otherwise.
731 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
732 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
733 ok
&= SecDbItemSelect(q
, dbt
, error
, ^bool(const SecDbAttr
*attr
) {
735 },^bool(CFMutableStringRef sql
, bool *needWhere
) {
736 SecDbAppendWhereClause(sql
, q
, accessGroups
);
738 },^bool(sqlite3_stmt
* stmt
, int col
) {
739 return sqlBindWhereClause(stmt
, q
, accessGroups
, &col
, error
);
740 }, ^(SecDbItemRef item
, bool *stop
) {
741 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
742 const SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
);
743 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, NULL
));
744 bool item_is_sync
= SecDbItemIsSyncable(item
);
745 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
746 CFReleaseSafe(storedSHA1
);
747 bool makeTombstone
= q
->q_use_tomb
? CFBooleanGetValue(q
->q_use_tomb
) : (item_is_sync
&& !SecDbItemIsTombstone(item
));
748 item
->cryptoOp
= kSecKsDelete
;
749 ok
= SecDbItemDelete(item
, dbt
, makeTombstone
, error
);
753 q
->q_sync_changed
= true;
756 if (ok
&& !q
->q_changed
) {
757 ok
= SecError(errSecItemNotFound
, error
, CFSTR("Delete failed to delete anything"));
762 /* Return true iff the item in question should not be backed up, nor restored,
763 but when restoring a backup the original version of the item should be
764 added back to the keychain again after the restore completes. */
765 static bool SecItemIsSystemBound(CFDictionaryRef item
, const SecDbClass
*class) {
766 CFStringRef agrp
= CFDictionaryGetValue(item
, kSecAttrAccessGroup
);
770 if (CFEqualSafe(agrp
, kSOSInternalAccessGroup
)) {
771 secdebug("backup", "found sysbound item: %@", item
);
775 if (CFEqual(agrp
, CFSTR("lockdown-identities"))) {
776 secdebug("backup", "found sys_bound item: %@", item
);
780 if (CFEqual(agrp
, CFSTR("apple")) && class == &genp_class
) {
781 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
782 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
783 if (isString(service
) && isString(account
) &&
784 CFEqual(service
, CFSTR("com.apple.managedconfiguration")) &&
785 (CFEqual(account
, CFSTR("Public")) ||
786 CFEqual(account
, CFSTR("Private")))) {
787 secdebug("backup", "found sys_bound item: %@", item
);
791 secdebug("backup", "found non sys_bound item: %@", item
);
795 /* Delete all items from the current keychain. If this is not an in
796 place upgrade we don't delete items in the 'lockdown-identities'
797 access group, this ensures that an import or restore of a backup
798 will never overwrite an existing activation record. */
799 static bool SecServerDeleteAll(SecDbConnectionRef dbt
, CFErrorRef
*error
) {
800 return kc_transaction(dbt
, error
, ^{
801 bool ok
= (SecDbExec(dbt
, CFSTR("DELETE from genp;"), error
) &&
802 SecDbExec(dbt
, CFSTR("DELETE from inet;"), error
) &&
803 SecDbExec(dbt
, CFSTR("DELETE from cert;"), error
) &&
804 SecDbExec(dbt
, CFSTR("DELETE from keys;"), error
));
809 struct s3dl_export_row_ctx
{
810 struct s3dl_query_ctx qc
;
811 keybag_handle_t dest_keybag
;
812 enum SecItemFilter filter
;
815 static void s3dl_export_row(sqlite3_stmt
*stmt
, void *context
) {
816 struct s3dl_export_row_ctx
*c
= context
;
818 SecAccessControlRef access_control
= NULL
;
819 CFErrorRef localError
= NULL
;
821 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
822 CFMutableDictionaryRef item
;
823 if (s3dl_item_from_col(stmt
, q
, 1, c
->qc
.accessGroups
, &item
, &access_control
, &localError
)) {
825 /* Only export sysbound items is do_sys_bound is true, only export non sysbound items otherwise. */
826 bool do_sys_bound
= c
->filter
== kSecSysBoundItemFilter
;
827 if (c
->filter
== kSecNoItemFilter
||
828 SecItemIsSystemBound(item
, q
->q_class
) == do_sys_bound
) {
829 /* Re-encode the item. */
830 secdebug("item", "export rowid %llu item: %@", rowid
, item
);
831 /* The code below could be moved into handle_row. */
832 CFDataRef pref
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
834 if (c
->dest_keybag
!= KEYBAG_NONE
) {
835 CFMutableDictionaryRef auth_attribs
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
836 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInAuthenticatedDataFlag
) {
837 CFTypeRef value
= CFDictionaryGetValue(item
, desc
->name
);
839 CFDictionaryAddValue(auth_attribs
, desc
->name
, value
);
840 CFDictionaryRemoveValue(item
, desc
->name
);
844 /* Encode and encrypt the item to the specified keybag. */
845 CFDataRef edata
= NULL
;
846 bool encrypted
= ks_encrypt_data(c
->dest_keybag
, access_control
, &q
->q_use_cred_handle
, item
, auth_attribs
, &edata
, &q
->q_error
);
847 CFDictionaryRemoveAllValues(item
);
848 CFRelease(auth_attribs
);
850 CFDictionarySetValue(item
, kSecValueData
, edata
);
851 CFReleaseSafe(edata
);
853 seccritical("ks_encrypt_data %@,rowid=%" PRId64
": failed: %@", q
->q_class
->name
, rowid
, q
->q_error
);
854 CFReleaseNull(q
->q_error
);
857 if (CFDictionaryGetCount(item
)) {
858 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
859 CFArrayAppendValue((CFMutableArrayRef
)c
->qc
.result
, item
);
868 /* This happens a lot when trying to migrate keychain before first unlock, so only a notice */
869 /* If the error is "corrupted item" then we just ignore it, otherwise we save it in the query */
870 secnotice("item","Could not export item for rowid %llu: %@", rowid
, localError
);
871 if(SecErrorGetOSStatus(localError
)==errSecDecode
) {
872 CFReleaseNull(localError
);
874 CFReleaseSafe(q
->q_error
);
875 q
->q_error
=localError
;
878 CFReleaseSafe(access_control
);
881 CF_RETURNS_RETAINED CFDictionaryRef
SecServerExportKeychainPlist(SecDbConnectionRef dbt
,
882 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
883 enum SecItemFilter filter
, CFErrorRef
*error
) {
884 CFMutableDictionaryRef keychain
;
885 keychain
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
886 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
888 if (error
&& !*error
)
889 SecError(errSecAllocate
, error
, CFSTR("Can't create keychain dictionary"));
893 Query q
= { .q_keybag
= src_keybag
};
894 q
.q_return_type
= kSecReturnDataMask
| kSecReturnAttributesMask
| \
895 kSecReturnPersistentRefMask
;
896 q
.q_limit
= kSecMatchUnlimited
;
898 /* Enable interactive means that auth-protected items will be silently ignored
899 (and their ACLs copied into query, which we will simply ignore). */
900 query_enable_interactive(&q
);
902 /* Get rid of this duplicate. */
903 const SecDbClass
*SecDbClasses
[] = {
910 for (class_ix
= 0; class_ix
< array_size(SecDbClasses
);
912 q
.q_class
= SecDbClasses
[class_ix
];
913 struct s3dl_export_row_ctx ctx
= {
914 .qc
= { .q
= &q
, .dbt
= dbt
},
915 .dest_keybag
= dest_keybag
, .filter
= filter
,
918 secnotice("item", "exporting class '%@'", q
.q_class
->name
);
920 CFErrorRef localError
= NULL
;
921 if (s3dl_query(s3dl_export_row
, &ctx
, &localError
)) {
922 if (CFArrayGetCount(ctx
.qc
.result
))
923 CFDictionaryAddValue(keychain
, q
.q_class
->name
, ctx
.qc
.result
);
926 OSStatus status
= (OSStatus
)CFErrorGetCode(localError
);
927 if (status
== errSecItemNotFound
) {
928 CFRelease(localError
);
930 secerror("Export failed: %@", localError
);
932 CFReleaseSafe(*error
);
935 CFRelease(localError
);
937 CFReleaseNull(keychain
);
938 CFReleaseNull(ctx
.qc
.result
);
942 CFReleaseNull(ctx
.qc
.result
);
945 /* Release all ignored ACLs of auth-protected items. */
946 CFReleaseNull(q
.q_required_access_controls
);
951 struct SecServerImportClassState
{
952 SecDbConnectionRef dbt
;
954 keybag_handle_t src_keybag
;
955 keybag_handle_t dest_keybag
;
956 enum SecItemFilter filter
;
959 struct SecServerImportItemState
{
960 const SecDbClass
*class;
961 struct SecServerImportClassState
*s
;
964 static void SecServerImportItem(const void *value
, void *context
) {
965 struct SecServerImportItemState
*state
=
966 (struct SecServerImportItemState
*)context
;
969 if (!isDictionary(value
)) {
970 SecError(errSecParam
, &state
->s
->error
, CFSTR("value %@ is not a dictionary"), value
);
974 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
976 secdebug("item", "Import Item : %@", dict
);
978 /* We don't filter non sys_bound items during import since we know we
979 will never have any in this case, we use the kSecSysBoundItemFilter
980 to indicate that we don't preserve rowid's during import instead. */
981 if (state
->s
->filter
== kSecBackupableItemFilter
&&
982 SecItemIsSystemBound(dict
, state
->class))
987 /* This is sligthly confusing:
988 - During upgrade all items are exported with KEYBAG_NONE.
989 - During restore from backup, existing sys_bound items are exported with KEYBAG_NONE, and are exported as dictionary of attributes.
990 - Item in the actual backup are export with a real keybag, and are exported as encrypted v_Data and v_PersistentRef
992 if (state
->s
->src_keybag
== KEYBAG_NONE
) {
993 item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, state
->class, dict
, state
->s
->dest_keybag
, &state
->s
->error
);
995 item
= SecDbItemCreateWithBackupDictionary(kCFAllocatorDefault
, state
->class, dict
, state
->s
->src_keybag
, state
->s
->dest_keybag
, &state
->s
->error
);
999 if(state
->s
->filter
!= kSecSysBoundItemFilter
) {
1000 SecDbItemExtractRowIdFromBackupDictionary(item
, dict
, &state
->s
->error
);
1002 SecDbItemInferSyncable(item
, &state
->s
->error
);
1003 SecDbItemInsert(item
, state
->s
->dbt
, NULL
, &state
->s
->error
);
1006 /* Reset error if we had one, since we just skip the current item
1007 and continue importing what we can. */
1008 if (state
->s
->error
) {
1009 secwarning("Failed to import an item (%@) of class '%@': %@ - ignoring error.",
1010 item
, state
->class->name
, state
->s
->error
);
1011 CFReleaseNull(state
->s
->error
);
1014 CFReleaseSafe(item
);
1017 static void SecServerImportClass(const void *key
, const void *value
,
1019 struct SecServerImportClassState
*state
=
1020 (struct SecServerImportClassState
*)context
;
1023 if (!isString(key
)) {
1024 SecError(errSecParam
, &state
->error
, CFSTR("class name %@ is not a string"), key
);
1027 const SecDbClass
*class = kc_class_with_name(key
);
1028 if (!class || class == &identity_class
) {
1029 SecError(errSecParam
, &state
->error
, CFSTR("attempt to import an identity"));
1032 struct SecServerImportItemState item_state
= {
1033 .class = class, .s
= state
1035 if (isArray(value
)) {
1036 CFArrayRef items
= (CFArrayRef
)value
;
1037 CFArrayApplyFunction(items
, CFRangeMake(0, CFArrayGetCount(items
)),
1038 SecServerImportItem
, &item_state
);
1040 CFDictionaryRef item
= (CFDictionaryRef
)value
;
1041 SecServerImportItem(item
, &item_state
);
1045 bool SecServerImportKeychainInPlist(SecDbConnectionRef dbt
,
1046 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
1047 CFDictionaryRef keychain
, enum SecItemFilter filter
, CFErrorRef
*error
) {
1050 CFDictionaryRef sys_bound
= NULL
;
1051 if (filter
== kSecBackupableItemFilter
) {
1052 /* Grab a copy of all the items for which SecItemIsSystemBound()
1054 require(sys_bound
= SecServerExportKeychainPlist(dbt
, KEYBAG_DEVICE
,
1055 KEYBAG_NONE
, kSecSysBoundItemFilter
,
1059 /* Delete everything in the keychain. */
1060 require(ok
= SecServerDeleteAll(dbt
, error
), errOut
);
1062 struct SecServerImportClassState state
= {
1064 .src_keybag
= src_keybag
,
1065 .dest_keybag
= dest_keybag
,
1068 /* Import the provided items, preserving rowids. */
1069 CFDictionaryApplyFunction(keychain
, SecServerImportClass
, &state
);
1072 state
.src_keybag
= KEYBAG_NONE
;
1073 /* Import the items we preserved with random rowids. */
1074 state
.filter
= kSecSysBoundItemFilter
;
1075 CFDictionaryApplyFunction(sys_bound
, SecServerImportClass
, &state
);
1079 CFReleaseSafe(*error
);
1080 *error
= state
.error
;
1082 CFRelease(state
.error
);
1088 CFReleaseSafe(sys_bound
);
1093 #pragma mark - key rolling support
1096 struct check_generation_ctx
{
1097 struct s3dl_query_ctx query_ctx
;
1098 uint32_t current_generation
;
1101 static void check_generation(sqlite3_stmt
*stmt
, void *context
) {
1102 struct check_generation_ctx
*c
= context
;
1103 CFDataRef blob
= NULL
;
1105 const uint8_t *cursor
= NULL
;
1107 keyclass_t keyclass
;
1108 uint32_t current_generation
= c
->current_generation
;
1110 require(blob
= s3dl_copy_data_from_col(stmt
, 1, &c
->query_ctx
.q
->q_error
), out
);
1111 blobLen
= CFDataGetLength(blob
);
1112 cursor
= CFDataGetBytePtr(blob
);
1114 /* Check for underflow, ensuring we have at least one full AES block left. */
1115 if (blobLen
< sizeof(version
) + sizeof(keyclass
)) {
1116 SecError(errSecDecode
, &c
->query_ctx
.q
->q_error
, CFSTR("check_generation: Check for underflow"));
1120 version
= *((uint32_t *)cursor
);
1121 cursor
+= sizeof(version
);
1123 (void) version
; // TODO: do something with the version number.
1125 keyclass
= *((keyclass_t
*)cursor
);
1127 // TODO: export get_key_gen macro
1128 if (((keyclass
& ~key_class_last
) == 0) != (current_generation
== 0)) {
1129 c
->query_ctx
.found
++;
1132 CFReleaseSafe(blob
);
1136 c
->query_ctx
.found
++;
1137 CFReleaseSafe(blob
);
1140 bool s3dl_dbt_keys_current(SecDbConnectionRef dbt
, uint32_t current_generation
, CFErrorRef
*error
) {
1141 CFErrorRef localError
= NULL
;
1142 struct check_generation_ctx ctx
= { .query_ctx
= { .dbt
= dbt
}, .current_generation
= current_generation
};
1144 const SecDbClass
*classes
[] = {
1151 for (size_t class_ix
= 0; class_ix
< array_size(classes
); ++class_ix
) {
1152 Query
*q
= query_create(classes
[class_ix
], NULL
, &localError
);
1156 ctx
.query_ctx
.q
= q
;
1157 q
->q_limit
= kSecMatchUnlimited
;
1159 bool ok
= s3dl_query(check_generation
, &ctx
, &localError
);
1160 query_destroy(q
, NULL
);
1161 CFReleaseNull(ctx
.query_ctx
.result
);
1163 if (!ok
&& localError
&& (CFErrorGetCode(localError
) == errSecItemNotFound
)) {
1164 CFReleaseNull(localError
);
1167 secerror("Class %@ not up to date", classes
[class_ix
]->name
);
1173 bool s3dl_dbt_update_keys(SecDbConnectionRef dbt
, CFErrorRef
*error
) {
1174 return SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
1175 __block
bool ok
= false;
1176 uint32_t keystore_generation_status
;
1178 /* can we migrate to new class keys right now? */
1179 if (!aks_generation(KEYBAG_DEVICE
, generation_noop
, &keystore_generation_status
) &&
1180 (keystore_generation_status
& generation_change_in_progress
)) {
1182 /* take a lock assertion */
1183 bool operated_while_unlocked
= SecAKSDoWhileUserBagLocked(error
, ^{
1184 CFErrorRef localError
= NULL
;
1185 CFDictionaryRef backup
= SecServerExportKeychainPlist(dbt
,
1186 KEYBAG_DEVICE
, KEYBAG_NONE
, kSecNoItemFilter
, &localError
);
1189 secerror("Ignoring export error: %@ during roll export", localError
);
1190 CFReleaseNull(localError
);
1192 ok
= SecServerImportKeychainInPlist(dbt
, KEYBAG_NONE
,
1193 KEYBAG_DEVICE
, backup
, kSecNoItemFilter
, &localError
);
1195 secerror("Ignoring export error: %@ during roll export", localError
);
1196 CFReleaseNull(localError
);
1201 if (!operated_while_unlocked
)
1204 ok
= SecError(errSecBadReq
, error
, CFSTR("No key roll in progress."));