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 * SecDbQuery.c - CoreFoundation-based constants and functions for
26 access to Security items (certificates, keys, identities, and
30 #include <securityd/SecDbQuery.h>
32 #include <securityd/SecItemDb.h>
33 #include <securityd/SecItemSchema.h>
34 #include <securityd/SecItemServer.h>
35 #include <securityd/spi.h>
36 #include <Security/SecBasePriv.h>
37 #include <Security/SecCertificateInternal.h>
38 #include <Security/SecItem.h>
39 #include <Security/SecItemPriv.h>
40 #include <Security/SecItemInternal.h>
41 #include <Security/SecAccessControl.h>
42 #include <Security/SecAccessControlPriv.h>
43 #include <CommonCrypto/CommonDigest.h>
44 #include <CommonCrypto/CommonDigestSPI.h>
46 #include <pthread/pthread.h>
49 #include <LocalAuthentication/LAPublicDefines.h>
50 #include <coreauthd_spi.h>
51 #include <libaks_acl_cf_keys.h>
54 /* Upper limit for number of keys in a QUERY dictionary. */
55 #define QUERY_KEY_LIMIT_BASE (128)
57 #define QUERY_KEY_LIMIT (31 + QUERY_KEY_LIMIT_BASE)
59 #define QUERY_KEY_LIMIT QUERY_KEY_LIMIT_BASE
62 /* Inline accessors to attr and match values in a query. */
63 CFIndex
query_attr_count(const Query
*q
)
68 Pair
query_attr_at(const Query
*q
, CFIndex ix
)
70 return q
->q_pairs
[ix
];
73 CFIndex
query_match_count(const Query
*q
)
75 return q
->q_match_end
- q
->q_match_begin
;
78 __unused
static inline Pair
query_match_at(const Query
*q
, CFIndex ix
)
80 return q
->q_pairs
[q
->q_match_begin
+ ix
];
83 /* Private routines used to parse a query. */
85 const SecDbClass
*kc_class_with_name(CFStringRef name
) {
88 // TODO Iterate kc_db_classes and look for name == class->name.
89 // Or get clever and switch on first letter of class name and compare to verify
90 static const void *kc_db_classes
[] = {
98 if (CFEqual(name
, kSecClassGenericPassword
))
100 else if (CFEqual(name
, kSecClassInternetPassword
))
102 else if (CFEqual(name
, kSecClassCertificate
))
104 else if (CFEqual(name
, kSecClassKey
))
106 else if (CFEqual(name
, kSecClassIdentity
))
107 return &identity_class
;
112 static void query_set_access_control(Query
*q
, SecAccessControlRef access_control
) {
113 if (q
->q_access_control
) {
114 if (!CFEqual(q
->q_access_control
, access_control
)) {
115 SecError(errSecItemIllegalQuery
, &q
->q_error
, CFSTR("conflicting kSecAccess and kSecAccessControl attributes"));
118 /* Store access control virtual attribute. */
119 q
->q_access_control
= (SecAccessControlRef
)CFRetain(access_control
);
121 /* Also set legacy access attribute. */
122 CFTypeRef protection
= SecAccessControlGetProtection(q
->q_access_control
);
124 SecError(errSecParam
, &q
->q_error
, CFSTR("kSecAccessControl missing protection"));
127 CFDictionarySetValue(q
->q_item
, kSecAttrAccessible
, protection
);
131 /* AUDIT[securityd](done):
132 key (ok) is a caller provided, string or number of length 4.
133 value (ok) is a caller provided, non NULL CFTypeRef.
135 void query_add_attribute_with_desc(const SecDbAttr
*desc
, const void *value
, Query
*q
)
137 if (CFEqual(desc
->name
, kSecAttrSynchronizable
)) {
139 if (CFEqual(value
, kSecAttrSynchronizableAny
))
140 return; /* skip the attribute so it isn't part of the search */
143 CFTypeRef attr
= NULL
;
144 switch (desc
->kind
) {
146 attr
= copyData(value
);
149 case kSecDbAccessControlAttr
:
150 attr
= copyBlob(value
);
153 case kSecDbCreationDateAttr
:
154 case kSecDbModificationDateAttr
:
155 attr
= copyDate(value
);
157 case kSecDbNumberAttr
:
160 attr
= copyNumber(value
);
162 case kSecDbAccessAttr
:
163 case kSecDbStringAttr
:
164 attr
= copyString(value
);
167 attr
= copySHA1(value
);
169 case kSecDbRowIdAttr
:
170 case kSecDbPrimaryKeyAttr
:
171 case kSecDbEncryptedDataAttr
:
172 case kSecDbUTombAttr
:
177 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("attribute %@: value: %@ failed to convert"), desc
->name
, value
);
181 /* Store plaintext attr data in q_item unless it's a kSecDbSHA1Attr. */
182 if (q
->q_item
&& desc
->kind
!= kSecDbSHA1Attr
) {
183 CFDictionarySetValue(q
->q_item
, desc
->name
, attr
);
186 /* Convert attr to (sha1) digest if requested. */
187 if (desc
->flags
& kSecDbSHA1ValueInFlag
) {
188 CFDataRef data
= copyData(attr
);
191 SecError(errSecInternal
, &q
->q_error
, CFSTR("failed to get attribute %@ data"), desc
->name
);
195 CFMutableDataRef digest
= CFDataCreateMutable(0, CC_SHA1_DIGEST_LENGTH
);
196 CFDataSetLength(digest
, CC_SHA1_DIGEST_LENGTH
);
197 /* 64 bits cast: worst case is we generate the wrong hash */
198 assert((unsigned long)CFDataGetLength(data
)<UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
199 CCDigest(kCCDigestSHA1
, CFDataGetBytePtr(data
), (CC_LONG
)CFDataGetLength(data
),
200 CFDataGetMutableBytePtr(digest
));
205 if (desc
->kind
!= kSecDbAccessControlAttr
) {
206 /* Record the new attr key, value in q_pairs. */
207 q
->q_pairs
[q
->q_attr_end
].key
= desc
->name
;
208 q
->q_pairs
[q
->q_attr_end
++].value
= attr
;
214 void query_add_attribute(const void *key
, const void *value
, Query
*q
)
216 const SecDbAttr
*desc
= SecDbAttrWithKey(q
->q_class
, key
, &q
->q_error
);
218 query_add_attribute_with_desc(desc
, value
, q
);
220 if (desc
->kind
== kSecDbAccessControlAttr
) {
221 CFDataRef attr
= (CFDataRef
)CFDictionaryGetValue(q
->q_item
, desc
->name
);
223 SecAccessControlRef access_control
= SecAccessControlCreateFromData(kCFAllocatorDefault
, attr
, &q
->q_error
);
224 if (access_control
) {
225 query_set_access_control(q
, access_control
);
226 CFRelease(access_control
);
231 if (desc
->kind
== kSecDbAccessAttr
) {
232 SecAccessControlRef access_control
= SecAccessControlCreate(kCFAllocatorDefault
, &q
->q_error
);
233 if (access_control
) {
234 CFStringRef attr
= (CFStringRef
)CFDictionaryGetValue(q
->q_item
, desc
->name
);
236 if (SecAccessControlSetProtection(access_control
, attr
, &q
->q_error
))
237 query_set_access_control(q
, access_control
);
239 CFRelease(access_control
);
245 void query_add_or_attribute(const void *key
, const void *value
, Query
*q
)
247 const SecDbAttr
*desc
= SecDbAttrWithKey(q
->q_class
, key
, &q
->q_error
);
249 CFTypeRef oldValue
= CFDictionaryGetValue(q
->q_item
, desc
->name
);
250 CFMutableArrayRef array
= NULL
;
252 if (isArray(oldValue
)) {
253 array
= (CFMutableArrayRef
)CFRetain(oldValue
);
255 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
256 CFArrayAppendValue(array
, oldValue
);
258 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
260 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
263 query_add_attribute_with_desc(desc
, value
, q
);
264 CFTypeRef newValue
= CFDictionaryGetValue(q
->q_item
, desc
->name
);
265 CFArrayAppendValue(array
, newValue
);
266 CFDictionarySetValue(q
->q_item
, desc
->name
, array
);
272 void query_add_not_attribute(const void *key
, const void *value
, Query
*q
)
274 const SecDbAttr
*desc
= SecDbAttrWithKey(q
->q_class
, key
, &q
->q_error
);
276 CFTypeRef oldValue
= CFDictionaryGetValue(q
->q_item
, desc
->name
);
277 CFMutableArrayRef array
= NULL
;
279 if (isArray(oldValue
)) {
280 array
= (CFMutableArrayRef
)CFRetain(oldValue
);
282 // This should never run, as we shouldn't be turning a attr = value into a attr not in (value, value2)
283 secerror("negating %@ = %@ in query", desc
->name
, oldValue
);
284 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
285 CFArrayAppendValue(array
, kCFNull
);
286 CFArrayAppendValue(array
, oldValue
);
288 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
290 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
291 CFArrayAppendValue(array
, kCFNull
);
294 query_add_attribute_with_desc(desc
, value
, q
);
295 CFTypeRef newValue
= CFDictionaryGetValue(q
->q_item
, desc
->name
);
296 CFArrayAppendValue(array
, newValue
);
297 CFDictionarySetValue(q
->q_item
, desc
->name
, array
);
304 /* AUDIT[securityd](done):
305 key (ok) is a caller provided, string starting with 'm'.
306 value (ok) is a caller provided, non NULL CFTypeRef.
308 static void query_add_match(const void *key
, const void *value
, Query
*q
)
310 /* Record the match key, value in q_pairs. */
311 --(q
->q_match_begin
);
312 q
->q_pairs
[q
->q_match_begin
].key
= key
;
313 q
->q_pairs
[q
->q_match_begin
].value
= value
;
315 if (CFEqual(kSecMatchLimit
, key
)) {
316 /* Figure out what the value for kSecMatchLimit is if specified. */
317 if (CFGetTypeID(value
) == CFNumberGetTypeID()) {
318 if (!CFNumberGetValue(value
, kCFNumberCFIndexType
, &q
->q_limit
))
319 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("failed to convert match limit %@ to CFIndex"), value
);
320 } else if (CFEqual(kSecMatchLimitAll
, value
)) {
321 q
->q_limit
= kSecMatchUnlimited
;
322 } else if (CFEqual(kSecMatchLimitOne
, value
)) {
325 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("unsupported match limit %@"), value
);
327 } else if (CFEqual(kSecMatchIssuers
, key
) &&
328 (CFGetTypeID(value
) == CFArrayGetTypeID()))
330 CFMutableArrayRef canonical_issuers
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
331 if (canonical_issuers
) {
332 CFIndex i
, count
= CFArrayGetCount(value
);
333 for (i
= 0; i
< count
; i
++) {
334 CFTypeRef issuer_data
= CFArrayGetValueAtIndex(value
, i
);
335 CFDataRef issuer_canonical
= NULL
;
336 if (CFDataGetTypeID() == CFGetTypeID(issuer_data
))
337 issuer_canonical
= SecDistinguishedNameCopyNormalizedContent((CFDataRef
)issuer_data
);
338 if (issuer_canonical
) {
339 CFArrayAppendValue(canonical_issuers
, issuer_canonical
);
340 CFRelease(issuer_canonical
);
344 if (CFArrayGetCount(canonical_issuers
) > 0) {
345 q
->q_match_issuer
= canonical_issuers
;
347 CFRelease(canonical_issuers
);
352 static bool query_set_class(Query
*q
, CFStringRef c_name
, CFErrorRef
*error
) {
353 const SecDbClass
*value
;
354 if (c_name
&& CFGetTypeID(c_name
) == CFStringGetTypeID() &&
355 (value
= kc_class_with_name(c_name
)) &&
356 (q
->q_class
== 0 || q
->q_class
== value
)) {
361 if (error
&& !*error
)
362 SecError((c_name
? errSecNoSuchClass
: errSecItemClassMissing
), error
, CFSTR("can find class named: %@"), c_name
);
368 static const SecDbClass
*query_get_class(CFDictionaryRef query
, CFErrorRef
*error
) {
369 CFStringRef c_name
= NULL
;
370 const void *value
= CFDictionaryGetValue(query
, kSecClass
);
371 if (isString(value
)) {
374 value
= CFDictionaryGetValue(query
, kSecValuePersistentRef
);
376 CFDataRef pref
= value
;
377 _SecItemParsePersistentRef(pref
, &c_name
, 0);
381 if (c_name
&& (value
= kc_class_with_name(c_name
))) {
385 SecError(errSecNoSuchClass
, error
, CFSTR("can't find class named: %@"), c_name
);
387 SecError(errSecItemClassMissing
, error
, CFSTR("query missing class name"));
392 /* AUDIT[securityd](done):
393 key (ok) is a caller provided, string starting with 'c'.
394 value (ok) is a caller provided, non NULL CFTypeRef.
396 static void query_add_class(const void *key
, const void *value
, Query
*q
)
398 if (CFEqual(key
, kSecClass
)) {
399 query_set_class(q
, value
, &q
->q_error
);
401 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_class: key %@ is not %@"), key
, kSecClass
);
405 /* AUDIT[securityd](done):
406 key (ok) is a caller provided, string starting with 'r'.
407 value (ok) is a caller provided, non NULL CFTypeRef.
409 static void query_add_return(const void *key
, const void *value
, Query
*q
)
412 if (CFGetTypeID(value
) != CFBooleanGetTypeID()) {
413 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_return: value %@ is not CFBoolean"), value
);
417 int set_it
= CFEqual(value
, kCFBooleanTrue
);
419 if (CFEqual(key
, kSecReturnData
))
420 mask
= kSecReturnDataMask
;
421 else if (CFEqual(key
, kSecReturnAttributes
))
422 mask
= kSecReturnAttributesMask
;
423 else if (CFEqual(key
, kSecReturnRef
))
424 mask
= kSecReturnRefMask
;
425 else if (CFEqual(key
, kSecReturnPersistentRef
))
426 mask
= kSecReturnPersistentRefMask
;
428 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_return: unknown key %@"), key
);
432 if ((q
->q_return_type
& mask
) && !set_it
) {
433 /* Clear out this bit (it's set so xor with the mask will clear it). */
434 q
->q_return_type
^= mask
;
435 } else if (!(q
->q_return_type
& mask
) && set_it
) {
437 q
->q_return_type
|= mask
;
441 /* AUDIT[securityd](done):
442 key (ok) is a caller provided, string starting with 'u'.
443 value (ok since q_use_item_list is unused) is a caller provided, non
446 static void query_add_use(const void *key
, const void *value
, Query
*q
)
448 if (CFEqual(key
, kSecUseItemList
)) {
449 /* TODO: Add sanity checking when we start using this. */
450 q
->q_use_item_list
= value
;
451 } else if (CFEqual(key
, kSecUseTombstones
)) {
452 if (CFGetTypeID(value
) == CFBooleanGetTypeID()) {
453 q
->q_use_tomb
= value
;
454 } else if (CFGetTypeID(value
) == CFNumberGetTypeID()) {
455 q
->q_use_tomb
= CFBooleanGetValue(value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
456 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
457 q
->q_use_tomb
= CFStringGetIntValue(value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
459 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is neither CFBoolean nor CFNumber"), value
, key
);
462 } else if (CFEqual(key
, kSecUseCredentialReference
)) {
464 CFRetainAssign(q
->q_use_cred_handle
, value
);
466 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is not CFData"), value
, key
);
469 } else if (CFEqual(key
, kSecUseAuthenticationUI
)) {
470 if (isString(value
)) {
471 q
->q_skip_acl_items
= CFEqualSafe(kSecUseAuthenticationUISkip
, value
);
473 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is not CFString"), value
, key
);
476 #if defined(MULTIPLE_KEYCHAINS)
477 } else if (CFEqual(key
, kSecUseKeychain
)) {
478 q
->q_use_keychain
= value
;
479 } else if (CFEqual(key
, kSecUseKeychainList
)) {
480 q
->q_use_keychain_list
= value
;
481 #endif /* !defined(MULTIPLE_KEYCHAINS) */
483 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_use: unknown key %@"), key
);
488 static void query_set_data(const void *value
, Query
*q
) {
489 if (!isData(value
)) {
490 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("set_data: value %@ is not type data"), value
);
494 CFDictionarySetValue(q
->q_item
, kSecValueData
, value
);
498 /* AUDIT[securityd](done):
499 key (ok) is a caller provided, string starting with 'u'.
500 value (ok) is a caller provided, non NULL CFTypeRef.
502 static void query_add_value(const void *key
, const void *value
, Query
*q
)
504 if (CFEqual(key
, kSecValueData
)) {
505 query_set_data(value
, q
);
507 } else if (CFEqual(key
, kSecValueRef
)) {
509 /* TODO: Add value type sanity checking. */
511 } else if (CFEqual(key
, kSecValuePersistentRef
)) {
513 if (_SecItemParsePersistentRef(value
, &c_name
, &q
->q_row_id
))
514 query_set_class(q
, c_name
, &q
->q_error
);
516 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_value: value %@ is not a valid persitent ref"), value
);
518 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_value: unknown key %@"), key
);
523 /* AUDIT[securityd](done):
524 key (ok) is a caller provided, unchecked.
525 value (ok) is a caller provided, unchecked.
527 static void query_update_applier(const void *key
, const void *value
,
530 Query
*q
= (Query
*)context
;
531 /* If something went wrong there is no point processing any more args. */
535 /* Make sure we have a string key. */
536 if (!isString(key
)) {
537 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("update_applier: unknown key type %@"), key
);
542 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("update_applier: key %@ has NULL value"), key
);
546 if (CFEqual(key
, kSecValueData
)) {
547 query_set_data(value
, q
);
549 query_add_attribute(key
, value
, q
);
553 /* AUDIT[securityd](done):
554 key (ok) is a caller provided, unchecked.
555 value (ok) is a caller provided, unchecked.
557 static void query_applier(const void *key
, const void *value
, void *context
)
559 Query
*q
= (Query
*)context
;
560 /* If something went wrong there is no point processing any more args. */
564 /* Make sure we have a key. */
566 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("applier: NULL key"));
570 /* Make sure we have a value. */
572 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("applier: key %@ has NULL value"), key
);
576 /* Figure out what type of key we are dealing with. */
577 CFTypeID key_id
= CFGetTypeID(key
);
578 if (key_id
== CFStringGetTypeID()) {
579 CFIndex key_len
= CFStringGetLength(key
);
580 /* String keys can be different things. The subtype is determined by:
581 length 4 strings are all attributes. Otherwise the first char
583 c: class must be kSecClass
584 m: match like kSecMatchPolicy
585 r: return like kSecReturnData
591 query_add_attribute(key
, value
, q
);
592 } else if (key_len
> 1) {
593 UniChar k_first_char
= CFStringGetCharacterAtIndex(key
, 0);
594 switch (k_first_char
)
596 case 'c': /* class */
597 query_add_class(key
, value
, q
);
599 case 'm': /* match */
600 query_add_match(key
, value
, q
);
602 case 'r': /* return */
603 query_add_return(key
, value
, q
);
606 query_add_use(key
, value
, q
);
608 case 'v': /* value */
609 query_add_value(key
, value
, q
);
612 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("applier: key %@ invalid"), key
);
616 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("applier: key %@ invalid length"), key
);
618 } else if (key_id
== CFNumberGetTypeID()) {
619 /* Numeric keys are always (extended) attributes. */
620 /* TODO: Why is this here? query_add_attribute() doesn't take numbers. */
621 query_add_attribute(key
, value
, q
);
623 /* We only support string and number type keys. */
624 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("applier: key %@ neither string nor number"), key
);
628 static CFStringRef
query_infer_keyclass(Query
*q
, CFStringRef agrp
) {
629 /* apsd and lockdown are always dku. */
630 if (CFEqual(agrp
, CFSTR("com.apple.apsd"))
631 || CFEqual(agrp
, CFSTR("lockdown-identities"))) {
632 return kSecAttrAccessibleAlwaysThisDeviceOnly
;
634 /* All other certs or in the apple agrp is dk. */
635 if (q
->q_class
== &cert_class
) {
636 /* third party certs are always dk. */
637 return kSecAttrAccessibleAlways
;
639 /* The rest defaults to ak. */
640 return kSecAttrAccessibleWhenUnlocked
;
643 void query_ensure_access_control(Query
*q
, CFStringRef agrp
) {
644 if (q
->q_access_control
== 0) {
645 CFStringRef accessible
= query_infer_keyclass(q
, agrp
);
646 query_add_attribute(kSecAttrAccessible
, accessible
, q
);
650 bool query_error(Query
*q
, CFErrorRef
*error
) {
651 CFErrorRef tmp
= q
->q_error
;
653 return SecErrorPropagate(tmp
, error
);
656 bool query_destroy(Query
*q
, CFErrorRef
*error
) {
657 bool ok
= query_error(q
, error
);
658 CFIndex ix
, attr_count
= query_attr_count(q
);
659 for (ix
= 0; ix
< attr_count
; ++ix
) {
660 CFReleaseSafe(query_attr_at(q
, ix
).value
);
662 CFReleaseSafe(q
->q_item
);
663 CFReleaseSafe(q
->q_primary_key_digest
);
664 CFReleaseSafe(q
->q_match_issuer
);
665 CFReleaseSafe(q
->q_access_control
);
666 CFReleaseSafe(q
->q_use_cred_handle
);
667 CFReleaseSafe(q
->q_caller_access_groups
);
673 bool query_notify_and_destroy(Query
*q
, bool ok
, CFErrorRef
*error
) {
674 if (ok
&& !q
->q_error
&& q
->q_sync_changed
) {
675 SecKeychainChanged(true);
677 return query_destroy(q
, error
) && ok
;
680 /* Allocate and initialize a Query object for query. */
681 Query
*query_create(const SecDbClass
*qclass
, CFDictionaryRef query
,
685 if (error
&& !*error
)
686 SecError(errSecItemClassMissing
, error
, CFSTR("Missing class"));
690 /* Number of pairs we need is the number of attributes in this class
691 plus the number of keys in the dictionary, minus one for each key in
692 the dictionary that is a regular attribute. */
693 CFIndex key_count
= SecDbClassAttrCount(qclass
);
694 if (key_count
== 0) {
695 // Identities claim to have 0 attributes, but they really support any keys or cert attribute.
696 key_count
= SecDbClassAttrCount(&cert_class
) + SecDbClassAttrCount(&keys_class
);
700 key_count
+= CFDictionaryGetCount(query
);
701 SecDbForEachAttr(qclass
, attr
) {
702 if (CFDictionaryContainsKey(query
, attr
->name
))
707 if (key_count
> QUERY_KEY_LIMIT
) {
708 if (error
&& !*error
)
710 secerror("key_count: %ld, QUERY_KEY_LIMIT: %d", (long)key_count
, QUERY_KEY_LIMIT
);
711 SecError(errSecItemIllegalQuery
, error
, CFSTR("Past query key limit"));
716 Query
*q
= calloc(1, sizeof(Query
) + sizeof(Pair
) * key_count
);
718 if (error
&& !*error
)
719 SecError(errSecAllocate
, error
, CFSTR("Out of memory"));
723 q
->q_keybag
= KEYBAG_DEVICE
;
725 q
->q_match_begin
= q
->q_match_end
= key_count
;
726 q
->q_item
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
731 /* Parse query for a Query object q. */
732 static bool query_parse_with_applier(Query
*q
, CFDictionaryRef query
,
733 CFDictionaryApplierFunction applier
,
735 CFDictionaryApplyFunction(query
, applier
, q
);
736 return query_error(q
, error
);
739 /* Parse query for a Query object q. */
740 static bool query_parse(Query
*q
, CFDictionaryRef query
,
742 return query_parse_with_applier(q
, query
, query_applier
, error
);
745 /* Parse query for a Query object q. */
746 bool query_update_parse(Query
*q
, CFDictionaryRef update
,
748 return query_parse_with_applier(q
, update
, query_update_applier
, error
);
751 Query
*query_create_with_limit(CFDictionaryRef query
, CFIndex limit
, CFErrorRef
*error
) {
753 q
= query_create(query_get_class(query
, error
), query
, error
);
756 if (!query_parse(q
, query
, error
)) {
757 query_destroy(q
, error
);
760 if (!q
->q_sync
&& !q
->q_row_id
) {
761 /* query did not specify a kSecAttrSynchronizable attribute,
762 * and did not contain a persistent reference. */
763 query_add_attribute(kSecAttrSynchronizable
, kCFBooleanFalse
, q
);
771 query_set_caller_access_groups(Query
*q
, CFArrayRef caller_access_groups
) {
772 CFRetainAssign(q
->q_caller_access_groups
, caller_access_groups
);