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 <Security/SecPolicyInternal.h>
45 #include <Security/SecuritydXPC.h>
46 #include <CommonCrypto/CommonDigest.h>
47 #include <CommonCrypto/CommonDigestSPI.h>
49 #include <pthread/pthread.h>
52 #include <LocalAuthentication/LAPublicDefines.h>
53 #include <coreauthd_spi.h>
54 #include <libaks_acl_cf_keys.h>
57 /* Upper limit for number of keys in a QUERY dictionary. */
58 #define QUERY_KEY_LIMIT_BASE (128)
60 #define QUERY_KEY_LIMIT (31 + QUERY_KEY_LIMIT_BASE)
62 #define QUERY_KEY_LIMIT QUERY_KEY_LIMIT_BASE
66 static const uint8_t systemKeychainUUID
[] = "\xF6\x23\xAE\x5C\xCC\x81\x4C\xAC\x8A\xD4\xF0\x01\x3F\x31\x35\x11";
69 SecMUSRCopySystemKeychainUUID(void)
71 return CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)systemKeychainUUID
, 16, kCFAllocatorNull
);
75 SecMUSRGetSystemKeychainUUID(void)
77 static dispatch_once_t onceToken
;
78 static CFDataRef systemKeychainData
= NULL
;
79 dispatch_once(&onceToken
, ^{
80 systemKeychainData
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)systemKeychainUUID
, 16, kCFAllocatorNull
);
82 return systemKeychainData
;
86 SecMUSRGetSingleUserKeychainUUID(void)
88 static dispatch_once_t onceToken
;
89 static CFDataRef singleUser
= NULL
;
90 dispatch_once(&onceToken
, ^{
91 singleUser
= CFDataCreateWithBytesNoCopy(NULL
, NULL
, 0, kCFAllocatorNull
);
97 SecMUSRIsSingleUserView(CFDataRef musr
)
99 return CFEqual(musr
, SecMUSRGetSingleUserKeychainUUID());
102 static const uint8_t allKeychainViewsUUID
[16] = "\xC8\x60\x07\xEC\x89\x62\x4D\xAF\x85\x65\x1F\xE6\x0F\x50\x5D\xB7";
105 SecMUSRGetAllViews(void)
107 static dispatch_once_t onceToken
;
108 static CFDataRef allKeychainViewsData
= NULL
;
109 dispatch_once(&onceToken
, ^{
110 allKeychainViewsData
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)allKeychainViewsUUID
, 16, kCFAllocatorNull
);
112 return allKeychainViewsData
;
116 SecMUSRIsViewAllViews(CFDataRef musr
)
118 return CFEqual(musr
, SecMUSRGetAllViews());
124 SecMUSRCreateActiveUserUUID(uid_t uid
)
126 uint8_t uuid
[16] = "\xA7\x5A\x3A\x35\xA5\x57\x4B\x10\xBE\x2E\x83\x94\x7E\x4A\x34\x72";
127 uint32_t num
= htonl(uid
);
128 memcpy(&uuid
[12], &num
, sizeof(num
));
129 return CFDataCreate(NULL
, uuid
, sizeof(uuid
));
133 SecMUSRCreateSyncBubbleUserUUID(uid_t uid
)
135 uint8_t uuid
[16] = "\x82\x1A\xAB\x9F\xA3\xC8\x4E\x11\xAA\x90\x4C\xE8\x9E\xA6\xD7\xEC";
136 uint32_t num
= htonl(uid
);
137 memcpy(&uuid
[12], &num
, sizeof(num
));
138 return CFDataCreate(NULL
, uuid
, sizeof(uuid
));
141 static const uint8_t bothUserAndSystemUUID
[12] = "\x36\xC4\xBE\x2E\x99\x0A\x46\x9A\xAC\x89\x09\xA4";
145 SecMUSRCreateBothUserAndSystemUUID(uid_t uid
)
148 memcpy(uuid
, bothUserAndSystemUUID
, 12);
149 uint32_t num
= htonl(uid
);
150 memcpy(&uuid
[12], &num
, sizeof(num
));
151 return CFDataCreate(NULL
, uuid
, sizeof(uuid
));
155 SecMUSRGetBothUserAndSystemUUID(CFDataRef musr
, uid_t
*uid
)
157 if (CFDataGetLength(musr
) != 16)
159 const uint8_t *uuid
= CFDataGetBytePtr(musr
);
160 if (memcmp(uuid
, bothUserAndSystemUUID
, 12) != 0)
164 memcpy(&num
, &uuid
[12], sizeof(num
));
172 /* Inline accessors to attr and match values in a query. */
173 CFIndex
query_attr_count(const Query
*q
)
175 return q
->q_attr_end
;
178 Pair
query_attr_at(const Query
*q
, CFIndex ix
)
180 return q
->q_pairs
[ix
];
183 CFIndex
query_match_count(const Query
*q
)
185 return q
->q_match_end
- q
->q_match_begin
;
188 __unused
static inline Pair
query_match_at(const Query
*q
, CFIndex ix
)
190 return q
->q_pairs
[q
->q_match_begin
+ ix
];
193 /* Private routines used to parse a query. */
195 const SecDbClass
*kc_class_with_name(CFStringRef name
) {
196 if (isString(name
)) {
198 // TODO Iterate kc_db_classes and look for name == class->name.
199 // Or get clever and switch on first letter of class name and compare to verify
200 static const void *kc_db_classes
[] = {
208 if (CFEqual(name
, kSecClassGenericPassword
))
210 else if (CFEqual(name
, kSecClassInternetPassword
))
212 else if (CFEqual(name
, kSecClassCertificate
))
214 else if (CFEqual(name
, kSecClassKey
))
216 else if (CFEqual(name
, kSecClassIdentity
))
217 return &identity_class
;
222 static void query_set_access_control(Query
*q
, SecAccessControlRef access_control
) {
223 if (q
->q_access_control
) {
224 if (!CFEqual(q
->q_access_control
, access_control
)) {
225 SecError(errSecItemIllegalQuery
, &q
->q_error
, CFSTR("conflicting kSecAccess and kSecAccessControl attributes"));
228 /* Store access control virtual attribute. */
229 q
->q_access_control
= (SecAccessControlRef
)CFRetain(access_control
);
231 /* Also set legacy access attribute. */
232 CFTypeRef protection
= SecAccessControlGetProtection(q
->q_access_control
);
234 SecError(errSecParam
, &q
->q_error
, CFSTR("kSecAccessControl missing protection"));
237 CFDictionarySetValue(q
->q_item
, kSecAttrAccessible
, protection
);
241 /* AUDIT[securityd](done):
242 key (ok) is a caller provided, string or number of length 4.
243 value (ok) is a caller provided, non NULL CFTypeRef.
245 void query_add_attribute_with_desc(const SecDbAttr
*desc
, const void *value
, Query
*q
)
247 if (CFEqual(desc
->name
, kSecAttrSynchronizable
)) {
249 if (CFEqual(value
, kSecAttrSynchronizableAny
))
250 return; /* skip the attribute so it isn't part of the search */
253 CFTypeRef attr
= NULL
;
254 switch (desc
->kind
) {
256 attr
= copyData(value
);
259 case kSecDbAccessControlAttr
:
260 attr
= copyBlob(value
);
263 case kSecDbCreationDateAttr
:
264 case kSecDbModificationDateAttr
:
265 attr
= copyDate(value
);
267 case kSecDbNumberAttr
:
270 attr
= copyNumber(value
);
272 case kSecDbAccessAttr
:
273 case kSecDbStringAttr
:
274 attr
= copyString(value
);
277 attr
= copySHA1(value
);
279 case kSecDbRowIdAttr
:
280 case kSecDbPrimaryKeyAttr
:
281 case kSecDbEncryptedDataAttr
:
282 case kSecDbUTombAttr
:
285 attr
= copyUUID(value
);
290 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("attribute %@: value: %@ failed to convert"), desc
->name
, value
);
294 /* Store plaintext attr data in q_item unless it's a kSecDbSHA1Attr. */
295 if (q
->q_item
&& desc
->kind
!= kSecDbSHA1Attr
) {
296 CFDictionarySetValue(q
->q_item
, desc
->name
, attr
);
299 /* Convert attr to (sha1) digest if requested. */
300 if (desc
->flags
& kSecDbSHA1ValueInFlag
) {
301 CFDataRef data
= copyData(attr
);
304 SecError(errSecInternal
, &q
->q_error
, CFSTR("failed to get attribute %@ data"), desc
->name
);
308 CFMutableDataRef digest
= CFDataCreateMutable(0, CC_SHA1_DIGEST_LENGTH
);
309 CFDataSetLength(digest
, CC_SHA1_DIGEST_LENGTH
);
310 /* 64 bits cast: worst case is we generate the wrong hash */
311 assert((unsigned long)CFDataGetLength(data
)<UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
312 CCDigest(kCCDigestSHA1
, CFDataGetBytePtr(data
), (CC_LONG
)CFDataGetLength(data
),
313 CFDataGetMutableBytePtr(digest
));
318 if (desc
->kind
!= kSecDbAccessControlAttr
) {
319 /* Record the new attr key, value in q_pairs. */
320 if (q
->q_attr_end
+ 1 < q
->q_pairs_count
) {
321 q
->q_pairs
[q
->q_attr_end
].key
= desc
->name
;
322 q
->q_pairs
[q
->q_attr_end
++].value
= attr
;
324 SecError(errSecInternal
, &q
->q_error
, CFSTR("q_pairs overflow"));
332 void query_add_attribute(const void *key
, const void *value
, Query
*q
)
334 const SecDbAttr
*desc
= SecDbAttrWithKey(q
->q_class
, key
, &q
->q_error
);
336 query_add_attribute_with_desc(desc
, value
, q
);
338 if (desc
->kind
== kSecDbAccessControlAttr
) {
339 CFDataRef attr
= (CFDataRef
)CFDictionaryGetValue(q
->q_item
, desc
->name
);
341 SecAccessControlRef access_control
= SecAccessControlCreateFromData(kCFAllocatorDefault
, attr
, &q
->q_error
);
342 if (access_control
) {
343 query_set_access_control(q
, access_control
);
344 CFRelease(access_control
);
349 if (desc
->kind
== kSecDbAccessAttr
) {
350 SecAccessControlRef access_control
= SecAccessControlCreate(kCFAllocatorDefault
, &q
->q_error
);
351 if (access_control
) {
352 CFStringRef attr
= (CFStringRef
)CFDictionaryGetValue(q
->q_item
, desc
->name
);
354 if (SecAccessControlSetProtection(access_control
, attr
, &q
->q_error
))
355 query_set_access_control(q
, access_control
);
357 CFRelease(access_control
);
363 void query_add_or_attribute(const void *key
, const void *value
, Query
*q
)
365 const SecDbAttr
*desc
= SecDbAttrWithKey(q
->q_class
, key
, &q
->q_error
);
367 CFTypeRef oldValue
= CFDictionaryGetValue(q
->q_item
, desc
->name
);
368 CFMutableArrayRef array
= NULL
;
370 if (isArray(oldValue
)) {
371 array
= (CFMutableArrayRef
)CFRetain(oldValue
);
373 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
374 CFArrayAppendValue(array
, oldValue
);
376 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
378 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
381 query_add_attribute_with_desc(desc
, value
, q
);
382 CFTypeRef newValue
= CFDictionaryGetValue(q
->q_item
, desc
->name
);
383 CFArrayAppendValue(array
, newValue
);
384 CFDictionarySetValue(q
->q_item
, desc
->name
, array
);
390 void query_add_not_attribute(const void *key
, const void *value
, Query
*q
)
392 const SecDbAttr
*desc
= SecDbAttrWithKey(q
->q_class
, key
, &q
->q_error
);
394 CFTypeRef oldValue
= CFDictionaryGetValue(q
->q_item
, desc
->name
);
395 CFMutableArrayRef array
= NULL
;
397 if (isArray(oldValue
)) {
398 array
= (CFMutableArrayRef
)CFRetain(oldValue
);
400 // This should never run, as we shouldn't be turning a attr = value into a attr not in (value, value2)
401 secerror("negating %@ = %@ in query", desc
->name
, oldValue
);
402 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
403 CFArrayAppendValue(array
, kCFNull
);
404 CFArrayAppendValue(array
, oldValue
);
406 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
408 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
409 CFArrayAppendValue(array
, kCFNull
);
412 query_add_attribute_with_desc(desc
, value
, q
);
413 CFTypeRef newValue
= CFDictionaryGetValue(q
->q_item
, desc
->name
);
414 CFArrayAppendValue(array
, newValue
);
415 CFDictionarySetValue(q
->q_item
, desc
->name
, array
);
422 /* AUDIT[securityd](done):
423 key (ok) is a caller provided, string starting with 'm'.
424 value (ok) is a caller provided, non NULL CFTypeRef.
426 static void query_add_match(const void *key
, const void *value
, Query
*q
)
428 /* Record the match key, value in q_pairs. */
429 --(q
->q_match_begin
);
430 q
->q_pairs
[q
->q_match_begin
].key
= key
;
431 q
->q_pairs
[q
->q_match_begin
].value
= value
;
433 if (CFEqual(kSecMatchLimit
, key
)) {
434 /* Figure out what the value for kSecMatchLimit is if specified. */
435 if (CFGetTypeID(value
) == CFNumberGetTypeID()) {
436 if (!CFNumberGetValue(value
, kCFNumberCFIndexType
, &q
->q_limit
))
437 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("failed to convert match limit %@ to CFIndex"), value
);
438 } else if (CFEqual(kSecMatchLimitAll
, value
)) {
439 q
->q_limit
= kSecMatchUnlimited
;
440 } else if (CFEqual(kSecMatchLimitOne
, value
)) {
443 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("unsupported match limit %@"), value
);
445 } else if (CFEqual(kSecMatchIssuers
, key
) &&
446 (CFGetTypeID(value
) == CFArrayGetTypeID()))
448 CFMutableArrayRef canonical_issuers
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
449 if (canonical_issuers
) {
450 CFIndex i
, count
= CFArrayGetCount(value
);
451 for (i
= 0; i
< count
; i
++) {
452 CFTypeRef issuer_data
= CFArrayGetValueAtIndex(value
, i
);
453 CFDataRef issuer_canonical
= NULL
;
454 if (CFDataGetTypeID() == CFGetTypeID(issuer_data
))
455 issuer_canonical
= SecDistinguishedNameCopyNormalizedContent((CFDataRef
)issuer_data
);
456 if (issuer_canonical
) {
457 CFArrayAppendValue(canonical_issuers
, issuer_canonical
);
458 CFRelease(issuer_canonical
);
462 if (CFArrayGetCount(canonical_issuers
) > 0) {
463 q
->q_match_issuer
= canonical_issuers
;
465 CFRelease(canonical_issuers
);
467 } else if (CFEqual(kSecMatchPolicy
, key
)) {
468 if (CFGetTypeID(value
) != CFArrayGetTypeID()) {
469 SecError(errSecParam
, &q
->q_error
, CFSTR("unsupported value for kSecMatchPolicy attribute"));
472 xpc_object_t policiesArrayXPC
= _CFXPCCreateXPCObjectFromCFObject(value
);
473 if (!policiesArrayXPC
) {
474 SecError(errSecParam
, &q
->q_error
, CFSTR("unsupported kSecMatchPolicy object in query"));
478 CFArrayRef policiesArray
= SecPolicyXPCArrayCopyArray(policiesArrayXPC
, &q
->q_error
);
479 xpc_release(policiesArrayXPC
);
483 if (CFArrayGetCount(policiesArray
) != 1 || CFGetTypeID(CFArrayGetValueAtIndex(policiesArray
, 0)) != SecPolicyGetTypeID()) {
484 CFRelease(policiesArray
);
485 SecError(errSecParam
, &q
->q_error
, CFSTR("unsupported array of policies"));
489 query_set_policy(q
, (SecPolicyRef
)CFArrayGetValueAtIndex(policiesArray
, 0));
490 CFRelease(policiesArray
);
491 } else if (CFEqual(kSecMatchValidOnDate
, key
)) {
492 if (CFGetTypeID(value
) == CFNullGetTypeID()) {
493 CFDateRef date
= CFDateCreate(kCFAllocatorDefault
, CFAbsoluteTimeGetCurrent());
494 query_set_valid_on_date(q
, date
);
496 } else if (CFGetTypeID(value
) == CFDateGetTypeID()) {
497 query_set_valid_on_date(q
, value
);
499 SecError(errSecParam
, &q
->q_error
, CFSTR("unsupported value for kSecMatchValidOnDate attribute"));
502 } else if (CFEqual(kSecMatchTrustedOnly
, key
)) {
503 if ((CFGetTypeID(value
) == CFBooleanGetTypeID())) {
504 query_set_trusted_only(q
, value
);
506 SecError(errSecParam
, &q
->q_error
, CFSTR("unsupported value for kSecMatchTrustedOnly attribute"));
512 static bool query_set_class(Query
*q
, CFStringRef c_name
, CFErrorRef
*error
) {
513 const SecDbClass
*value
;
514 if (c_name
&& CFGetTypeID(c_name
) == CFStringGetTypeID() &&
515 (value
= kc_class_with_name(c_name
)) &&
516 (q
->q_class
== 0 || q
->q_class
== value
)) {
521 if (error
&& !*error
)
522 SecError((c_name
? errSecNoSuchClass
: errSecItemClassMissing
), error
, CFSTR("can find class named: %@"), c_name
);
528 static const SecDbClass
*query_get_class(CFDictionaryRef query
, CFErrorRef
*error
) {
529 CFStringRef c_name
= NULL
;
530 const void *value
= CFDictionaryGetValue(query
, kSecClass
);
531 if (isString(value
)) {
534 value
= CFDictionaryGetValue(query
, kSecValuePersistentRef
);
536 CFDataRef pref
= value
;
537 _SecItemParsePersistentRef(pref
, &c_name
, 0);
541 if (c_name
&& (value
= kc_class_with_name(c_name
))) {
545 SecError(errSecNoSuchClass
, error
, CFSTR("can't find class named: %@"), c_name
);
547 SecError(errSecItemClassMissing
, error
, CFSTR("query missing class name"));
552 /* AUDIT[securityd](done):
553 key (ok) is a caller provided, string starting with 'c'.
554 value (ok) is a caller provided, non NULL CFTypeRef.
556 static void query_add_class(const void *key
, const void *value
, Query
*q
)
558 if (CFEqual(key
, kSecClass
)) {
559 query_set_class(q
, value
, &q
->q_error
);
561 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_class: key %@ is not %@"), key
, kSecClass
);
565 /* AUDIT[securityd](done):
566 key (ok) is a caller provided, string starting with 'r'.
567 value (ok) is a caller provided, non NULL CFTypeRef.
569 static void query_add_return(const void *key
, const void *value
, Query
*q
)
572 if (CFGetTypeID(value
) != CFBooleanGetTypeID()) {
573 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_return: value %@ is not CFBoolean"), value
);
577 int set_it
= CFEqual(value
, kCFBooleanTrue
);
579 if (CFEqual(key
, kSecReturnData
))
580 mask
= kSecReturnDataMask
;
581 else if (CFEqual(key
, kSecReturnAttributes
))
582 mask
= kSecReturnAttributesMask
;
583 else if (CFEqual(key
, kSecReturnRef
))
584 mask
= kSecReturnRefMask
;
585 else if (CFEqual(key
, kSecReturnPersistentRef
))
586 mask
= kSecReturnPersistentRefMask
;
588 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_return: unknown key %@"), key
);
592 if ((q
->q_return_type
& mask
) && !set_it
) {
593 /* Clear out this bit (it's set so xor with the mask will clear it). */
594 q
->q_return_type
^= mask
;
595 } else if (!(q
->q_return_type
& mask
) && set_it
) {
597 q
->q_return_type
|= mask
;
601 /* AUDIT[securityd](done):
602 key (ok) is a caller provided, string starting with 'u'.
603 value (ok since q_use_item_list is unused) is a caller provided, non
606 static void query_add_use(const void *key
, const void *value
, Query
*q
)
608 if (CFEqual(key
, kSecUseItemList
)) {
609 /* TODO: Add sanity checking when we start using this. */
610 q
->q_use_item_list
= value
;
611 } else if (CFEqual(key
, kSecUseTombstones
)) {
612 if (CFGetTypeID(value
) == CFBooleanGetTypeID()) {
613 q
->q_use_tomb
= value
;
614 } else if (CFGetTypeID(value
) == CFNumberGetTypeID()) {
615 q
->q_use_tomb
= CFBooleanGetValue(value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
616 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
617 q
->q_use_tomb
= CFStringGetIntValue(value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
619 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is neither CFBoolean nor CFNumber"), value
, key
);
622 } else if (CFEqual(key
, kSecUseCredentialReference
)) {
624 CFRetainAssign(q
->q_use_cred_handle
, value
);
626 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is not CFData"), value
, key
);
629 } else if (CFEqual(key
, kSecUseAuthenticationUI
)) {
630 if (isString(value
)) {
631 q
->q_skip_acl_items
= CFEqualSafe(kSecUseAuthenticationUISkip
, value
);
633 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is not CFString"), value
, key
);
637 } else if (CFEqual(key
, kSecUseSystemKeychain
)) {
638 #if TARGET_OS_EMBEDDED
639 q
->q_keybag
= KEYBAG_DEVICE
;
641 q
->q_system_keychain
= true;
642 } else if (CFEqual(key
, kSecUseSyncBubbleKeychain
)) {
643 if (isNumber(value
) && CFNumberGetValue(value
, kCFNumberSInt32Type
, &q
->q_sync_bubble
) && q
->q_sync_bubble
> 0) {
644 #if TARGET_OS_EMBEDDED
645 q
->q_keybag
= KEYBAG_DEVICE
;
648 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is not valid uid"), value
, key
);
653 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_use: unknown key %@"), key
);
658 static void query_set_data(const void *value
, Query
*q
) {
659 if (!isData(value
)) {
660 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("set_data: value %@ is not type data"), value
);
664 CFDictionarySetValue(q
->q_item
, kSecValueData
, value
);
668 /* AUDIT[securityd](done):
669 key (ok) is a caller provided, string starting with 'u'.
670 value (ok) is a caller provided, non NULL CFTypeRef.
672 static void query_add_value(const void *key
, const void *value
, Query
*q
)
674 if (CFEqual(key
, kSecValueData
)) {
675 query_set_data(value
, q
);
677 } else if (CFEqual(key
, kSecValueRef
)) {
679 /* TODO: Add value type sanity checking. */
681 } else if (CFEqual(key
, kSecValuePersistentRef
)) {
683 if (_SecItemParsePersistentRef(value
, &c_name
, &q
->q_row_id
))
684 query_set_class(q
, c_name
, &q
->q_error
);
686 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_value: value %@ is not a valid persitent ref"), value
);
688 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_value: unknown key %@"), key
);
693 /* AUDIT[securityd](done):
694 key (ok) is a caller provided, unchecked.
695 value (ok) is a caller provided, unchecked.
697 static void query_update_applier(const void *key
, const void *value
,
700 Query
*q
= (Query
*)context
;
701 /* If something went wrong there is no point processing any more args. */
705 /* Make sure we have a string key. */
706 if (!isString(key
)) {
707 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("update_applier: unknown key type %@"), key
);
712 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("update_applier: key %@ has NULL value"), key
);
716 if (CFEqual(key
, CFSTR("musr"))) {
717 secnotice("item", "update_applier: refusing to update musr");
721 if (CFEqual(key
, kSecValueData
)) {
722 query_set_data(value
, q
);
724 query_add_attribute(key
, value
, q
);
728 /* AUDIT[securityd](done):
729 key (ok) is a caller provided, unchecked.
730 value (ok) is a caller provided, unchecked.
732 static void query_applier(const void *key
, const void *value
, void *context
)
734 Query
*q
= (Query
*)context
;
735 /* If something went wrong there is no point processing any more args. */
739 /* Make sure we have a key. */
741 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("applier: NULL key"));
745 /* Make sure we have a value. */
747 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("applier: key %@ has NULL value"), key
);
751 /* Figure out what type of key we are dealing with. */
752 CFTypeID key_id
= CFGetTypeID(key
);
753 if (key_id
== CFStringGetTypeID()) {
754 CFIndex key_len
= CFStringGetLength(key
);
755 /* String keys can be different things. The subtype is determined by:
756 length 4 strings are all attributes. Otherwise the first char
758 c: class must be kSecClass
759 m: match like kSecMatchPolicy
760 r: return like kSecReturnData
766 query_add_attribute(key
, value
, q
);
767 } else if (key_len
> 1) {
768 UniChar k_first_char
= CFStringGetCharacterAtIndex(key
, 0);
769 switch (k_first_char
)
771 case 'c': /* class */
772 query_add_class(key
, value
, q
);
774 case 'm': /* match */
775 query_add_match(key
, value
, q
);
777 case 'r': /* return */
778 query_add_return(key
, value
, q
);
781 query_add_use(key
, value
, q
);
783 case 'v': /* value */
784 query_add_value(key
, value
, q
);
787 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("applier: key %@ invalid"), key
);
791 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("applier: key %@ invalid length"), key
);
793 } else if (key_id
== CFNumberGetTypeID()) {
794 /* Numeric keys are always (extended) attributes. */
795 /* TODO: Why is this here? query_add_attribute() doesn't take numbers. */
796 query_add_attribute(key
, value
, q
);
798 /* We only support string and number type keys. */
799 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("applier: key %@ neither string nor number"), key
);
803 static CFStringRef
query_infer_keyclass(Query
*q
, CFStringRef agrp
) {
804 /* apsd are always dku. */
805 if (CFEqual(agrp
, CFSTR("com.apple.apsd"))) {
806 return kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate
;
808 /* All other certs or in the apple agrp is dk. */
809 if (q
->q_class
== &cert_class
) {
810 /* third party certs are always dk. */
811 return kSecAttrAccessibleAlwaysPrivate
;
813 /* The rest defaults to ak. */
814 return kSecAttrAccessibleWhenUnlocked
;
817 void query_ensure_access_control(Query
*q
, CFStringRef agrp
) {
818 if (q
->q_access_control
== 0) {
819 CFStringRef accessible
= query_infer_keyclass(q
, agrp
);
820 query_add_attribute(kSecAttrAccessible
, accessible
, q
);
824 bool query_error(Query
*q
, CFErrorRef
*error
) {
825 CFErrorRef tmp
= q
->q_error
;
827 return SecErrorPropagate(tmp
, error
);
830 bool query_destroy(Query
*q
, CFErrorRef
*error
) {
831 bool ok
= query_error(q
, error
);
832 CFIndex ix
, attr_count
= query_attr_count(q
);
833 for (ix
= 0; ix
< attr_count
; ++ix
) {
834 CFReleaseSafe(query_attr_at(q
, ix
).value
);
836 CFReleaseSafe(q
->q_item
);
837 CFReleaseSafe(q
->q_musrView
);
838 CFReleaseSafe(q
->q_primary_key_digest
);
839 CFReleaseSafe(q
->q_match_issuer
);
840 CFReleaseSafe(q
->q_access_control
);
841 CFReleaseSafe(q
->q_use_cred_handle
);
842 CFReleaseSafe(q
->q_caller_access_groups
);
843 CFReleaseSafe(q
->q_match_policy
);
844 CFReleaseSafe(q
->q_match_valid_on_date
);
845 CFReleaseSafe(q
->q_match_trusted_only
);
851 bool query_notify_and_destroy(Query
*q
, bool ok
, CFErrorRef
*error
) {
852 if (ok
&& !q
->q_error
&& (q
->q_sync_changed
|| (q
->q_changed
&& !SecMUSRIsSingleUserView(q
->q_musrView
)))) {
853 SecKeychainChanged();
855 return query_destroy(q
, error
) && ok
;
858 /* Allocate and initialize a Query object for query. */
859 Query
*query_create(const SecDbClass
*qclass
,
861 CFDictionaryRef query
,
865 if (error
&& !*error
)
866 SecError(errSecItemClassMissing
, error
, CFSTR("Missing class"));
871 musr
= SecMUSRGetSingleUserKeychainUUID();
873 /* Number of pairs we need is the number of attributes in this class
874 plus the number of keys in the dictionary, minus one for each key in
875 the dictionary that is a regular attribute. */
876 CFIndex key_count
= SecDbClassAttrCount(qclass
);
877 if (key_count
== 0) {
878 // Identities claim to have 0 attributes, but they really support any keys or cert attribute.
879 key_count
= SecDbClassAttrCount(&cert_class
) + SecDbClassAttrCount(&keys_class
);
883 key_count
+= CFDictionaryGetCount(query
);
884 SecDbForEachAttr(qclass
, attr
) {
885 if (CFDictionaryContainsKey(query
, attr
->name
))
890 if (key_count
> QUERY_KEY_LIMIT
) {
891 if (error
&& !*error
)
893 secerror("key_count: %ld, QUERY_KEY_LIMIT: %d", (long)key_count
, QUERY_KEY_LIMIT
);
894 SecError(errSecItemIllegalQuery
, error
, CFSTR("Past query key limit"));
899 Query
*q
= calloc(1, sizeof(Query
) + sizeof(Pair
) * key_count
);
901 if (error
&& !*error
)
902 SecError(errSecAllocate
, error
, CFSTR("Out of memory"));
906 q
->q_pairs_count
= key_count
;
907 q
->q_musrView
= (CFDataRef
)CFRetain(musr
);
908 q
->q_keybag
= KEYBAG_DEVICE
;
910 q
->q_match_begin
= q
->q_match_end
= key_count
;
911 q
->q_item
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
916 /* Parse query for a Query object q. */
917 static bool query_parse_with_applier(Query
*q
, CFDictionaryRef query
,
918 CFDictionaryApplierFunction applier
,
920 CFDictionaryApplyFunction(query
, applier
, q
);
921 return query_error(q
, error
);
924 /* Parse query for a Query object q. */
925 static bool query_parse(Query
*q
, CFDictionaryRef query
,
927 return query_parse_with_applier(q
, query
, query_applier
, error
);
930 /* Parse query for a Query object q. */
931 bool query_update_parse(Query
*q
, CFDictionaryRef update
,
933 return query_parse_with_applier(q
, update
, query_update_applier
, error
);
936 Query
*query_create_with_limit(CFDictionaryRef query
, CFDataRef musr
, CFIndex limit
, CFErrorRef
*error
) {
938 q
= query_create(query_get_class(query
, error
), musr
, query
, error
);
941 if (!query_parse(q
, query
, error
)) {
942 query_destroy(q
, error
);
945 if (!q
->q_sync
&& !q
->q_row_id
) {
946 /* query did not specify a kSecAttrSynchronizable attribute,
947 * and did not contain a persistent reference. */
948 query_add_attribute(kSecAttrSynchronizable
, kCFBooleanFalse
, q
);
956 query_set_caller_access_groups(Query
*q
, CFArrayRef caller_access_groups
) {
957 CFRetainAssign(q
->q_caller_access_groups
, caller_access_groups
);
961 query_set_policy(Query
*q
, SecPolicyRef policy
) {
962 CFRetainAssign(q
->q_match_policy
, policy
);
965 void query_set_valid_on_date(Query
*q
, CFDateRef date
) {
966 CFRetainAssign(q
->q_match_valid_on_date
, date
);
969 void query_set_trusted_only(Query
*q
, CFBooleanRef trusted_only
) {
970 CFRetainAssign(q
->q_match_trusted_only
, trusted_only
);