]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/SecDbQuery.c
Security-57337.20.44.tar.gz
[apple/security.git] / OSX / 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 CFTypeRef protection = SecAccessControlGetProtection(q->q_access_control);
123 if (!protection) {
124 SecError(errSecParam, &q->q_error, CFSTR("kSecAccessControl missing protection"));
125 return;
126 }
127 CFDictionarySetValue(q->q_item, kSecAttrAccessible, protection);
128 }
129 }
130
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.
134 */
135 void query_add_attribute_with_desc(const SecDbAttr *desc, const void *value, Query *q)
136 {
137 if (CFEqual(desc->name, kSecAttrSynchronizable)) {
138 q->q_sync = true;
139 if (CFEqual(value, kSecAttrSynchronizableAny))
140 return; /* skip the attribute so it isn't part of the search */
141 }
142
143 CFTypeRef attr = NULL;
144 switch (desc->kind) {
145 case kSecDbDataAttr:
146 attr = copyData(value);
147 break;
148 case kSecDbBlobAttr:
149 case kSecDbAccessControlAttr:
150 attr = copyBlob(value);
151 break;
152 case kSecDbDateAttr:
153 case kSecDbCreationDateAttr:
154 case kSecDbModificationDateAttr:
155 attr = copyDate(value);
156 break;
157 case kSecDbNumberAttr:
158 case kSecDbSyncAttr:
159 case kSecDbTombAttr:
160 attr = copyNumber(value);
161 break;
162 case kSecDbAccessAttr:
163 case kSecDbStringAttr:
164 attr = copyString(value);
165 break;
166 case kSecDbSHA1Attr:
167 attr = copySHA1(value);
168 break;
169 case kSecDbRowIdAttr:
170 case kSecDbPrimaryKeyAttr:
171 case kSecDbEncryptedDataAttr:
172 case kSecDbUTombAttr:
173 break;
174 }
175
176 if (!attr) {
177 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("attribute %@: value: %@ failed to convert"), desc->name, value);
178 return;
179 }
180
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);
184 }
185
186 /* Convert attr to (sha1) digest if requested. */
187 if (desc->flags & kSecDbSHA1ValueInFlag) {
188 CFDataRef data = copyData(attr);
189 CFRelease(attr);
190 if (!data) {
191 SecError(errSecInternal, &q->q_error, CFSTR("failed to get attribute %@ data"), desc->name);
192 return;
193 }
194
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));
201 CFRelease(data);
202 attr = digest;
203 }
204
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;
209 } else {
210 CFReleaseSafe(attr);
211 }
212 }
213
214 void query_add_attribute(const void *key, const void *value, Query *q)
215 {
216 const SecDbAttr *desc = SecDbAttrWithKey(q->q_class, key, &q->q_error);
217 if (desc) {
218 query_add_attribute_with_desc(desc, value, q);
219
220 if (desc->kind == kSecDbAccessControlAttr) {
221 CFDataRef attr = (CFDataRef)CFDictionaryGetValue(q->q_item, desc->name);
222 if (attr) {
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);
227 }
228 }
229 }
230
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);
235 if (attr) {
236 if (SecAccessControlSetProtection(access_control, attr, &q->q_error))
237 query_set_access_control(q, access_control);
238 }
239 CFRelease(access_control);
240 }
241 }
242 }
243 }
244
245 void query_add_or_attribute(const void *key, const void *value, Query *q)
246 {
247 const SecDbAttr *desc = SecDbAttrWithKey(q->q_class, key, &q->q_error);
248 if (desc) {
249 CFTypeRef oldValue = CFDictionaryGetValue(q->q_item, desc->name);
250 CFMutableArrayRef array = NULL;
251 if (oldValue) {
252 if (isArray(oldValue)) {
253 array = (CFMutableArrayRef)CFRetain(oldValue);
254 } else {
255 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
256 CFArrayAppendValue(array, oldValue);
257 }
258 CFDictionaryRemoveValue(q->q_item, desc->name);
259 } else {
260 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
261 }
262 if (array) {
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);
267 CFRelease(array);
268 }
269 }
270 }
271
272 void query_add_not_attribute(const void *key, const void *value, Query *q)
273 {
274 const SecDbAttr *desc = SecDbAttrWithKey(q->q_class, key, &q->q_error);
275 if (desc) {
276 CFTypeRef oldValue = CFDictionaryGetValue(q->q_item, desc->name);
277 CFMutableArrayRef array = NULL;
278 if (oldValue) {
279 if (isArray(oldValue)) {
280 array = (CFMutableArrayRef)CFRetain(oldValue);
281 } else {
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);
287 }
288 CFDictionaryRemoveValue(q->q_item, desc->name);
289 } else {
290 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
291 CFArrayAppendValue(array, kCFNull);
292 }
293 if (array) {
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);
298 CFRelease(array);
299 }
300 }
301 }
302
303
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.
307 */
308 static void query_add_match(const void *key, const void *value, Query *q)
309 {
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;
314
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)) {
323 q->q_limit = 1;
324 } else {
325 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("unsupported match limit %@"), value);
326 }
327 } else if (CFEqual(kSecMatchIssuers, key) &&
328 (CFGetTypeID(value) == CFArrayGetTypeID()))
329 {
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);
341 }
342 }
343
344 if (CFArrayGetCount(canonical_issuers) > 0) {
345 q->q_match_issuer = canonical_issuers;
346 } else
347 CFRelease(canonical_issuers);
348 }
349 }
350 }
351
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)) {
357 q->q_class = value;
358 return true;
359 }
360
361 if (error && !*error)
362 SecError((c_name ? errSecNoSuchClass : errSecItemClassMissing), error, CFSTR("can find class named: %@"), c_name);
363
364
365 return false;
366 }
367
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)) {
372 c_name = value;
373 } else {
374 value = CFDictionaryGetValue(query, kSecValuePersistentRef);
375 if (isData(value)) {
376 CFDataRef pref = value;
377 _SecItemParsePersistentRef(pref, &c_name, 0);
378 }
379 }
380
381 if (c_name && (value = kc_class_with_name(c_name))) {
382 return value;
383 } else {
384 if (c_name)
385 SecError(errSecNoSuchClass, error, CFSTR("can't find class named: %@"), c_name);
386 else
387 SecError(errSecItemClassMissing, error, CFSTR("query missing class name"));
388 return NULL;
389 }
390 }
391
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.
395 */
396 static void query_add_class(const void *key, const void *value, Query *q)
397 {
398 if (CFEqual(key, kSecClass)) {
399 query_set_class(q, value, &q->q_error);
400 } else {
401 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("add_class: key %@ is not %@"), key, kSecClass);
402 }
403 }
404
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.
408 */
409 static void query_add_return(const void *key, const void *value, Query *q)
410 {
411 ReturnTypeMask mask;
412 if (CFGetTypeID(value) != CFBooleanGetTypeID()) {
413 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_return: value %@ is not CFBoolean"), value);
414 return;
415 }
416
417 int set_it = CFEqual(value, kCFBooleanTrue);
418
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;
427 else {
428 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("add_return: unknown key %@"), key);
429 return;
430 }
431
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) {
436 /* Set this bit. */
437 q->q_return_type |= mask;
438 }
439 }
440
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
444 NULL CFTypeRef.
445 */
446 static void query_add_use(const void *key, const void *value, Query *q)
447 {
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;
458 } else {
459 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_use: value %@ for key %@ is neither CFBoolean nor CFNumber"), value, key);
460 return;
461 }
462 } else if (CFEqual(key, kSecUseCredentialReference)) {
463 if (isData(value)) {
464 CFRetainAssign(q->q_use_cred_handle, value);
465 } else {
466 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_use: value %@ for key %@ is not CFData"), value, key);
467 return;
468 }
469 } else if (CFEqual(key, kSecUseAuthenticationUI)) {
470 if (isString(value)) {
471 q->q_skip_acl_items = CFEqualSafe(kSecUseAuthenticationUISkip, value);
472 } else {
473 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_use: value %@ for key %@ is not CFString"), value, key);
474 return;
475 }
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) */
482 } else {
483 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("add_use: unknown key %@"), key);
484 return;
485 }
486 }
487
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);
491 } else {
492 q->q_data = value;
493 if (q->q_item)
494 CFDictionarySetValue(q->q_item, kSecValueData, value);
495 }
496 }
497
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.
501 */
502 static void query_add_value(const void *key, const void *value, Query *q)
503 {
504 if (CFEqual(key, kSecValueData)) {
505 query_set_data(value, q);
506 #ifdef NO_SERVER
507 } else if (CFEqual(key, kSecValueRef)) {
508 q->q_ref = value;
509 /* TODO: Add value type sanity checking. */
510 #endif
511 } else if (CFEqual(key, kSecValuePersistentRef)) {
512 CFStringRef c_name;
513 if (_SecItemParsePersistentRef(value, &c_name, &q->q_row_id))
514 query_set_class(q, c_name, &q->q_error);
515 else
516 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_value: value %@ is not a valid persitent ref"), value);
517 } else {
518 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("add_value: unknown key %@"), key);
519 return;
520 }
521 }
522
523 /* AUDIT[securityd](done):
524 key (ok) is a caller provided, unchecked.
525 value (ok) is a caller provided, unchecked.
526 */
527 static void query_update_applier(const void *key, const void *value,
528 void *context)
529 {
530 Query *q = (Query *)context;
531 /* If something went wrong there is no point processing any more args. */
532 if (q->q_error)
533 return;
534
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);
538 return;
539 }
540
541 if (!value) {
542 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("update_applier: key %@ has NULL value"), key);
543 return;
544 }
545
546 if (CFEqual(key, kSecValueData)) {
547 query_set_data(value, q);
548 } else {
549 query_add_attribute(key, value, q);
550 }
551 }
552
553 /* AUDIT[securityd](done):
554 key (ok) is a caller provided, unchecked.
555 value (ok) is a caller provided, unchecked.
556 */
557 static void query_applier(const void *key, const void *value, void *context)
558 {
559 Query *q = (Query *)context;
560 /* If something went wrong there is no point processing any more args. */
561 if (q->q_error)
562 return;
563
564 /* Make sure we have a key. */
565 if (!key) {
566 SecError(errSecItemInvalidKeyType, &q->q_error, CFSTR("applier: NULL key"));
567 return;
568 }
569
570 /* Make sure we have a value. */
571 if (!value) {
572 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("applier: key %@ has NULL value"), key);
573 return;
574 }
575
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
582 determines the type:
583 c: class must be kSecClass
584 m: match like kSecMatchPolicy
585 r: return like kSecReturnData
586 u: use keys
587 v: value
588 */
589 if (key_len == 4) {
590 /* attributes */
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)
595 {
596 case 'c': /* class */
597 query_add_class(key, value, q);
598 break;
599 case 'm': /* match */
600 query_add_match(key, value, q);
601 break;
602 case 'r': /* return */
603 query_add_return(key, value, q);
604 break;
605 case 'u': /* use */
606 query_add_use(key, value, q);
607 break;
608 case 'v': /* value */
609 query_add_value(key, value, q);
610 break;
611 default:
612 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("applier: key %@ invalid"), key);
613 break;
614 }
615 } else {
616 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("applier: key %@ invalid length"), key);
617 }
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);
622 } else {
623 /* We only support string and number type keys. */
624 SecError(errSecItemInvalidKeyType, &q->q_error, CFSTR("applier: key %@ neither string nor number"), key);
625 }
626 }
627
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;
633 }
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;
638 }
639 /* The rest defaults to ak. */
640 return kSecAttrAccessibleWhenUnlocked;
641 }
642
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);
647 }
648 }
649
650 bool query_error(Query *q, CFErrorRef *error) {
651 CFErrorRef tmp = q->q_error;
652 q->q_error = NULL;
653 return SecErrorPropagate(tmp, error);
654 }
655
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);
661 }
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);
668
669 free(q);
670 return ok;
671 }
672
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);
676 }
677 return query_destroy(q, error) && ok;
678 }
679
680 /* Allocate and initialize a Query object for query. */
681 Query *query_create(const SecDbClass *qclass, CFDictionaryRef query,
682 CFErrorRef *error)
683 {
684 if (!qclass) {
685 if (error && !*error)
686 SecError(errSecItemClassMissing, error, CFSTR("Missing class"));
687 return NULL;
688 }
689
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);
697 }
698
699 if (query) {
700 key_count += CFDictionaryGetCount(query);
701 SecDbForEachAttr(qclass, attr) {
702 if (CFDictionaryContainsKey(query, attr->name))
703 --key_count;
704 }
705 }
706
707 if (key_count > QUERY_KEY_LIMIT) {
708 if (error && !*error)
709 {
710 secerror("key_count: %ld, QUERY_KEY_LIMIT: %d", (long)key_count, QUERY_KEY_LIMIT);
711 SecError(errSecItemIllegalQuery, error, CFSTR("Past query key limit"));
712 }
713 return NULL;
714 }
715
716 Query *q = calloc(1, sizeof(Query) + sizeof(Pair) * key_count);
717 if (q == NULL) {
718 if (error && !*error)
719 SecError(errSecAllocate, error, CFSTR("Out of memory"));
720 return NULL;
721 }
722
723 q->q_keybag = KEYBAG_DEVICE;
724 q->q_class = qclass;
725 q->q_match_begin = q->q_match_end = key_count;
726 q->q_item = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
727
728 return q;
729 }
730
731 /* Parse query for a Query object q. */
732 static bool query_parse_with_applier(Query *q, CFDictionaryRef query,
733 CFDictionaryApplierFunction applier,
734 CFErrorRef *error) {
735 CFDictionaryApplyFunction(query, applier, q);
736 return query_error(q, error);
737 }
738
739 /* Parse query for a Query object q. */
740 static bool query_parse(Query *q, CFDictionaryRef query,
741 CFErrorRef *error) {
742 return query_parse_with_applier(q, query, query_applier, error);
743 }
744
745 /* Parse query for a Query object q. */
746 bool query_update_parse(Query *q, CFDictionaryRef update,
747 CFErrorRef *error) {
748 return query_parse_with_applier(q, update, query_update_applier, error);
749 }
750
751 Query *query_create_with_limit(CFDictionaryRef query, CFIndex limit, CFErrorRef *error) {
752 Query *q;
753 q = query_create(query_get_class(query, error), query, error);
754 if (q) {
755 q->q_limit = limit;
756 if (!query_parse(q, query, error)) {
757 query_destroy(q, error);
758 return NULL;
759 }
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);
764 }
765 }
766 return q;
767 }
768
769
770 void
771 query_set_caller_access_groups(Query *q, CFArrayRef caller_access_groups) {
772 CFRetainAssign(q->q_caller_access_groups, caller_access_groups);
773 }