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 CFDictionarySetValue(q
->q_item
, kSecAttrAccessible
, SecAccessControlGetProtection(q
->q_access_control
));
126 /* AUDIT[securityd](done):
127 key (ok) is a caller provided, string or number of length 4.
128 value (ok) is a caller provided, non NULL CFTypeRef.
130 static void query_add_attribute_with_desc(const SecDbAttr
*desc
, const void *value
, Query
*q
)
132 if (CFEqual(desc
->name
, kSecAttrSynchronizable
)) {
134 if (CFEqual(value
, kSecAttrSynchronizableAny
))
135 return; /* skip the attribute so it isn't part of the search */
138 CFTypeRef attr
= NULL
;
139 switch (desc
->kind
) {
141 attr
= copyData(value
);
144 case kSecDbAccessControlAttr
:
145 attr
= copyBlob(value
);
148 case kSecDbCreationDateAttr
:
149 case kSecDbModificationDateAttr
:
150 attr
= copyDate(value
);
152 case kSecDbNumberAttr
:
155 attr
= copyNumber(value
);
157 case kSecDbAccessAttr
:
158 case kSecDbStringAttr
:
159 attr
= copyString(value
);
162 attr
= copySHA1(value
);
164 case kSecDbRowIdAttr
:
165 case kSecDbPrimaryKeyAttr
:
166 case kSecDbEncryptedDataAttr
:
171 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("attribute %@: value: %@ failed to convert"), desc
->name
, value
);
175 /* Store plaintext attr data in q_item unless it's a kSecDbSHA1Attr. */
176 if (q
->q_item
&& desc
->kind
!= kSecDbSHA1Attr
) {
177 CFDictionarySetValue(q
->q_item
, desc
->name
, attr
);
180 if (desc
->kind
== kSecDbAccessControlAttr
) {
181 SecAccessControlRef access_control
= SecAccessControlCreateFromData(kCFAllocatorDefault
, attr
, &q
->q_error
);
182 if (access_control
) {
183 query_set_access_control(q
, access_control
);
184 CFRelease(access_control
);
188 if (desc
->kind
== kSecDbAccessAttr
) {
189 SecAccessControlRef access_control
= SecAccessControlCreateWithFlags(kCFAllocatorDefault
, attr
, 0, &q
->q_error
);
190 if (access_control
) {
191 query_set_access_control(q
, access_control
);
192 CFRelease(access_control
);
196 /* Convert attr to (sha1) digest if requested. */
197 if (desc
->flags
& kSecDbSHA1ValueInFlag
) {
198 CFDataRef data
= copyData(attr
);
201 SecError(errSecInternal
, &q
->q_error
, CFSTR("failed to get attribute %@ data"), desc
->name
);
205 CFMutableDataRef digest
= CFDataCreateMutable(0, CC_SHA1_DIGEST_LENGTH
);
206 CFDataSetLength(digest
, CC_SHA1_DIGEST_LENGTH
);
207 /* 64 bits cast: worst case is we generate the wrong hash */
208 assert((unsigned long)CFDataGetLength(data
)<UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
209 CCDigest(kCCDigestSHA1
, CFDataGetBytePtr(data
), (CC_LONG
)CFDataGetLength(data
),
210 CFDataGetMutableBytePtr(digest
));
215 if (desc
->kind
!= kSecDbAccessControlAttr
) {
216 /* Record the new attr key, value in q_pairs. */
217 q
->q_pairs
[q
->q_attr_end
].key
= desc
->name
;
218 q
->q_pairs
[q
->q_attr_end
++].value
= attr
;
224 void query_add_attribute(const void *key
, const void *value
, Query
*q
)
226 const SecDbAttr
*desc
= SecDbAttrWithKey(q
->q_class
, key
, &q
->q_error
);
228 query_add_attribute_with_desc(desc
, value
, q
);
231 /* First remove key from q->q_pairs if it's present, then add the attribute again. */
232 static void query_set_attribute_with_desc(const SecDbAttr
*desc
, const void *value
, Query
*q
) {
233 if (CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
235 for (ix
= 0; ix
< q
->q_attr_end
; ++ix
) {
236 if (CFEqual(desc
->name
, q
->q_pairs
[ix
].key
)) {
237 CFReleaseSafe(q
->q_pairs
[ix
].value
);
239 for (; ix
< q
->q_attr_end
; ++ix
) {
240 q
->q_pairs
[ix
] = q
->q_pairs
[ix
+ 1];
242 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
247 query_add_attribute_with_desc(desc
, value
, q
);
250 /* AUDIT[securityd](done):
251 key (ok) is a caller provided, string starting with 'm'.
252 value (ok) is a caller provided, non NULL CFTypeRef.
254 static void query_add_match(const void *key
, const void *value
, Query
*q
)
256 /* Record the match key, value in q_pairs. */
257 --(q
->q_match_begin
);
258 q
->q_pairs
[q
->q_match_begin
].key
= key
;
259 q
->q_pairs
[q
->q_match_begin
].value
= value
;
261 if (CFEqual(kSecMatchLimit
, key
)) {
262 /* Figure out what the value for kSecMatchLimit is if specified. */
263 if (CFGetTypeID(value
) == CFNumberGetTypeID()) {
264 if (!CFNumberGetValue(value
, kCFNumberCFIndexType
, &q
->q_limit
))
265 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("failed to convert match limit %@ to CFIndex"), value
);
266 } else if (CFEqual(kSecMatchLimitAll
, value
)) {
267 q
->q_limit
= kSecMatchUnlimited
;
268 } else if (CFEqual(kSecMatchLimitOne
, value
)) {
271 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("unsupported match limit %@"), value
);
273 } else if (CFEqual(kSecMatchIssuers
, key
) &&
274 (CFGetTypeID(value
) == CFArrayGetTypeID()))
276 CFMutableArrayRef canonical_issuers
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
277 if (canonical_issuers
) {
278 CFIndex i
, count
= CFArrayGetCount(value
);
279 for (i
= 0; i
< count
; i
++) {
280 CFTypeRef issuer_data
= CFArrayGetValueAtIndex(value
, i
);
281 CFDataRef issuer_canonical
= NULL
;
282 if (CFDataGetTypeID() == CFGetTypeID(issuer_data
))
283 issuer_canonical
= SecDistinguishedNameCopyNormalizedContent((CFDataRef
)issuer_data
);
284 if (issuer_canonical
) {
285 CFArrayAppendValue(canonical_issuers
, issuer_canonical
);
286 CFRelease(issuer_canonical
);
290 if (CFArrayGetCount(canonical_issuers
) > 0) {
291 q
->q_match_issuer
= canonical_issuers
;
293 CFRelease(canonical_issuers
);
298 static bool query_set_class(Query
*q
, CFStringRef c_name
, CFErrorRef
*error
) {
299 const SecDbClass
*value
;
300 if (c_name
&& CFGetTypeID(c_name
) == CFStringGetTypeID() &&
301 (value
= kc_class_with_name(c_name
)) &&
302 (q
->q_class
== 0 || q
->q_class
== value
)) {
307 if (error
&& !*error
)
308 SecError((c_name
? errSecNoSuchClass
: errSecItemClassMissing
), error
, CFSTR("can find class named: %@"), c_name
);
314 static const SecDbClass
*query_get_class(CFDictionaryRef query
, CFErrorRef
*error
) {
315 CFStringRef c_name
= NULL
;
316 const void *value
= CFDictionaryGetValue(query
, kSecClass
);
317 if (isString(value
)) {
320 value
= CFDictionaryGetValue(query
, kSecValuePersistentRef
);
322 CFDataRef pref
= value
;
323 _SecItemParsePersistentRef(pref
, &c_name
, 0);
327 if (c_name
&& (value
= kc_class_with_name(c_name
))) {
331 SecError(errSecNoSuchClass
, error
, CFSTR("can't find class named: %@"), c_name
);
333 SecError(errSecItemClassMissing
, error
, CFSTR("query missing class name"));
338 /* AUDIT[securityd](done):
339 key (ok) is a caller provided, string starting with 'c'.
340 value (ok) is a caller provided, non NULL CFTypeRef.
342 static void query_add_class(const void *key
, const void *value
, Query
*q
)
344 if (CFEqual(key
, kSecClass
)) {
345 query_set_class(q
, value
, &q
->q_error
);
347 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_class: key %@ is not %@"), key
, kSecClass
);
351 /* AUDIT[securityd](done):
352 key (ok) is a caller provided, string starting with 'r'.
353 value (ok) is a caller provided, non NULL CFTypeRef.
355 static void query_add_return(const void *key
, const void *value
, Query
*q
)
358 if (CFGetTypeID(value
) != CFBooleanGetTypeID()) {
359 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_return: value %@ is not CFBoolean"), value
);
363 int set_it
= CFEqual(value
, kCFBooleanTrue
);
365 if (CFEqual(key
, kSecReturnData
))
366 mask
= kSecReturnDataMask
;
367 else if (CFEqual(key
, kSecReturnAttributes
))
368 mask
= kSecReturnAttributesMask
;
369 else if (CFEqual(key
, kSecReturnRef
))
370 mask
= kSecReturnRefMask
;
371 else if (CFEqual(key
, kSecReturnPersistentRef
))
372 mask
= kSecReturnPersistentRefMask
;
374 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_return: unknown key %@"), key
);
378 if ((q
->q_return_type
& mask
) && !set_it
) {
379 /* Clear out this bit (it's set so xor with the mask will clear it). */
380 q
->q_return_type
^= mask
;
381 } else if (!(q
->q_return_type
& mask
) && set_it
) {
383 q
->q_return_type
|= mask
;
387 /* AUDIT[securityd](done):
388 key (ok) is a caller provided, string starting with 'u'.
389 value (ok since q_use_item_list is unused) is a caller provided, non
392 static void query_add_use(const void *key
, const void *value
, Query
*q
)
394 if (CFEqual(key
, kSecUseItemList
)) {
395 /* TODO: Add sanity checking when we start using this. */
396 q
->q_use_item_list
= value
;
397 } else if (CFEqual(key
, kSecUseTombstones
)) {
398 if (CFGetTypeID(value
) == CFBooleanGetTypeID()) {
399 q
->q_use_tomb
= value
;
400 } else if (CFGetTypeID(value
) == CFNumberGetTypeID()) {
401 q
->q_use_tomb
= CFBooleanGetValue(value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
402 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
403 q
->q_use_tomb
= CFStringGetIntValue(value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
405 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is neither CFBoolean nor CFNumber"), value
, key
);
408 } else if (CFEqual(key
, kSecUseCredentialReference
)) {
410 CFRetainAssign(q
->q_use_cred_handle
, value
);
412 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is not CFData"), value
, key
);
415 } else if (CFEqual(key
, kSecUseOperationPrompt
)) {
416 if (isString(value
)) {
417 CFRetainAssign(q
->q_use_operation_prompt
, value
);
419 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is not CFString"), value
, key
);
422 } else if (CFEqual(key
, kSecUseNoAuthenticationUI
)) {
423 if (isBoolean(value
)) {
424 q
->q_use_no_authentication_ui
= value
;
425 } else if (isNumber(value
)) {
426 q
->q_use_no_authentication_ui
= CFBooleanGetValue(value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
427 } else if (isString(value
)) {
428 q
->q_use_no_authentication_ui
= CFStringGetIntValue(value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
430 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is neither CFBoolean nor CFNumber"), value
, key
);
433 #if defined(MULTIPLE_KEYCHAINS)
434 } else if (CFEqual(key
, kSecUseKeychain
)) {
435 q
->q_use_keychain
= value
;
436 } else if (CFEqual(key
, kSecUseKeychainList
)) {
437 q
->q_use_keychain_list
= value
;
438 #endif /* !defined(MULTIPLE_KEYCHAINS) */
440 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_use: unknown key %@"), key
);
445 static void query_set_data(const void *value
, Query
*q
) {
446 if (!isData(value
)) {
447 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("set_data: value %@ is not type data"), value
);
451 CFDictionarySetValue(q
->q_item
, kSecValueData
, value
);
455 /* AUDIT[securityd](done):
456 key (ok) is a caller provided, string starting with 'u'.
457 value (ok) is a caller provided, non NULL CFTypeRef.
459 static void query_add_value(const void *key
, const void *value
, Query
*q
)
461 if (CFEqual(key
, kSecValueData
)) {
462 query_set_data(value
, q
);
464 } else if (CFEqual(key
, kSecValueRef
)) {
466 /* TODO: Add value type sanity checking. */
468 } else if (CFEqual(key
, kSecValuePersistentRef
)) {
470 if (_SecItemParsePersistentRef(value
, &c_name
, &q
->q_row_id
))
471 query_set_class(q
, c_name
, &q
->q_error
);
473 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_value: value %@ is not a valid persitent ref"), value
);
475 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_value: unknown key %@"), key
);
480 /* AUDIT[securityd](done):
481 key (ok) is a caller provided, unchecked.
482 value (ok) is a caller provided, unchecked.
484 static void query_update_applier(const void *key
, const void *value
,
487 Query
*q
= (Query
*)context
;
488 /* If something went wrong there is no point processing any more args. */
492 /* Make sure we have a string key. */
493 if (!isString(key
)) {
494 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("update_applier: unknown key type %@"), key
);
499 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("update_applier: key %@ has NULL value"), key
);
503 if (CFEqual(key
, kSecValueData
)) {
504 query_set_data(value
, q
);
506 query_add_attribute(key
, value
, q
);
510 /* AUDIT[securityd](done):
511 key (ok) is a caller provided, unchecked.
512 value (ok) is a caller provided, unchecked.
514 static void query_applier(const void *key
, const void *value
, void *context
)
516 Query
*q
= (Query
*)context
;
517 /* If something went wrong there is no point processing any more args. */
521 /* Make sure we have a key. */
523 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("applier: NULL key"));
527 /* Make sure we have a value. */
529 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("applier: key %@ has NULL value"), key
);
533 /* Figure out what type of key we are dealing with. */
534 CFTypeID key_id
= CFGetTypeID(key
);
535 if (key_id
== CFStringGetTypeID()) {
536 CFIndex key_len
= CFStringGetLength(key
);
537 /* String keys can be different things. The subtype is determined by:
538 length 4 strings are all attributes. Otherwise the first char
540 c: class must be kSecClass
541 m: match like kSecMatchPolicy
542 r: return like kSecReturnData
548 query_add_attribute(key
, value
, q
);
549 } else if (key_len
> 1) {
550 UniChar k_first_char
= CFStringGetCharacterAtIndex(key
, 0);
551 switch (k_first_char
)
553 case 'c': /* class */
554 query_add_class(key
, value
, q
);
556 case 'm': /* match */
557 query_add_match(key
, value
, q
);
559 case 'r': /* return */
560 query_add_return(key
, value
, q
);
563 query_add_use(key
, value
, q
);
565 case 'v': /* value */
566 query_add_value(key
, value
, q
);
569 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("applier: key %@ invalid"), key
);
573 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("applier: key %@ invalid length"), key
);
575 } else if (key_id
== CFNumberGetTypeID()) {
576 /* Numeric keys are always (extended) attributes. */
577 /* TODO: Why is this here? query_add_attribute() doesn't take numbers. */
578 query_add_attribute(key
, value
, q
);
580 /* We only support string and number type keys. */
581 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("applier: key %@ neither string nor number"), key
);
585 static CFStringRef
query_infer_keyclass(Query
*q
, CFStringRef agrp
) {
586 /* apsd and lockdown are always dku. */
587 if (CFEqual(agrp
, CFSTR("com.apple.apsd"))
588 || CFEqual(agrp
, CFSTR("lockdown-identities"))) {
589 return kSecAttrAccessibleAlwaysThisDeviceOnly
;
591 /* All other certs or in the apple agrp is dk. */
592 if (q
->q_class
== &cert_class
) {
593 /* third party certs are always dk. */
594 return kSecAttrAccessibleAlways
;
596 /* The rest defaults to ak. */
597 return kSecAttrAccessibleWhenUnlocked
;
600 void query_ensure_access_control(Query
*q
, CFStringRef agrp
) {
601 if (q
->q_access_control
== 0) {
602 CFStringRef accessible
= query_infer_keyclass(q
, agrp
);
603 query_add_attribute(kSecAttrAccessible
, accessible
, q
);
607 bool query_error(Query
*q
, CFErrorRef
*error
) {
608 CFErrorRef tmp
= q
->q_error
;
610 return CFErrorPropagate(tmp
, error
);
613 bool query_destroy(Query
*q
, CFErrorRef
*error
) {
614 bool ok
= query_error(q
, error
);
615 CFIndex ix
, attr_count
= query_attr_count(q
);
616 for (ix
= 0; ix
< attr_count
; ++ix
) {
617 CFReleaseSafe(query_attr_at(q
, ix
).value
);
619 CFReleaseSafe(q
->q_item
);
620 CFReleaseSafe(q
->q_primary_key_digest
);
621 CFReleaseSafe(q
->q_match_issuer
);
622 CFReleaseSafe(q
->q_access_control
);
623 CFReleaseSafe(q
->q_use_cred_handle
);
624 CFReleaseSafe(q
->q_required_access_controls
);
625 CFReleaseSafe(q
->q_use_operation_prompt
);
626 CFReleaseSafe(q
->q_caller_access_groups
);
632 bool query_notify_and_destroy(Query
*q
, bool ok
, CFErrorRef
*error
) {
633 if (ok
&& !q
->q_error
&& q
->q_sync_changed
) {
634 SecKeychainChanged(true);
636 return query_destroy(q
, error
) && ok
;
639 /* Allocate and initialize a Query object for query. */
640 Query
*query_create(const SecDbClass
*qclass
, CFDictionaryRef query
,
644 if (error
&& !*error
)
645 SecError(errSecItemClassMissing
, error
, CFSTR("Missing class"));
649 /* Number of pairs we need is the number of attributes in this class
650 plus the number of keys in the dictionary, minus one for each key in
651 the dictionary that is a regular attribute. */
652 CFIndex key_count
= SecDbClassAttrCount(qclass
);
653 if (key_count
== 0) {
654 // Identities claim to have 0 attributes, but they really support any keys or cert attribute.
655 key_count
= SecDbClassAttrCount(&cert_class
) + SecDbClassAttrCount(&keys_class
);
659 key_count
+= CFDictionaryGetCount(query
);
660 SecDbForEachAttr(qclass
, attr
) {
661 if (CFDictionaryContainsKey(query
, attr
->name
))
666 if (key_count
> QUERY_KEY_LIMIT
) {
667 if (error
&& !*error
)
669 secerror("key_count: %ld, QUERY_KEY_LIMIT: %d", (long)key_count
, QUERY_KEY_LIMIT
);
670 SecError(errSecItemIllegalQuery
, error
, CFSTR("Past query key limit"));
675 Query
*q
= calloc(1, sizeof(Query
) + sizeof(Pair
) * key_count
);
677 if (error
&& !*error
)
678 SecError(errSecAllocate
, error
, CFSTR("Out of memory"));
682 q
->q_keybag
= KEYBAG_DEVICE
;
684 q
->q_match_begin
= q
->q_match_end
= key_count
;
685 q
->q_item
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
686 q
->q_crypto_op
= kSecKsUnwrap
;
691 /* Parse query for a Query object q. */
692 static bool query_parse_with_applier(Query
*q
, CFDictionaryRef query
,
693 CFDictionaryApplierFunction applier
,
695 CFDictionaryApplyFunction(query
, applier
, q
);
696 return query_error(q
, error
);
699 /* Parse query for a Query object q. */
700 static bool query_parse(Query
*q
, CFDictionaryRef query
,
702 return query_parse_with_applier(q
, query
, query_applier
, error
);
705 /* Parse query for a Query object q. */
706 bool query_update_parse(Query
*q
, CFDictionaryRef update
,
708 return query_parse_with_applier(q
, update
, query_update_applier
, error
);
711 Query
*query_create_with_limit(CFDictionaryRef query
, CFIndex limit
,
714 q
= query_create(query_get_class(query
, error
), query
, error
);
717 if (!query_parse(q
, query
, error
)) {
718 query_destroy(q
, error
);
721 if (!q
->q_sync
&& !q
->q_row_id
) {
722 /* query did not specify a kSecAttrSynchronizable attribute,
723 * and did not contain a persistent reference. */
724 query_add_attribute(kSecAttrSynchronizable
, kCFBooleanFalse
, q
);
731 //TODO: Move this to SecDbItemRef
733 /* Make sure all attributes that are marked as not_null have a value. If
734 force_date is false, only set mdat and cdat if they aren't already set. */
736 query_pre_add(Query
*q
, bool force_date
) {
737 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
738 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInFlag
) {
739 if (desc
->kind
== kSecDbCreationDateAttr
||
740 desc
->kind
== kSecDbModificationDateAttr
) {
742 query_set_attribute_with_desc(desc
, now
, q
);
743 } else if (!CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
744 query_add_attribute_with_desc(desc
, now
, q
);
746 } else if ((desc
->flags
& kSecDbNotNullFlag
) &&
747 !CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
748 CFTypeRef value
= NULL
;
749 if (desc
->flags
& kSecDbDefault0Flag
) {
750 if (desc
->kind
== kSecDbDateAttr
)
751 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
754 value
= CFNumberCreate(0, kCFNumberSInt32Type
, &vzero
);
756 } else if (desc
->flags
& kSecDbDefaultEmptyFlag
) {
757 if (desc
->kind
== kSecDbDataAttr
)
758 value
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
765 /* Safe to use query_add_attribute here since the attr wasn't
767 query_add_attribute_with_desc(desc
, value
, q
);
775 //TODO: Move this to SecDbItemRef
777 /* Update modification_date if needed. */
779 query_pre_update(Query
*q
) {
780 SecDbForEachAttr(q
->q_class
, desc
) {
781 if (desc
->kind
== kSecDbModificationDateAttr
) {
782 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
783 query_set_attribute_with_desc(desc
, now
, q
);
790 query_set_caller_access_groups(Query
*q
, CFArrayRef caller_access_groups
) {
791 CFRetainAssign(q
->q_caller_access_groups
, caller_access_groups
);
795 query_needs_authentication(Query
*q
) {
796 return q
->q_required_access_controls
&& CFArrayGetCount(q
->q_required_access_controls
) > 0;
800 query_enable_interactive(Query
*q
) {
801 CFAssignRetained(q
->q_required_access_controls
, CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
));
805 query_authenticate(Query
*q
, CFErrorRef
**error
) {
807 CFMutableDictionaryRef hints
= NULL
;
809 if (q
->q_use_no_authentication_ui
&& CFBooleanGetValue(q
->q_use_no_authentication_ui
)) {
810 SecError(errSecInteractionNotAllowed
, *error
, CFSTR("authentication UI is not allowed"));
813 if (isString(q
->q_use_operation_prompt
)) {
815 hints
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
818 CFNumberRef reasonKey
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, kLAOptionAuthenticationReason
);
819 CFDictionaryAddValue(hints
, reasonKey
, q
->q_use_operation_prompt
);
820 CFRelease(reasonKey
);
824 CFReleaseNull(**error
);
826 CFErrorRef authError
= NULL
;
828 CFArrayForEachC(q
->q_required_access_controls
, ac_data
) {
829 ok
= VREvaluateACL(q
->q_use_cred_handle
, ac_data
, kAKSKeyOpDecrypt
, hints
, &authError
);
831 ok
= SecCFCreateError(errSecAuthFailed
, kSecErrorDomain
, CFSTR("LocalAuthentication failed"), authError
, *error
);
832 CFReleaseSafe(authError
);
836 CFArrayRemoveAllValues(q
->q_required_access_controls
);
840 CFReleaseSafe(hints
);