]> git.saurik.com Git - apple/security.git/blob - Security/sec/securityd/SecDbQuery.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / sec / securityd / SecDbQuery.c
1 /*
2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * SecDbQuery.c - CoreFoundation-based constants and functions for
26 access to Security items (certificates, keys, identities, and
27 passwords.)
28 */
29
30 #include <securityd/SecDbQuery.h>
31
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>
45
46 #include <pthread/pthread.h>
47
48 #if USE_KEYSTORE
49 #include <LocalAuthentication/LAPublicDefines.h>
50 #include <coreauthd_spi.h>
51 #include <libaks_acl_cf_keys.h>
52 #endif
53
54 /* Upper limit for number of keys in a QUERY dictionary. */
55 #define QUERY_KEY_LIMIT_BASE (128)
56 #ifdef NO_SERVER
57 #define QUERY_KEY_LIMIT (31 + QUERY_KEY_LIMIT_BASE)
58 #else
59 #define QUERY_KEY_LIMIT QUERY_KEY_LIMIT_BASE
60 #endif
61
62 /* Inline accessors to attr and match values in a query. */
63 CFIndex query_attr_count(const Query *q)
64 {
65 return q->q_attr_end;
66 }
67
68 Pair query_attr_at(const Query *q, CFIndex ix)
69 {
70 return q->q_pairs[ix];
71 }
72
73 CFIndex query_match_count(const Query *q)
74 {
75 return q->q_match_end - q->q_match_begin;
76 }
77
78 __unused static inline Pair query_match_at(const Query *q, CFIndex ix)
79 {
80 return q->q_pairs[q->q_match_begin + ix];
81 }
82
83 /* Private routines used to parse a query. */
84
85 const SecDbClass *kc_class_with_name(CFStringRef name) {
86 if (isString(name)) {
87 #if 0
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[] = {
91 &genp_class,
92 &inet_class,
93 &cert_class,
94 &keys_class,
95 &identity_class
96 };
97 #endif
98 if (CFEqual(name, kSecClassGenericPassword))
99 return &genp_class;
100 else if (CFEqual(name, kSecClassInternetPassword))
101 return &inet_class;
102 else if (CFEqual(name, kSecClassCertificate))
103 return &cert_class;
104 else if (CFEqual(name, kSecClassKey))
105 return &keys_class;
106 else if (CFEqual(name, kSecClassIdentity))
107 return &identity_class;
108 }
109 return NULL;
110 }
111
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"));
116 }
117 } else {
118 /* Store access control virtual attribute. */
119 q->q_access_control = (SecAccessControlRef)CFRetain(access_control);
120
121 /* Also set legacy access attribute. */
122 CFDictionarySetValue(q->q_item, kSecAttrAccessible, SecAccessControlGetProtection(q->q_access_control));
123 }
124 }
125
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.
129 */
130 static void query_add_attribute_with_desc(const SecDbAttr *desc, const void *value, Query *q)
131 {
132 if (CFEqual(desc->name, kSecAttrSynchronizable)) {
133 q->q_sync = true;
134 if (CFEqual(value, kSecAttrSynchronizableAny))
135 return; /* skip the attribute so it isn't part of the search */
136 }
137
138 CFTypeRef attr = NULL;
139 switch (desc->kind) {
140 case kSecDbDataAttr:
141 attr = copyData(value);
142 break;
143 case kSecDbBlobAttr:
144 case kSecDbAccessControlAttr:
145 attr = copyBlob(value);
146 break;
147 case kSecDbDateAttr:
148 case kSecDbCreationDateAttr:
149 case kSecDbModificationDateAttr:
150 attr = copyDate(value);
151 break;
152 case kSecDbNumberAttr:
153 case kSecDbSyncAttr:
154 case kSecDbTombAttr:
155 attr = copyNumber(value);
156 break;
157 case kSecDbAccessAttr:
158 case kSecDbStringAttr:
159 attr = copyString(value);
160 break;
161 case kSecDbSHA1Attr:
162 attr = copySHA1(value);
163 break;
164 case kSecDbRowIdAttr:
165 case kSecDbPrimaryKeyAttr:
166 case kSecDbEncryptedDataAttr:
167 break;
168 }
169
170 if (!attr) {
171 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("attribute %@: value: %@ failed to convert"), desc->name, value);
172 return;
173 }
174
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);
178 }
179
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);
185 }
186 }
187
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);
193 }
194 }
195
196 /* Convert attr to (sha1) digest if requested. */
197 if (desc->flags & kSecDbSHA1ValueInFlag) {
198 CFDataRef data = copyData(attr);
199 CFRelease(attr);
200 if (!data) {
201 SecError(errSecInternal, &q->q_error, CFSTR("failed to get attribute %@ data"), desc->name);
202 return;
203 }
204
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));
211 CFRelease(data);
212 attr = digest;
213 }
214
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;
219 } else {
220 CFReleaseSafe(attr);
221 }
222 }
223
224 void query_add_attribute(const void *key, const void *value, Query *q)
225 {
226 const SecDbAttr *desc = SecDbAttrWithKey(q->q_class, key, &q->q_error);
227 if (desc)
228 query_add_attribute_with_desc(desc, value, q);
229 }
230
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)) {
234 CFIndex ix;
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);
238 --q->q_attr_end;
239 for (; ix < q->q_attr_end; ++ix) {
240 q->q_pairs[ix] = q->q_pairs[ix + 1];
241 }
242 CFDictionaryRemoveValue(q->q_item, desc->name);
243 break;
244 }
245 }
246 }
247 query_add_attribute_with_desc(desc, value, q);
248 }
249
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.
253 */
254 static void query_add_match(const void *key, const void *value, Query *q)
255 {
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;
260
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)) {
269 q->q_limit = 1;
270 } else {
271 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("unsupported match limit %@"), value);
272 }
273 } else if (CFEqual(kSecMatchIssuers, key) &&
274 (CFGetTypeID(value) == CFArrayGetTypeID()))
275 {
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);
287 }
288 }
289
290 if (CFArrayGetCount(canonical_issuers) > 0) {
291 q->q_match_issuer = canonical_issuers;
292 } else
293 CFRelease(canonical_issuers);
294 }
295 }
296 }
297
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)) {
303 q->q_class = value;
304 return true;
305 }
306
307 if (error && !*error)
308 SecError((c_name ? errSecNoSuchClass : errSecItemClassMissing), error, CFSTR("can find class named: %@"), c_name);
309
310
311 return false;
312 }
313
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)) {
318 c_name = value;
319 } else {
320 value = CFDictionaryGetValue(query, kSecValuePersistentRef);
321 if (isData(value)) {
322 CFDataRef pref = value;
323 _SecItemParsePersistentRef(pref, &c_name, 0);
324 }
325 }
326
327 if (c_name && (value = kc_class_with_name(c_name))) {
328 return value;
329 } else {
330 if (c_name)
331 SecError(errSecNoSuchClass, error, CFSTR("can't find class named: %@"), c_name);
332 else
333 SecError(errSecItemClassMissing, error, CFSTR("query missing class name"));
334 return NULL;
335 }
336 }
337
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.
341 */
342 static void query_add_class(const void *key, const void *value, Query *q)
343 {
344 if (CFEqual(key, kSecClass)) {
345 query_set_class(q, value, &q->q_error);
346 } else {
347 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("add_class: key %@ is not %@"), key, kSecClass);
348 }
349 }
350
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.
354 */
355 static void query_add_return(const void *key, const void *value, Query *q)
356 {
357 ReturnTypeMask mask;
358 if (CFGetTypeID(value) != CFBooleanGetTypeID()) {
359 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_return: value %@ is not CFBoolean"), value);
360 return;
361 }
362
363 int set_it = CFEqual(value, kCFBooleanTrue);
364
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;
373 else {
374 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("add_return: unknown key %@"), key);
375 return;
376 }
377
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) {
382 /* Set this bit. */
383 q->q_return_type |= mask;
384 }
385 }
386
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
390 NULL CFTypeRef.
391 */
392 static void query_add_use(const void *key, const void *value, Query *q)
393 {
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;
404 } else {
405 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_use: value %@ for key %@ is neither CFBoolean nor CFNumber"), value, key);
406 return;
407 }
408 } else if (CFEqual(key, kSecUseCredentialReference)) {
409 if (isData(value)) {
410 CFRetainAssign(q->q_use_cred_handle, value);
411 } else {
412 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_use: value %@ for key %@ is not CFData"), value, key);
413 return;
414 }
415 } else if (CFEqual(key, kSecUseOperationPrompt)) {
416 if (isString(value)) {
417 CFRetainAssign(q->q_use_operation_prompt, value);
418 } else {
419 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_use: value %@ for key %@ is not CFString"), value, key);
420 return;
421 }
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;
429 } else {
430 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_use: value %@ for key %@ is neither CFBoolean nor CFNumber"), value, key);
431 return;
432 }
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) */
439 } else {
440 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("add_use: unknown key %@"), key);
441 return;
442 }
443 }
444
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);
448 } else {
449 q->q_data = value;
450 if (q->q_item)
451 CFDictionarySetValue(q->q_item, kSecValueData, value);
452 }
453 }
454
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.
458 */
459 static void query_add_value(const void *key, const void *value, Query *q)
460 {
461 if (CFEqual(key, kSecValueData)) {
462 query_set_data(value, q);
463 #ifdef NO_SERVER
464 } else if (CFEqual(key, kSecValueRef)) {
465 q->q_ref = value;
466 /* TODO: Add value type sanity checking. */
467 #endif
468 } else if (CFEqual(key, kSecValuePersistentRef)) {
469 CFStringRef c_name;
470 if (_SecItemParsePersistentRef(value, &c_name, &q->q_row_id))
471 query_set_class(q, c_name, &q->q_error);
472 else
473 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_value: value %@ is not a valid persitent ref"), value);
474 } else {
475 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("add_value: unknown key %@"), key);
476 return;
477 }
478 }
479
480 /* AUDIT[securityd](done):
481 key (ok) is a caller provided, unchecked.
482 value (ok) is a caller provided, unchecked.
483 */
484 static void query_update_applier(const void *key, const void *value,
485 void *context)
486 {
487 Query *q = (Query *)context;
488 /* If something went wrong there is no point processing any more args. */
489 if (q->q_error)
490 return;
491
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);
495 return;
496 }
497
498 if (!value) {
499 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("update_applier: key %@ has NULL value"), key);
500 return;
501 }
502
503 if (CFEqual(key, kSecValueData)) {
504 query_set_data(value, q);
505 } else {
506 query_add_attribute(key, value, q);
507 }
508 }
509
510 /* AUDIT[securityd](done):
511 key (ok) is a caller provided, unchecked.
512 value (ok) is a caller provided, unchecked.
513 */
514 static void query_applier(const void *key, const void *value, void *context)
515 {
516 Query *q = (Query *)context;
517 /* If something went wrong there is no point processing any more args. */
518 if (q->q_error)
519 return;
520
521 /* Make sure we have a key. */
522 if (!key) {
523 SecError(errSecItemInvalidKeyType, &q->q_error, CFSTR("applier: NULL key"));
524 return;
525 }
526
527 /* Make sure we have a value. */
528 if (!value) {
529 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("applier: key %@ has NULL value"), key);
530 return;
531 }
532
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
539 determines the type:
540 c: class must be kSecClass
541 m: match like kSecMatchPolicy
542 r: return like kSecReturnData
543 u: use keys
544 v: value
545 */
546 if (key_len == 4) {
547 /* attributes */
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)
552 {
553 case 'c': /* class */
554 query_add_class(key, value, q);
555 break;
556 case 'm': /* match */
557 query_add_match(key, value, q);
558 break;
559 case 'r': /* return */
560 query_add_return(key, value, q);
561 break;
562 case 'u': /* use */
563 query_add_use(key, value, q);
564 break;
565 case 'v': /* value */
566 query_add_value(key, value, q);
567 break;
568 default:
569 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("applier: key %@ invalid"), key);
570 break;
571 }
572 } else {
573 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("applier: key %@ invalid length"), key);
574 }
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);
579 } else {
580 /* We only support string and number type keys. */
581 SecError(errSecItemInvalidKeyType, &q->q_error, CFSTR("applier: key %@ neither string nor number"), key);
582 }
583 }
584
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;
590 }
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;
595 }
596 /* The rest defaults to ak. */
597 return kSecAttrAccessibleWhenUnlocked;
598 }
599
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);
604 }
605 }
606
607 bool query_error(Query *q, CFErrorRef *error) {
608 CFErrorRef tmp = q->q_error;
609 q->q_error = NULL;
610 return CFErrorPropagate(tmp, error);
611 }
612
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);
618 }
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);
627
628 free(q);
629 return ok;
630 }
631
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);
635 }
636 return query_destroy(q, error) && ok;
637 }
638
639 /* Allocate and initialize a Query object for query. */
640 Query *query_create(const SecDbClass *qclass, CFDictionaryRef query,
641 CFErrorRef *error)
642 {
643 if (!qclass) {
644 if (error && !*error)
645 SecError(errSecItemClassMissing, error, CFSTR("Missing class"));
646 return NULL;
647 }
648
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);
656 }
657
658 if (query) {
659 key_count += CFDictionaryGetCount(query);
660 SecDbForEachAttr(qclass, attr) {
661 if (CFDictionaryContainsKey(query, attr->name))
662 --key_count;
663 }
664 }
665
666 if (key_count > QUERY_KEY_LIMIT) {
667 if (error && !*error)
668 {
669 secerror("key_count: %ld, QUERY_KEY_LIMIT: %d", (long)key_count, QUERY_KEY_LIMIT);
670 SecError(errSecItemIllegalQuery, error, CFSTR("Past query key limit"));
671 }
672 return NULL;
673 }
674
675 Query *q = calloc(1, sizeof(Query) + sizeof(Pair) * key_count);
676 if (q == NULL) {
677 if (error && !*error)
678 SecError(errSecAllocate, error, CFSTR("Out of memory"));
679 return NULL;
680 }
681
682 q->q_keybag = KEYBAG_DEVICE;
683 q->q_class = qclass;
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;
687
688 return q;
689 }
690
691 /* Parse query for a Query object q. */
692 static bool query_parse_with_applier(Query *q, CFDictionaryRef query,
693 CFDictionaryApplierFunction applier,
694 CFErrorRef *error) {
695 CFDictionaryApplyFunction(query, applier, q);
696 return query_error(q, error);
697 }
698
699 /* Parse query for a Query object q. */
700 static bool query_parse(Query *q, CFDictionaryRef query,
701 CFErrorRef *error) {
702 return query_parse_with_applier(q, query, query_applier, error);
703 }
704
705 /* Parse query for a Query object q. */
706 bool query_update_parse(Query *q, CFDictionaryRef update,
707 CFErrorRef *error) {
708 return query_parse_with_applier(q, update, query_update_applier, error);
709 }
710
711 Query *query_create_with_limit(CFDictionaryRef query, CFIndex limit,
712 CFErrorRef *error) {
713 Query *q;
714 q = query_create(query_get_class(query, error), query, error);
715 if (q) {
716 q->q_limit = limit;
717 if (!query_parse(q, query, error)) {
718 query_destroy(q, error);
719 return NULL;
720 }
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);
725 }
726 }
727 return q;
728 }
729
730
731 //TODO: Move this to SecDbItemRef
732
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. */
735 void
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) {
741 if (force_date) {
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);
745 }
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);
752 else {
753 SInt32 vzero = 0;
754 value = CFNumberCreate(0, kCFNumberSInt32Type, &vzero);
755 }
756 } else if (desc->flags & kSecDbDefaultEmptyFlag) {
757 if (desc->kind == kSecDbDataAttr)
758 value = CFDataCreate(kCFAllocatorDefault, NULL, 0);
759 else {
760 value = CFSTR("");
761 CFRetain(value);
762 }
763 }
764 if (value) {
765 /* Safe to use query_add_attribute here since the attr wasn't
766 set yet. */
767 query_add_attribute_with_desc(desc, value, q);
768 CFRelease(value);
769 }
770 }
771 }
772 CFReleaseSafe(now);
773 }
774
775 //TODO: Move this to SecDbItemRef
776
777 /* Update modification_date if needed. */
778 void
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);
784 CFReleaseSafe(now);
785 }
786 }
787 }
788
789 void
790 query_set_caller_access_groups(Query *q, CFArrayRef caller_access_groups) {
791 CFRetainAssign(q->q_caller_access_groups, caller_access_groups);
792 }
793
794 bool
795 query_needs_authentication(Query *q) {
796 return q->q_required_access_controls && CFArrayGetCount(q->q_required_access_controls) > 0;
797 }
798
799 void
800 query_enable_interactive(Query *q) {
801 CFAssignRetained(q->q_required_access_controls, CFArrayCreateMutableForCFTypes(kCFAllocatorDefault));
802 }
803
804 bool
805 query_authenticate(Query *q, CFErrorRef **error) {
806 bool ok = true;
807 CFMutableDictionaryRef hints = NULL;
808 #if USE_KEYSTORE
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"));
811 return false;
812 }
813 if (isString(q->q_use_operation_prompt)) {
814 if (!hints) {
815 hints = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
816 }
817 // VRHintAppAction
818 CFNumberRef reasonKey = CFNumberCreateWithCFIndex(kCFAllocatorDefault, kLAOptionAuthenticationReason);
819 CFDictionaryAddValue(hints, reasonKey, q->q_use_operation_prompt);
820 CFRelease(reasonKey);
821 }
822
823 if (*error)
824 CFReleaseNull(**error);
825
826 CFErrorRef authError = NULL;
827 CFDataRef ac_data;
828 CFArrayForEachC(q->q_required_access_controls, ac_data) {
829 ok = VREvaluateACL(q->q_use_cred_handle, ac_data, kAKSKeyOpDecrypt, hints, &authError);
830 if (!ok) {
831 ok = SecCFCreateError(errSecAuthFailed, kSecErrorDomain, CFSTR("LocalAuthentication failed"), authError, *error);
832 CFReleaseSafe(authError);
833 goto out;
834 }
835 }
836 CFArrayRemoveAllValues(q->q_required_access_controls);
837
838 out:
839 #endif
840 CFReleaseSafe(hints);
841 return ok;
842 }