3 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
26 * SecDbQuery.c - CoreFoundation-based constants and functions for
27 access to Security items (certificates, keys, identities, and
31 #include <securityd/SecDbQuery.h>
33 #include <securityd/SecItemDb.h>
34 #include <securityd/SecItemSchema.h>
35 #include <securityd/SecItemServer.h>
36 #include <securityd/spi.h>
37 #include <Security/SecBasePriv.h>
38 #include <Security/SecCertificateInternal.h>
39 #include <Security/SecItem.h>
40 #include <Security/SecItemPriv.h>
41 #include <Security/SecItemInternal.h>
42 #include <Security/SecAccessControl.h>
43 #include <Security/SecAccessControlPriv.h>
44 #include <CommonCrypto/CommonDigest.h>
45 #include <CommonCrypto/CommonDigestSPI.h>
47 #include <pthread/pthread.h>
50 #include <LocalAuthentication/LAPublicDefines.h>
51 #include <coreauthd_spi.h>
52 #include <libaks_acl_cf_keys.h>
55 /* Upper limit for number of keys in a QUERY dictionary. */
56 #define QUERY_KEY_LIMIT_BASE (128)
58 #define QUERY_KEY_LIMIT (31 + QUERY_KEY_LIMIT_BASE)
60 #define QUERY_KEY_LIMIT QUERY_KEY_LIMIT_BASE
64 static const uint8_t systemKeychainUUID
[] = "\xF6\x23\xAE\x5C\xCC\x81\x4C\xAC\x8A\xD4\xF0\x01\x3F\x31\x35\x11";
67 SecMUSRCopySystemKeychainUUID(void)
69 return CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)systemKeychainUUID
, 16, kCFAllocatorNull
);
73 SecMUSRGetSystemKeychainUUID(void)
75 static dispatch_once_t onceToken
;
76 static CFDataRef systemKeychainData
= NULL
;
77 dispatch_once(&onceToken
, ^{
78 systemKeychainData
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)systemKeychainUUID
, 16, kCFAllocatorNull
);
80 return systemKeychainData
;
84 SecMUSRGetSingleUserKeychainUUID(void)
86 static dispatch_once_t onceToken
;
87 static CFDataRef singleUser
= NULL
;
88 dispatch_once(&onceToken
, ^{
89 singleUser
= CFDataCreateWithBytesNoCopy(NULL
, NULL
, 0, kCFAllocatorNull
);
95 SecMUSRIsSingleUserView(CFDataRef musr
)
97 return CFEqual(musr
, SecMUSRGetSingleUserKeychainUUID());
100 static const uint8_t allKeychainViewsUUID
[16] = "\xC8\x60\x07\xEC\x89\x62\x4D\xAF\x85\x65\x1F\xE6\x0F\x50\x5D\xB7";
103 SecMUSRGetAllViews(void)
105 static dispatch_once_t onceToken
;
106 static CFDataRef allKeychainViewsData
= NULL
;
107 dispatch_once(&onceToken
, ^{
108 allKeychainViewsData
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)allKeychainViewsUUID
, 16, kCFAllocatorNull
);
110 return allKeychainViewsData
;
114 SecMUSRIsViewAllViews(CFDataRef musr
)
116 return CFEqual(musr
, SecMUSRGetAllViews());
122 SecMUSRCreateActiveUserUUID(uid_t uid
)
124 uint8_t uuid
[16] = "\xA7\x5A\x3A\x35\xA5\x57\x4B\x10\xBE\x2E\x83\x94\x7E\x4A\x34\x72";
125 uint32_t num
= htonl(uid
);
126 memcpy(&uuid
[12], &num
, sizeof(num
));
127 return CFDataCreate(NULL
, uuid
, sizeof(uuid
));
131 SecMUSRCreateSyncBubbleUserUUID(uid_t uid
)
133 uint8_t uuid
[16] = "\x82\x1A\xAB\x9F\xA3\xC8\x4E\x11\xAA\x90\x4C\xE8\x9E\xA6\xD7\xEC";
134 uint32_t num
= htonl(uid
);
135 memcpy(&uuid
[12], &num
, sizeof(num
));
136 return CFDataCreate(NULL
, uuid
, sizeof(uuid
));
139 static const uint8_t bothUserAndSystemUUID
[12] = "\x36\xC4\xBE\x2E\x99\x0A\x46\x9A\xAC\x89\x09\xA4";
143 SecMUSRCreateBothUserAndSystemUUID(uid_t uid
)
146 memcpy(uuid
, bothUserAndSystemUUID
, 12);
147 uint32_t num
= htonl(uid
);
148 memcpy(&uuid
[12], &num
, sizeof(num
));
149 return CFDataCreate(NULL
, uuid
, sizeof(uuid
));
153 SecMUSRGetBothUserAndSystemUUID(CFDataRef musr
, uid_t
*uid
)
155 if (CFDataGetLength(musr
) != 16)
157 const uint8_t *uuid
= CFDataGetBytePtr(musr
);
158 if (memcmp(uuid
, bothUserAndSystemUUID
, 12) != 0)
162 memcpy(&num
, &uuid
[12], sizeof(num
));
170 /* Inline accessors to attr and match values in a query. */
171 CFIndex
query_attr_count(const Query
*q
)
173 return q
->q_attr_end
;
176 Pair
query_attr_at(const Query
*q
, CFIndex ix
)
178 return q
->q_pairs
[ix
];
181 CFIndex
query_match_count(const Query
*q
)
183 return q
->q_match_end
- q
->q_match_begin
;
186 __unused
static inline Pair
query_match_at(const Query
*q
, CFIndex ix
)
188 return q
->q_pairs
[q
->q_match_begin
+ ix
];
191 /* Private routines used to parse a query. */
193 const SecDbClass
*kc_class_with_name(CFStringRef name
) {
194 if (isString(name
)) {
196 // TODO Iterate kc_db_classes and look for name == class->name.
197 // Or get clever and switch on first letter of class name and compare to verify
198 static const void *kc_db_classes
[] = {
206 if (CFEqual(name
, kSecClassGenericPassword
))
208 else if (CFEqual(name
, kSecClassInternetPassword
))
210 else if (CFEqual(name
, kSecClassCertificate
))
212 else if (CFEqual(name
, kSecClassKey
))
214 else if (CFEqual(name
, kSecClassIdentity
))
215 return &identity_class
;
220 static void query_set_access_control(Query
*q
, SecAccessControlRef access_control
) {
221 if (q
->q_access_control
) {
222 if (!CFEqual(q
->q_access_control
, access_control
)) {
223 SecError(errSecItemIllegalQuery
, &q
->q_error
, CFSTR("conflicting kSecAccess and kSecAccessControl attributes"));
226 /* Store access control virtual attribute. */
227 q
->q_access_control
= (SecAccessControlRef
)CFRetain(access_control
);
229 /* Also set legacy access attribute. */
230 CFTypeRef protection
= SecAccessControlGetProtection(q
->q_access_control
);
232 SecError(errSecParam
, &q
->q_error
, CFSTR("kSecAccessControl missing protection"));
235 CFDictionarySetValue(q
->q_item
, kSecAttrAccessible
, protection
);
239 /* AUDIT[securityd](done):
240 key (ok) is a caller provided, string or number of length 4.
241 value (ok) is a caller provided, non NULL CFTypeRef.
243 void query_add_attribute_with_desc(const SecDbAttr
*desc
, const void *value
, Query
*q
)
245 if (CFEqual(desc
->name
, kSecAttrSynchronizable
)) {
247 if (CFEqual(value
, kSecAttrSynchronizableAny
))
248 return; /* skip the attribute so it isn't part of the search */
251 CFTypeRef attr
= NULL
;
252 switch (desc
->kind
) {
254 attr
= copyData(value
);
257 case kSecDbAccessControlAttr
:
258 attr
= copyBlob(value
);
261 case kSecDbCreationDateAttr
:
262 case kSecDbModificationDateAttr
:
263 attr
= copyDate(value
);
265 case kSecDbNumberAttr
:
268 attr
= copyNumber(value
);
270 case kSecDbAccessAttr
:
271 case kSecDbStringAttr
:
272 attr
= copyString(value
);
275 attr
= copySHA1(value
);
277 case kSecDbRowIdAttr
:
278 case kSecDbPrimaryKeyAttr
:
279 case kSecDbEncryptedDataAttr
:
280 case kSecDbUTombAttr
:
283 attr
= copyUUID(value
);
288 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("attribute %@: value: %@ failed to convert"), desc
->name
, value
);
292 /* Store plaintext attr data in q_item unless it's a kSecDbSHA1Attr. */
293 if (q
->q_item
&& desc
->kind
!= kSecDbSHA1Attr
) {
294 CFDictionarySetValue(q
->q_item
, desc
->name
, attr
);
297 /* Convert attr to (sha1) digest if requested. */
298 if (desc
->flags
& kSecDbSHA1ValueInFlag
) {
299 CFDataRef data
= copyData(attr
);
302 SecError(errSecInternal
, &q
->q_error
, CFSTR("failed to get attribute %@ data"), desc
->name
);
306 CFMutableDataRef digest
= CFDataCreateMutable(0, CC_SHA1_DIGEST_LENGTH
);
307 CFDataSetLength(digest
, CC_SHA1_DIGEST_LENGTH
);
308 /* 64 bits cast: worst case is we generate the wrong hash */
309 assert((unsigned long)CFDataGetLength(data
)<UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
310 CCDigest(kCCDigestSHA1
, CFDataGetBytePtr(data
), (CC_LONG
)CFDataGetLength(data
),
311 CFDataGetMutableBytePtr(digest
));
316 if (desc
->kind
!= kSecDbAccessControlAttr
) {
317 /* Record the new attr key, value in q_pairs. */
318 q
->q_pairs
[q
->q_attr_end
].key
= desc
->name
;
319 q
->q_pairs
[q
->q_attr_end
++].value
= attr
;
325 void query_add_attribute(const void *key
, const void *value
, Query
*q
)
327 const SecDbAttr
*desc
= SecDbAttrWithKey(q
->q_class
, key
, &q
->q_error
);
329 query_add_attribute_with_desc(desc
, value
, q
);
331 if (desc
->kind
== kSecDbAccessControlAttr
) {
332 CFDataRef attr
= (CFDataRef
)CFDictionaryGetValue(q
->q_item
, desc
->name
);
334 SecAccessControlRef access_control
= SecAccessControlCreateFromData(kCFAllocatorDefault
, attr
, &q
->q_error
);
335 if (access_control
) {
336 query_set_access_control(q
, access_control
);
337 CFRelease(access_control
);
342 if (desc
->kind
== kSecDbAccessAttr
) {
343 SecAccessControlRef access_control
= SecAccessControlCreate(kCFAllocatorDefault
, &q
->q_error
);
344 if (access_control
) {
345 CFStringRef attr
= (CFStringRef
)CFDictionaryGetValue(q
->q_item
, desc
->name
);
347 if (SecAccessControlSetProtection(access_control
, attr
, &q
->q_error
))
348 query_set_access_control(q
, access_control
);
350 CFRelease(access_control
);
356 void query_add_or_attribute(const void *key
, const void *value
, Query
*q
)
358 const SecDbAttr
*desc
= SecDbAttrWithKey(q
->q_class
, key
, &q
->q_error
);
360 CFTypeRef oldValue
= CFDictionaryGetValue(q
->q_item
, desc
->name
);
361 CFMutableArrayRef array
= NULL
;
363 if (isArray(oldValue
)) {
364 array
= (CFMutableArrayRef
)CFRetain(oldValue
);
366 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
367 CFArrayAppendValue(array
, oldValue
);
369 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
371 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
374 query_add_attribute_with_desc(desc
, value
, q
);
375 CFTypeRef newValue
= CFDictionaryGetValue(q
->q_item
, desc
->name
);
376 CFArrayAppendValue(array
, newValue
);
377 CFDictionarySetValue(q
->q_item
, desc
->name
, array
);
383 void query_add_not_attribute(const void *key
, const void *value
, Query
*q
)
385 const SecDbAttr
*desc
= SecDbAttrWithKey(q
->q_class
, key
, &q
->q_error
);
387 CFTypeRef oldValue
= CFDictionaryGetValue(q
->q_item
, desc
->name
);
388 CFMutableArrayRef array
= NULL
;
390 if (isArray(oldValue
)) {
391 array
= (CFMutableArrayRef
)CFRetain(oldValue
);
393 // This should never run, as we shouldn't be turning a attr = value into a attr not in (value, value2)
394 secerror("negating %@ = %@ in query", desc
->name
, oldValue
);
395 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
396 CFArrayAppendValue(array
, kCFNull
);
397 CFArrayAppendValue(array
, oldValue
);
399 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
401 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
402 CFArrayAppendValue(array
, kCFNull
);
405 query_add_attribute_with_desc(desc
, value
, q
);
406 CFTypeRef newValue
= CFDictionaryGetValue(q
->q_item
, desc
->name
);
407 CFArrayAppendValue(array
, newValue
);
408 CFDictionarySetValue(q
->q_item
, desc
->name
, array
);
415 /* AUDIT[securityd](done):
416 key (ok) is a caller provided, string starting with 'm'.
417 value (ok) is a caller provided, non NULL CFTypeRef.
419 static void query_add_match(const void *key
, const void *value
, Query
*q
)
421 /* Record the match key, value in q_pairs. */
422 --(q
->q_match_begin
);
423 q
->q_pairs
[q
->q_match_begin
].key
= key
;
424 q
->q_pairs
[q
->q_match_begin
].value
= value
;
426 if (CFEqual(kSecMatchLimit
, key
)) {
427 /* Figure out what the value for kSecMatchLimit is if specified. */
428 if (CFGetTypeID(value
) == CFNumberGetTypeID()) {
429 if (!CFNumberGetValue(value
, kCFNumberCFIndexType
, &q
->q_limit
))
430 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("failed to convert match limit %@ to CFIndex"), value
);
431 } else if (CFEqual(kSecMatchLimitAll
, value
)) {
432 q
->q_limit
= kSecMatchUnlimited
;
433 } else if (CFEqual(kSecMatchLimitOne
, value
)) {
436 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("unsupported match limit %@"), value
);
438 } else if (CFEqual(kSecMatchIssuers
, key
) &&
439 (CFGetTypeID(value
) == CFArrayGetTypeID()))
441 CFMutableArrayRef canonical_issuers
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
442 if (canonical_issuers
) {
443 CFIndex i
, count
= CFArrayGetCount(value
);
444 for (i
= 0; i
< count
; i
++) {
445 CFTypeRef issuer_data
= CFArrayGetValueAtIndex(value
, i
);
446 CFDataRef issuer_canonical
= NULL
;
447 if (CFDataGetTypeID() == CFGetTypeID(issuer_data
))
448 issuer_canonical
= SecDistinguishedNameCopyNormalizedContent((CFDataRef
)issuer_data
);
449 if (issuer_canonical
) {
450 CFArrayAppendValue(canonical_issuers
, issuer_canonical
);
451 CFRelease(issuer_canonical
);
455 if (CFArrayGetCount(canonical_issuers
) > 0) {
456 q
->q_match_issuer
= canonical_issuers
;
458 CFRelease(canonical_issuers
);
463 static bool query_set_class(Query
*q
, CFStringRef c_name
, CFErrorRef
*error
) {
464 const SecDbClass
*value
;
465 if (c_name
&& CFGetTypeID(c_name
) == CFStringGetTypeID() &&
466 (value
= kc_class_with_name(c_name
)) &&
467 (q
->q_class
== 0 || q
->q_class
== value
)) {
472 if (error
&& !*error
)
473 SecError((c_name
? errSecNoSuchClass
: errSecItemClassMissing
), error
, CFSTR("can find class named: %@"), c_name
);
479 static const SecDbClass
*query_get_class(CFDictionaryRef query
, CFErrorRef
*error
) {
480 CFStringRef c_name
= NULL
;
481 const void *value
= CFDictionaryGetValue(query
, kSecClass
);
482 if (isString(value
)) {
485 value
= CFDictionaryGetValue(query
, kSecValuePersistentRef
);
487 CFDataRef pref
= value
;
488 _SecItemParsePersistentRef(pref
, &c_name
, 0);
492 if (c_name
&& (value
= kc_class_with_name(c_name
))) {
496 SecError(errSecNoSuchClass
, error
, CFSTR("can't find class named: %@"), c_name
);
498 SecError(errSecItemClassMissing
, error
, CFSTR("query missing class name"));
503 /* AUDIT[securityd](done):
504 key (ok) is a caller provided, string starting with 'c'.
505 value (ok) is a caller provided, non NULL CFTypeRef.
507 static void query_add_class(const void *key
, const void *value
, Query
*q
)
509 if (CFEqual(key
, kSecClass
)) {
510 query_set_class(q
, value
, &q
->q_error
);
512 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_class: key %@ is not %@"), key
, kSecClass
);
516 /* AUDIT[securityd](done):
517 key (ok) is a caller provided, string starting with 'r'.
518 value (ok) is a caller provided, non NULL CFTypeRef.
520 static void query_add_return(const void *key
, const void *value
, Query
*q
)
523 if (CFGetTypeID(value
) != CFBooleanGetTypeID()) {
524 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_return: value %@ is not CFBoolean"), value
);
528 int set_it
= CFEqual(value
, kCFBooleanTrue
);
530 if (CFEqual(key
, kSecReturnData
))
531 mask
= kSecReturnDataMask
;
532 else if (CFEqual(key
, kSecReturnAttributes
))
533 mask
= kSecReturnAttributesMask
;
534 else if (CFEqual(key
, kSecReturnRef
))
535 mask
= kSecReturnRefMask
;
536 else if (CFEqual(key
, kSecReturnPersistentRef
))
537 mask
= kSecReturnPersistentRefMask
;
539 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_return: unknown key %@"), key
);
543 if ((q
->q_return_type
& mask
) && !set_it
) {
544 /* Clear out this bit (it's set so xor with the mask will clear it). */
545 q
->q_return_type
^= mask
;
546 } else if (!(q
->q_return_type
& mask
) && set_it
) {
548 q
->q_return_type
|= mask
;
552 /* AUDIT[securityd](done):
553 key (ok) is a caller provided, string starting with 'u'.
554 value (ok since q_use_item_list is unused) is a caller provided, non
557 static void query_add_use(const void *key
, const void *value
, Query
*q
)
559 if (CFEqual(key
, kSecUseItemList
)) {
560 /* TODO: Add sanity checking when we start using this. */
561 q
->q_use_item_list
= value
;
562 } else if (CFEqual(key
, kSecUseTombstones
)) {
563 if (CFGetTypeID(value
) == CFBooleanGetTypeID()) {
564 q
->q_use_tomb
= value
;
565 } else if (CFGetTypeID(value
) == CFNumberGetTypeID()) {
566 q
->q_use_tomb
= CFBooleanGetValue(value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
567 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
568 q
->q_use_tomb
= CFStringGetIntValue(value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
570 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is neither CFBoolean nor CFNumber"), value
, key
);
573 } else if (CFEqual(key
, kSecUseCredentialReference
)) {
575 CFRetainAssign(q
->q_use_cred_handle
, value
);
577 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is not CFData"), value
, key
);
580 } else if (CFEqual(key
, kSecUseAuthenticationUI
)) {
581 if (isString(value
)) {
582 q
->q_skip_acl_items
= CFEqualSafe(kSecUseAuthenticationUISkip
, value
);
584 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is not CFString"), value
, key
);
588 } else if (CFEqual(key
, kSecUseSystemKeychain
)) {
589 #if TARGET_OS_EMBEDDED
590 q
->q_keybag
= KEYBAG_DEVICE
;
592 q
->q_system_keychain
= true;
593 } else if (CFEqual(key
, kSecUseSyncBubbleKeychain
)) {
594 if (isNumber(value
) && CFNumberGetValue(value
, kCFNumberSInt32Type
, &q
->q_sync_bubble
) && q
->q_sync_bubble
> 0) {
595 #if TARGET_OS_EMBEDDED
596 q
->q_keybag
= KEYBAG_DEVICE
;
599 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is not valid uid"), value
, key
);
604 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_use: unknown key %@"), key
);
609 static void query_set_data(const void *value
, Query
*q
) {
610 if (!isData(value
)) {
611 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("set_data: value %@ is not type data"), value
);
615 CFDictionarySetValue(q
->q_item
, kSecValueData
, value
);
619 /* AUDIT[securityd](done):
620 key (ok) is a caller provided, string starting with 'u'.
621 value (ok) is a caller provided, non NULL CFTypeRef.
623 static void query_add_value(const void *key
, const void *value
, Query
*q
)
625 if (CFEqual(key
, kSecValueData
)) {
626 query_set_data(value
, q
);
628 } else if (CFEqual(key
, kSecValueRef
)) {
630 /* TODO: Add value type sanity checking. */
632 } else if (CFEqual(key
, kSecValuePersistentRef
)) {
634 if (_SecItemParsePersistentRef(value
, &c_name
, &q
->q_row_id
))
635 query_set_class(q
, c_name
, &q
->q_error
);
637 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_value: value %@ is not a valid persitent ref"), value
);
639 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_value: unknown key %@"), key
);
644 /* AUDIT[securityd](done):
645 key (ok) is a caller provided, unchecked.
646 value (ok) is a caller provided, unchecked.
648 static void query_update_applier(const void *key
, const void *value
,
651 Query
*q
= (Query
*)context
;
652 /* If something went wrong there is no point processing any more args. */
656 /* Make sure we have a string key. */
657 if (!isString(key
)) {
658 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("update_applier: unknown key type %@"), key
);
663 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("update_applier: key %@ has NULL value"), key
);
667 if (CFEqual(key
, CFSTR("musr"))) {
668 secnotice("item", "update_applier: refusing to update musr");
672 if (CFEqual(key
, kSecValueData
)) {
673 query_set_data(value
, q
);
675 query_add_attribute(key
, value
, q
);
679 /* AUDIT[securityd](done):
680 key (ok) is a caller provided, unchecked.
681 value (ok) is a caller provided, unchecked.
683 static void query_applier(const void *key
, const void *value
, void *context
)
685 Query
*q
= (Query
*)context
;
686 /* If something went wrong there is no point processing any more args. */
690 /* Make sure we have a key. */
692 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("applier: NULL key"));
696 /* Make sure we have a value. */
698 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("applier: key %@ has NULL value"), key
);
702 /* Figure out what type of key we are dealing with. */
703 CFTypeID key_id
= CFGetTypeID(key
);
704 if (key_id
== CFStringGetTypeID()) {
705 CFIndex key_len
= CFStringGetLength(key
);
706 /* String keys can be different things. The subtype is determined by:
707 length 4 strings are all attributes. Otherwise the first char
709 c: class must be kSecClass
710 m: match like kSecMatchPolicy
711 r: return like kSecReturnData
717 query_add_attribute(key
, value
, q
);
718 } else if (key_len
> 1) {
719 UniChar k_first_char
= CFStringGetCharacterAtIndex(key
, 0);
720 switch (k_first_char
)
722 case 'c': /* class */
723 query_add_class(key
, value
, q
);
725 case 'm': /* match */
726 query_add_match(key
, value
, q
);
728 case 'r': /* return */
729 query_add_return(key
, value
, q
);
732 query_add_use(key
, value
, q
);
734 case 'v': /* value */
735 query_add_value(key
, value
, q
);
738 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("applier: key %@ invalid"), key
);
742 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("applier: key %@ invalid length"), key
);
744 } else if (key_id
== CFNumberGetTypeID()) {
745 /* Numeric keys are always (extended) attributes. */
746 /* TODO: Why is this here? query_add_attribute() doesn't take numbers. */
747 query_add_attribute(key
, value
, q
);
749 /* We only support string and number type keys. */
750 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("applier: key %@ neither string nor number"), key
);
754 static CFStringRef
query_infer_keyclass(Query
*q
, CFStringRef agrp
) {
755 /* apsd and lockdown are always dku. */
756 if (CFEqual(agrp
, CFSTR("com.apple.apsd"))
757 || CFEqual(agrp
, CFSTR("lockdown-identities"))) {
758 return kSecAttrAccessibleAlwaysThisDeviceOnly
;
760 /* All other certs or in the apple agrp is dk. */
761 if (q
->q_class
== &cert_class
) {
762 /* third party certs are always dk. */
763 return kSecAttrAccessibleAlways
;
765 /* The rest defaults to ak. */
766 return kSecAttrAccessibleWhenUnlocked
;
769 void query_ensure_access_control(Query
*q
, CFStringRef agrp
) {
770 if (q
->q_access_control
== 0) {
771 CFStringRef accessible
= query_infer_keyclass(q
, agrp
);
772 query_add_attribute(kSecAttrAccessible
, accessible
, q
);
776 bool query_error(Query
*q
, CFErrorRef
*error
) {
777 CFErrorRef tmp
= q
->q_error
;
779 return SecErrorPropagate(tmp
, error
);
782 bool query_destroy(Query
*q
, CFErrorRef
*error
) {
783 bool ok
= query_error(q
, error
);
784 CFIndex ix
, attr_count
= query_attr_count(q
);
785 for (ix
= 0; ix
< attr_count
; ++ix
) {
786 CFReleaseSafe(query_attr_at(q
, ix
).value
);
788 CFReleaseSafe(q
->q_item
);
789 CFReleaseSafe(q
->q_musrView
);
790 CFReleaseSafe(q
->q_primary_key_digest
);
791 CFReleaseSafe(q
->q_match_issuer
);
792 CFReleaseSafe(q
->q_access_control
);
793 CFReleaseSafe(q
->q_use_cred_handle
);
794 CFReleaseSafe(q
->q_caller_access_groups
);
800 bool query_notify_and_destroy(Query
*q
, bool ok
, CFErrorRef
*error
) {
801 if (ok
&& !q
->q_error
&& (q
->q_sync_changed
|| (q
->q_changed
&& !SecMUSRIsSingleUserView(q
->q_musrView
)))) {
802 SecKeychainChanged(true);
804 return query_destroy(q
, error
) && ok
;
807 /* Allocate and initialize a Query object for query. */
808 Query
*query_create(const SecDbClass
*qclass
,
810 CFDictionaryRef query
,
814 if (error
&& !*error
)
815 SecError(errSecItemClassMissing
, error
, CFSTR("Missing class"));
820 musr
= SecMUSRGetSingleUserKeychainUUID();
822 /* Number of pairs we need is the number of attributes in this class
823 plus the number of keys in the dictionary, minus one for each key in
824 the dictionary that is a regular attribute. */
825 CFIndex key_count
= SecDbClassAttrCount(qclass
);
826 if (key_count
== 0) {
827 // Identities claim to have 0 attributes, but they really support any keys or cert attribute.
828 key_count
= SecDbClassAttrCount(&cert_class
) + SecDbClassAttrCount(&keys_class
);
832 key_count
+= CFDictionaryGetCount(query
);
833 SecDbForEachAttr(qclass
, attr
) {
834 if (CFDictionaryContainsKey(query
, attr
->name
))
839 if (key_count
> QUERY_KEY_LIMIT
) {
840 if (error
&& !*error
)
842 secerror("key_count: %ld, QUERY_KEY_LIMIT: %d", (long)key_count
, QUERY_KEY_LIMIT
);
843 SecError(errSecItemIllegalQuery
, error
, CFSTR("Past query key limit"));
848 Query
*q
= calloc(1, sizeof(Query
) + sizeof(Pair
) * key_count
);
850 if (error
&& !*error
)
851 SecError(errSecAllocate
, error
, CFSTR("Out of memory"));
855 q
->q_musrView
= (CFDataRef
)CFRetain(musr
);
856 q
->q_keybag
= KEYBAG_DEVICE
;
858 q
->q_match_begin
= q
->q_match_end
= key_count
;
859 q
->q_item
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
864 /* Parse query for a Query object q. */
865 static bool query_parse_with_applier(Query
*q
, CFDictionaryRef query
,
866 CFDictionaryApplierFunction applier
,
868 CFDictionaryApplyFunction(query
, applier
, q
);
869 return query_error(q
, error
);
872 /* Parse query for a Query object q. */
873 static bool query_parse(Query
*q
, CFDictionaryRef query
,
875 return query_parse_with_applier(q
, query
, query_applier
, error
);
878 /* Parse query for a Query object q. */
879 bool query_update_parse(Query
*q
, CFDictionaryRef update
,
881 return query_parse_with_applier(q
, update
, query_update_applier
, error
);
884 Query
*query_create_with_limit(CFDictionaryRef query
, CFDataRef musr
, CFIndex limit
, CFErrorRef
*error
) {
886 q
= query_create(query_get_class(query
, error
), musr
, query
, error
);
889 if (!query_parse(q
, query
, error
)) {
890 query_destroy(q
, error
);
893 if (!q
->q_sync
&& !q
->q_row_id
) {
894 /* query did not specify a kSecAttrSynchronizable attribute,
895 * and did not contain a persistent reference. */
896 query_add_attribute(kSecAttrSynchronizable
, kCFBooleanFalse
, q
);
904 query_set_caller_access_groups(Query
*q
, CFArrayRef caller_access_groups
) {
905 CFRetainAssign(q
->q_caller_access_groups
, caller_access_groups
);