]> git.saurik.com Git - apple/security.git/blob - keychain/securityd/SecDbQuery.c
Security-59754.41.1.tar.gz
[apple/security.git] / keychain / securityd / SecDbQuery.c
1
2 /*
3 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 /*
26 * SecDbQuery.c - CoreFoundation-based constants and functions for
27 access to Security items (certificates, keys, identities, and
28 passwords.)
29 */
30
31 #include "keychain/securityd/SecDbQuery.h"
32
33 #include "keychain/securityd/SecItemDb.h"
34 #include "keychain/securityd/SecItemSchema.h"
35 #include "keychain/securityd/SecItemServer.h"
36 #include "keychain/securityd/spi.h"
37 #include <Security/SecBasePriv.h>
38 #include <Security/SecCertificateInternal.h>
39 #include <Security/SecItem.h>
40 #include <Security/SecItemPriv.h>
41 #include <Security/SecItemInternal.h>
42 #include <Security/SecAccessControl.h>
43 #include <Security/SecAccessControlPriv.h>
44 #include <Security/SecPolicyPriv.h>
45 #include <Security/SecuritydXPC.h>
46 #include <CommonCrypto/CommonDigest.h>
47 #include <CommonCrypto/CommonDigestSPI.h>
48
49 #include <pthread/pthread.h>
50
51 #if USE_KEYSTORE
52 #include <LocalAuthentication/LAPublicDefines.h>
53 #include <coreauthd_spi.h>
54 #include <libaks_acl_cf_keys.h>
55 #endif
56
57 /* Upper limit for number of keys in a QUERY dictionary. */
58 #define QUERY_KEY_LIMIT_BASE (128)
59 #ifdef NO_SERVER
60 #define QUERY_KEY_LIMIT (31 + QUERY_KEY_LIMIT_BASE)
61 #else
62 #define QUERY_KEY_LIMIT QUERY_KEY_LIMIT_BASE
63 #endif
64
65
66 static const uint8_t systemKeychainUUID[] = "\xF6\x23\xAE\x5C\xCC\x81\x4C\xAC\x8A\xD4\xF0\x01\x3F\x31\x35\x11";
67
68 CFDataRef
69 SecMUSRCopySystemKeychainUUID(void)
70 {
71 return CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)systemKeychainUUID, 16, kCFAllocatorNull);
72 }
73
74 CFDataRef
75 SecMUSRGetSystemKeychainUUID(void)
76 {
77 static dispatch_once_t onceToken;
78 static CFDataRef systemKeychainData = NULL;
79 dispatch_once(&onceToken, ^{
80 systemKeychainData = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)systemKeychainUUID, 16, kCFAllocatorNull);
81 });
82 return systemKeychainData;
83 }
84
85 CFDataRef
86 SecMUSRGetSingleUserKeychainUUID(void)
87 {
88 static dispatch_once_t onceToken;
89 static CFDataRef singleUser = NULL;
90 dispatch_once(&onceToken, ^{
91 singleUser = CFDataCreateWithBytesNoCopy(NULL, NULL, 0, kCFAllocatorNull);
92 });
93 return singleUser;
94 }
95
96 bool
97 SecMUSRIsSingleUserView(CFDataRef musr)
98 {
99 return CFEqual(musr, SecMUSRGetSingleUserKeychainUUID());
100 }
101
102 static const uint8_t allKeychainViewsUUID[16] = "\xC8\x60\x07\xEC\x89\x62\x4D\xAF\x85\x65\x1F\xE6\x0F\x50\x5D\xB7";
103
104 CFDataRef
105 SecMUSRGetAllViews(void)
106 {
107 static dispatch_once_t onceToken;
108 static CFDataRef allKeychainViewsData = NULL;
109 dispatch_once(&onceToken, ^{
110 allKeychainViewsData = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)allKeychainViewsUUID, 16, kCFAllocatorNull);
111 });
112 return allKeychainViewsData;
113 }
114
115 bool
116 SecMUSRIsViewAllViews(CFDataRef musr)
117 {
118 return CFEqual(musr, SecMUSRGetAllViews());
119 }
120
121 #if TARGET_OS_IPHONE
122
123 CFDataRef
124 SecMUSRCreateActiveUserUUID(uid_t uid)
125 {
126 uint8_t uuid[16] = "\xA7\x5A\x3A\x35\xA5\x57\x4B\x10\xBE\x2E\x83\x94\x7E\x4A\x34\x72";
127 uint32_t num = htonl(uid);
128 memcpy(&uuid[12], &num, sizeof(num));
129 return CFDataCreate(NULL, uuid, sizeof(uuid));
130 }
131
132 CFDataRef
133 SecMUSRCreateSyncBubbleUserUUID(uid_t uid)
134 {
135 uint8_t uuid[16] = "\x82\x1A\xAB\x9F\xA3\xC8\x4E\x11\xAA\x90\x4C\xE8\x9E\xA6\xD7\xEC";
136 uint32_t num = htonl(uid);
137 memcpy(&uuid[12], &num, sizeof(num));
138 return CFDataCreate(NULL, uuid, sizeof(uuid));
139 }
140
141 static const uint8_t bothUserAndSystemUUID[12] = "\x36\xC4\xBE\x2E\x99\x0A\x46\x9A\xAC\x89\x09\xA4";
142
143
144 CFDataRef
145 SecMUSRCreateBothUserAndSystemUUID(uid_t uid)
146 {
147 uint8_t uuid[16];
148 memcpy(uuid, bothUserAndSystemUUID, 12);
149 uint32_t num = htonl(uid);
150 memcpy(&uuid[12], &num, sizeof(num));
151 return CFDataCreate(NULL, uuid, sizeof(uuid));
152 }
153
154 bool
155 SecMUSRGetBothUserAndSystemUUID(CFDataRef musr, uid_t *uid)
156 {
157 if (CFDataGetLength(musr) != 16)
158 return false;
159 const uint8_t *uuid = CFDataGetBytePtr(musr);
160 if (memcmp(uuid, bothUserAndSystemUUID, 12) != 0)
161 return false;
162 if (uid) {
163 uint32_t num;
164 memcpy(&num, &uuid[12], sizeof(num));
165 *uid = htonl(num);
166 }
167 return true;
168 }
169
170 #endif
171
172 /* Inline accessors to attr and match values in a query. */
173 CFIndex query_attr_count(const Query *q)
174 {
175 return q->q_attr_end;
176 }
177
178 Pair query_attr_at(const Query *q, CFIndex ix)
179 {
180 return q->q_pairs[ix];
181 }
182
183 CFIndex query_match_count(const Query *q)
184 {
185 return q->q_match_end - q->q_match_begin;
186 }
187
188 __unused static inline Pair query_match_at(const Query *q, CFIndex ix)
189 {
190 return q->q_pairs[q->q_match_begin + ix];
191 }
192
193 /* Private routines used to parse a query. */
194
195 const SecDbClass *kc_class_with_name(CFStringRef name) {
196 if (isString(name)) {
197 if (CFEqual(name, kSecClassGenericPassword))
198 return genp_class();
199 else if (CFEqual(name, kSecClassInternetPassword))
200 return inet_class();
201 else if (CFEqual(name, kSecClassCertificate))
202 return cert_class();
203 else if (CFEqual(name, kSecClassKey))
204 return keys_class();
205 else if (CFEqual(name, kSecClassIdentity))
206 return identity_class();
207 }
208 return NULL;
209 }
210
211 static void query_set_access_control(Query *q, SecAccessControlRef access_control) {
212 if (q->q_access_control) {
213 if (!CFEqual(q->q_access_control, access_control)) {
214 SecError(errSecItemIllegalQuery, &q->q_error, CFSTR("conflicting kSecAccess and kSecAccessControl attributes"));
215 }
216 } else {
217 /* Store access control virtual attribute. */
218 q->q_access_control = (SecAccessControlRef)CFRetain(access_control);
219
220 /* Also set legacy access attribute. */
221 CFTypeRef protection = SecAccessControlGetProtection(q->q_access_control);
222 if (!protection) {
223 SecError(errSecParam, &q->q_error, CFSTR("kSecAccessControl missing protection"));
224 return;
225 }
226 CFDictionarySetValue(q->q_item, kSecAttrAccessible, protection);
227 }
228 }
229
230 /* AUDIT[securityd](done):
231 key (ok) is a caller provided, string or number of length 4.
232 value (ok) is a caller provided, non NULL CFTypeRef.
233 */
234 void query_add_attribute_with_desc(const SecDbAttr *desc, const void *value, Query *q)
235 {
236 if (CFEqual(desc->name, kSecAttrSynchronizable)) {
237 q->q_sync = true;
238 if (CFEqual(value, kSecAttrSynchronizableAny))
239 return; /* skip the attribute so it isn't part of the search */
240 }
241
242 CFTypeRef attr = NULL;
243 switch (desc->kind) {
244 case kSecDbDataAttr:
245 attr = copyData(value);
246 break;
247 case kSecDbBlobAttr:
248 case kSecDbAccessControlAttr:
249 attr = copyBlob(value);
250 break;
251 case kSecDbDateAttr:
252 case kSecDbCreationDateAttr:
253 case kSecDbModificationDateAttr:
254 attr = copyDate(value);
255 break;
256 case kSecDbNumberAttr:
257 case kSecDbSyncAttr:
258 case kSecDbTombAttr:
259 attr = copyNumber(value);
260 break;
261 case kSecDbAccessAttr:
262 case kSecDbStringAttr:
263 attr = copyString(value);
264 break;
265 case kSecDbSHA1Attr:
266 attr = copySHA1(value);
267 break;
268 case kSecDbRowIdAttr:
269 case kSecDbPrimaryKeyAttr:
270 case kSecDbEncryptedDataAttr:
271 case kSecDbUTombAttr:
272 break;
273 case kSecDbUUIDAttr:
274 attr = copyUUID(value);
275 break;
276 }
277
278 if (!attr) {
279 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("attribute %@: value: %@ failed to convert"), desc->name, value);
280 return;
281 }
282
283 /* Store plaintext attr data in q_item unless it's a kSecDbSHA1Attr. */
284 if (q->q_item && desc->kind != kSecDbSHA1Attr) {
285 CFDictionarySetValue(q->q_item, desc->name, attr);
286 }
287
288 /* Convert attr to (sha1) digest if requested. */
289 if (desc->flags & kSecDbSHA1ValueInFlag) {
290 CFDataRef data = copyData(attr);
291 CFRelease(attr);
292 if (!data) {
293 SecError(errSecInternal, &q->q_error, CFSTR("failed to get attribute %@ data"), desc->name);
294 return;
295 }
296
297 CFMutableDataRef digest = CFDataCreateMutable(0, CC_SHA1_DIGEST_LENGTH);
298 CFDataSetLength(digest, CC_SHA1_DIGEST_LENGTH);
299 /* 64 bits cast: worst case is we generate the wrong hash */
300 assert((unsigned long)CFDataGetLength(data)<UINT32_MAX); /* Debug check. Correct as long as CFIndex is long */
301 CCDigest(kCCDigestSHA1, CFDataGetBytePtr(data), (CC_LONG)CFDataGetLength(data),
302 CFDataGetMutableBytePtr(digest));
303 CFRelease(data);
304 attr = digest;
305 }
306
307 if (desc->kind != kSecDbAccessControlAttr) {
308 /* Record the new attr key, value in q_pairs. */
309 if (q->q_attr_end + 1 < q->q_pairs_count) {
310 q->q_pairs[q->q_attr_end].key = desc->name;
311 q->q_pairs[q->q_attr_end++].value = attr;
312 } else {
313 SecError(errSecInternal, &q->q_error, CFSTR("q_pairs overflow"));
314 CFReleaseSafe(attr);
315 }
316 } else {
317 CFReleaseSafe(attr);
318 }
319 }
320
321 void query_add_attribute(const void *key, const void *value, Query *q)
322 {
323 if (CFEqual(key, kSecAttrDeriveSyncIDFromItemAttributes)) {
324 q->q_uuid_from_primary_key = CFBooleanGetValue(value);
325 return; /* skip the attribute so it isn't part of the search */
326 }
327
328 const SecDbAttr *desc = SecDbAttrWithKey(q->q_class, key, &q->q_error);
329 if (desc) {
330 query_add_attribute_with_desc(desc, value, q);
331
332 if (desc->kind == kSecDbAccessControlAttr) {
333 CFDataRef attr = (CFDataRef)CFDictionaryGetValue(q->q_item, desc->name);
334 if (attr) {
335 SecAccessControlRef access_control = SecAccessControlCreateFromData(kCFAllocatorDefault, attr, &q->q_error);
336 if (access_control) {
337 query_set_access_control(q, access_control);
338 CFRelease(access_control);
339 }
340 }
341 }
342
343 if (desc->kind == kSecDbAccessAttr) {
344 SecAccessControlRef access_control = SecAccessControlCreate(kCFAllocatorDefault, &q->q_error);
345 if (access_control) {
346 CFStringRef attr = (CFStringRef)CFDictionaryGetValue(q->q_item, desc->name);
347 if (attr) {
348 if (SecAccessControlSetProtection(access_control, attr, &q->q_error))
349 query_set_access_control(q, access_control);
350 }
351 CFRelease(access_control);
352 }
353 }
354 }
355 }
356
357 void query_add_or_attribute(const void *key, const void *value, Query *q)
358 {
359 const SecDbAttr *desc = SecDbAttrWithKey(q->q_class, key, &q->q_error);
360 if (desc) {
361 CFTypeRef oldValue = CFDictionaryGetValue(q->q_item, desc->name);
362 CFMutableArrayRef array = NULL;
363 if (oldValue) {
364 if (isArray(oldValue)) {
365 array = (CFMutableArrayRef)CFRetain(oldValue);
366 } else {
367 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
368 CFArrayAppendValue(array, oldValue);
369 }
370 CFDictionaryRemoveValue(q->q_item, desc->name);
371 } else {
372 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
373 }
374 if (array) {
375 query_add_attribute_with_desc(desc, value, q);
376 CFTypeRef newValue = CFDictionaryGetValue(q->q_item, desc->name);
377 CFArrayAppendValue(array, newValue);
378 CFDictionarySetValue(q->q_item, desc->name, array);
379 CFRelease(array);
380 }
381 }
382 }
383
384 void query_add_not_attribute(const void *key, const void *value, Query *q)
385 {
386 const SecDbAttr *desc = SecDbAttrWithKey(q->q_class, key, &q->q_error);
387 if (desc) {
388 CFTypeRef oldValue = CFDictionaryGetValue(q->q_item, desc->name);
389 CFMutableArrayRef array = NULL;
390 if (oldValue) {
391 if (isArray(oldValue)) {
392 array = (CFMutableArrayRef)CFRetain(oldValue);
393 } else {
394 // This should never run, as we shouldn't be turning a attr = value into a attr not in (value, value2)
395 secerror("negating %@ = %@ in query", desc->name, oldValue);
396 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
397 CFArrayAppendValue(array, kCFNull);
398 CFArrayAppendValue(array, oldValue);
399 }
400 CFDictionaryRemoveValue(q->q_item, desc->name);
401 } else {
402 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
403 CFArrayAppendValue(array, kCFNull);
404 }
405 if (array) {
406 query_add_attribute_with_desc(desc, value, q);
407 CFTypeRef newValue = CFDictionaryGetValue(q->q_item, desc->name);
408 CFArrayAppendValue(array, newValue);
409 CFDictionarySetValue(q->q_item, desc->name, array);
410 CFRelease(array);
411 }
412 }
413 }
414
415 static void query_add_match(const void *key, const void *value, Query *q)
416 {
417 /* Record the match key, value in q_pairs. */
418 --(q->q_match_begin);
419 q->q_pairs[q->q_match_begin].key = key;
420 q->q_pairs[q->q_match_begin].value = value;
421
422 if (CFEqual(kSecMatchLimit, key)) {
423 /* Figure out what the value for kSecMatchLimit is if specified. */
424 if (CFGetTypeID(value) == CFNumberGetTypeID()) {
425 if (!CFNumberGetValue(value, kCFNumberCFIndexType, &q->q_limit))
426 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("failed to convert match limit %@ to CFIndex"), value);
427 } else if (CFEqual(kSecMatchLimitAll, value)) {
428 q->q_limit = kSecMatchUnlimited;
429 } else if (CFEqual(kSecMatchLimitOne, value)) {
430 q->q_limit = 1;
431 } else {
432 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("unsupported match limit %@"), value);
433 }
434 } else if (CFEqual(kSecMatchIssuers, key) &&
435 (CFGetTypeID(value) == CFArrayGetTypeID()))
436 {
437 CFMutableArrayRef canonical_issuers = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
438 if (canonical_issuers) {
439 CFIndex i, count = CFArrayGetCount(value);
440 for (i = 0; i < count; i++) {
441 CFTypeRef issuer_data = CFArrayGetValueAtIndex(value, i);
442 CFDataRef issuer_canonical = NULL;
443 if (CFDataGetTypeID() == CFGetTypeID(issuer_data))
444 issuer_canonical = SecDistinguishedNameCopyNormalizedContent((CFDataRef)issuer_data);
445 if (issuer_canonical) {
446 CFArrayAppendValue(canonical_issuers, issuer_canonical);
447 CFRelease(issuer_canonical);
448 }
449 }
450
451 if (CFArrayGetCount(canonical_issuers) > 0) {
452 q->q_match_issuer = canonical_issuers;
453 } else
454 CFRelease(canonical_issuers);
455 }
456 } else if (CFEqual(kSecMatchPolicy, key)) {
457 if (CFGetTypeID(value) != CFArrayGetTypeID()) {
458 SecError(errSecParam, &q->q_error, CFSTR("unsupported value for kSecMatchPolicy attribute"));
459 return;
460 }
461 xpc_object_t policiesArrayXPC = _CFXPCCreateXPCObjectFromCFObject(value);
462 if (!policiesArrayXPC) {
463 SecError(errSecParam, &q->q_error, CFSTR("unsupported kSecMatchPolicy object in query"));
464 return;
465 }
466
467 CFArrayRef policiesArray = SecPolicyXPCArrayCopyArray(policiesArrayXPC, &q->q_error);
468 xpc_release(policiesArrayXPC);
469 if (!policiesArray)
470 return;
471
472 if (CFArrayGetCount(policiesArray) != 1 || CFGetTypeID(CFArrayGetValueAtIndex(policiesArray, 0)) != SecPolicyGetTypeID()) {
473 CFRelease(policiesArray);
474 SecError(errSecParam, &q->q_error, CFSTR("unsupported array of policies"));
475 return;
476 }
477
478 query_set_policy(q, (SecPolicyRef)CFArrayGetValueAtIndex(policiesArray, 0));
479 CFRelease(policiesArray);
480 } else if (CFEqual(kSecMatchValidOnDate, key)) {
481 if (CFGetTypeID(value) == CFNullGetTypeID()) {
482 CFDateRef date = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent());
483 query_set_valid_on_date(q, date);
484 CFRelease(date);
485 } else if (CFGetTypeID(value) == CFDateGetTypeID()) {
486 query_set_valid_on_date(q, value);
487 } else {
488 SecError(errSecParam, &q->q_error, CFSTR("unsupported value for kSecMatchValidOnDate attribute"));
489 return;
490 }
491 } else if (CFEqual(kSecMatchTrustedOnly, key)) {
492 if ((CFGetTypeID(value) == CFBooleanGetTypeID())) {
493 query_set_trusted_only(q, value);
494 } else {
495 SecError(errSecParam, &q->q_error, CFSTR("unsupported value for kSecMatchTrustedOnly attribute"));
496 return;
497 }
498 }
499 }
500
501 static bool query_set_class(Query *q, CFStringRef c_name, CFErrorRef *error) {
502 const SecDbClass *value;
503 if (c_name && CFGetTypeID(c_name) == CFStringGetTypeID() &&
504 (value = kc_class_with_name(c_name)) &&
505 (q->q_class == 0 || q->q_class == value)) {
506 q->q_class = value;
507 return true;
508 }
509
510 if (error && !*error)
511 SecError((c_name ? errSecNoSuchClass : errSecItemClassMissing), error, CFSTR("can find class named: %@"), c_name);
512
513
514 return false;
515 }
516
517 static const SecDbClass *query_get_class(CFDictionaryRef query, CFErrorRef *error) {
518 CFStringRef c_name = NULL;
519 const void *value = CFDictionaryGetValue(query, kSecClass);
520 if (isString(value)) {
521 c_name = value;
522 } else {
523 value = CFDictionaryGetValue(query, kSecValuePersistentRef);
524 if (isData(value)) {
525 CFDataRef pref = value;
526 _SecItemParsePersistentRef(pref, &c_name, NULL, NULL);
527 }
528 }
529
530 if (c_name && (value = kc_class_with_name(c_name))) {
531 return value;
532 } else {
533 if (c_name)
534 SecError(errSecNoSuchClass, error, CFSTR("can't find class named: %@"), c_name);
535 else
536 SecError(errSecItemClassMissing, error, CFSTR("query missing class name"));
537 return NULL;
538 }
539 }
540
541 /* AUDIT[securityd](done):
542 key (ok) is a caller provided, string starting with 'c'.
543 value (ok) is a caller provided, non NULL CFTypeRef.
544 */
545 static void query_add_class(const void *key, const void *value, Query *q)
546 {
547 if (CFEqual(key, kSecClass)) {
548 query_set_class(q, value, &q->q_error);
549 } else {
550 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("add_class: key %@ is not %@"), key, kSecClass);
551 }
552 }
553
554 /* AUDIT[securityd](done):
555 key (ok) is a caller provided, string starting with 'r'.
556 value (ok) is a caller provided, non NULL CFTypeRef.
557 */
558 static void query_add_return(const void *key, const void *value, Query *q)
559 {
560 ReturnTypeMask mask;
561 if (CFGetTypeID(value) != CFBooleanGetTypeID()) {
562 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_return: value %@ is not CFBoolean"), value);
563 return;
564 }
565
566 int set_it = CFEqual(value, kCFBooleanTrue);
567
568 if (CFEqual(key, kSecReturnData))
569 mask = kSecReturnDataMask;
570 else if (CFEqual(key, kSecReturnAttributes))
571 mask = kSecReturnAttributesMask;
572 else if (CFEqual(key, kSecReturnRef))
573 mask = kSecReturnRefMask;
574 else if (CFEqual(key, kSecReturnPersistentRef))
575 mask = kSecReturnPersistentRefMask;
576 else {
577 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("add_return: unknown key %@"), key);
578 return;
579 }
580
581 if ((q->q_return_type & mask) && !set_it) {
582 /* Clear out this bit (it's set so xor with the mask will clear it). */
583 q->q_return_type ^= mask;
584 } else if (!(q->q_return_type & mask) && set_it) {
585 /* Set this bit. */
586 q->q_return_type |= mask;
587 }
588 }
589
590 /* AUDIT[securityd](done):
591 key (ok) is a caller provided, string starting with 'u'.
592 value (ok since q_use_item_list is unused) is a caller provided, non
593 NULL CFTypeRef.
594 */
595 static void query_add_use(const void *key, const void *value, Query *q)
596 {
597 // Gotta use a string literal because we just outlawed this symbol on iOS
598 if (CFEqual(key, CFSTR("u_ItemList"))) {
599 /* TODO: Add sanity checking when we start using this. */
600 q->q_use_item_list = value;
601 } else if (CFEqual(key, kSecUseTombstones)) {
602 if (CFGetTypeID(value) == CFBooleanGetTypeID()) {
603 q->q_use_tomb = value;
604 } else if (CFGetTypeID(value) == CFNumberGetTypeID()) {
605 q->q_use_tomb = CFBooleanGetValue(value) ? kCFBooleanTrue : kCFBooleanFalse;
606 } else if (CFGetTypeID(value) == CFStringGetTypeID()) {
607 q->q_use_tomb = CFStringGetIntValue(value) ? kCFBooleanTrue : kCFBooleanFalse;
608 } else {
609 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_use: value %@ for key %@ is neither CFBoolean nor CFNumber"), value, key);
610 return;
611 }
612 } else if (CFEqual(key, kSecUseCredentialReference)) {
613 if (isData(value)) {
614 CFRetainAssign(q->q_use_cred_handle, value);
615 } else {
616 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_use: value %@ for key %@ is not CFData"), value, key);
617 return;
618 }
619 } else if (CFEqual(key, kSecUseAuthenticationUI)) {
620 if (isString(value)) {
621 q->q_skip_acl_items = CFEqualSafe(kSecUseAuthenticationUISkip, value);
622 } else {
623 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_use: value %@ for key %@ is not CFString"), value, key);
624 return;
625 }
626 #if TARGET_OS_IPHONE
627 } else if (CFEqual(key, kSecUseSystemKeychain)) {
628 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
629 q->q_keybag = KEYBAG_DEVICE;
630 #endif
631 q->q_system_keychain = true;
632 } else if (CFEqual(key, kSecUseSyncBubbleKeychain)) {
633 if (isNumber(value) && CFNumberGetValue(value, kCFNumberSInt32Type, &q->q_sync_bubble) && q->q_sync_bubble > 0) {
634 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
635 q->q_keybag = KEYBAG_DEVICE;
636 #endif
637 } else {
638 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_use: value %@ for key %@ is not valid uid"), value, key);
639 return;
640 }
641 #endif
642 } else {
643 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("add_use: unknown key %@"), key);
644 return;
645 }
646 }
647
648 static void query_set_data(const void *value, Query *q) {
649 if (!isData(value)) {
650 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("set_data: value %@ is not type data"), value);
651 } else {
652 q->q_data = value;
653 if (q->q_item)
654 CFDictionarySetValue(q->q_item, kSecValueData, value);
655 }
656 }
657
658 static void query_set_token_persistent_ref(Query *q, CFDictionaryRef token_persistent_ref) {
659 if (token_persistent_ref) {
660 query_add_attribute(kSecAttrTokenID, CFDictionaryGetValue(token_persistent_ref, kSecAttrTokenID), q);
661 CFRetainAssign(q->q_token_object_id, CFDictionaryGetValue(token_persistent_ref, kSecAttrTokenOID));
662 }
663 }
664
665 /* AUDIT[securityd](done):
666 key (ok) is a caller provided, string starting with 'u'.
667 value (ok) is a caller provided, non NULL CFTypeRef.
668 */
669 static void query_add_value(const void *key, const void *value, Query *q)
670 {
671 if (CFEqual(key, kSecValueData)) {
672 query_set_data(value, q);
673 #ifdef NO_SERVER
674 } else if (CFEqual(key, kSecValueRef)) {
675 q->q_ref = value;
676 /* TODO: Add value type sanity checking. */
677 #endif
678 } else if (CFEqual(key, kSecValuePersistentRef)) {
679 CFStringRef c_name;
680 CFDictionaryRef token_persistent_ref = NULL;
681 if (_SecItemParsePersistentRef(value, &c_name, &q->q_row_id, &token_persistent_ref)) {
682 query_set_class(q, c_name, &q->q_error);
683 query_set_token_persistent_ref(q, token_persistent_ref);
684 CFReleaseNull(token_persistent_ref);
685 } else
686 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("add_value: value %@ is not a valid persitent ref"), value);
687 } else {
688 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("add_value: unknown key %@"), key);
689 return;
690 }
691 }
692
693 /* AUDIT[securityd](done):
694 key (ok) is a caller provided, unchecked.
695 value (ok) is a caller provided, unchecked.
696 */
697 static void query_update_applier(const void *key, const void *value,
698 void *context)
699 {
700 Query *q = (Query *)context;
701 /* If something went wrong there is no point processing any more args. */
702 if (q->q_error)
703 return;
704
705 /* Make sure we have a string key. */
706 if (!isString(key)) {
707 SecError(errSecItemInvalidKeyType, &q->q_error, CFSTR("update_applier: unknown key type %@"), key);
708 return;
709 }
710
711 if (!value) {
712 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("update_applier: key %@ has NULL value"), key);
713 return;
714 }
715
716 if (CFEqual(key, CFSTR("musr"))) {
717 secnotice("item", "update_applier: refusing to update musr");
718 return;
719 }
720
721 if (CFEqual(key, kSecValueData)) {
722 query_set_data(value, q);
723 } else {
724 query_add_attribute(key, value, q);
725 }
726 }
727
728 /* AUDIT[securityd](done):
729 key (ok) is a caller provided, unchecked.
730 value (ok) is a caller provided, unchecked.
731 */
732 static void query_applier(const void *key, const void *value, void *context)
733 {
734 Query *q = (Query *)context;
735 /* If something went wrong there is no point processing any more args. */
736 if (q->q_error)
737 return;
738
739 /* Make sure we have a key. */
740 if (!key) {
741 SecError(errSecItemInvalidKeyType, &q->q_error, CFSTR("applier: NULL key"));
742 return;
743 }
744
745 /* Make sure we have a value. */
746 if (!value) {
747 SecError(errSecItemInvalidValue, &q->q_error, CFSTR("applier: key %@ has NULL value"), key);
748 return;
749 }
750
751 /* Figure out what type of key we are dealing with. */
752 CFTypeID key_id = CFGetTypeID(key);
753 if (key_id == CFStringGetTypeID()) {
754 CFIndex key_len = CFStringGetLength(key);
755 /* String keys can be different things. The subtype is determined by:
756 length 4 strings are all attributes. Otherwise the first char
757 determines the type:
758 c: class must be kSecClass
759 m: match like kSecMatchPolicy
760 r: return like kSecReturnData
761 u: use keys
762 v: value
763 f: callbacks (ignored by the query applier)
764 */
765 if (key_len == 4) {
766 /* attributes */
767 query_add_attribute(key, value, q);
768 } else if (key_len > 1) {
769 // We added a database column named 'persistref', which is returned as an attribute but doesn't comply with
770 // these matching rules. skip it for now, since it isn't filled in anyway.
771 if(CFEqualSafe(key, CFSTR("persistref"))) {
772 return;
773 }
774
775 UniChar k_first_char = CFStringGetCharacterAtIndex(key, 0);
776 switch (k_first_char)
777 {
778 case 'c': /* class */
779 query_add_class(key, value, q);
780 break;
781 case 'm': /* match */
782 query_add_match(key, value, q);
783 break;
784 case 'r': /* return */
785 query_add_return(key, value, q);
786 break;
787 case 'u': /* use */
788 query_add_use(key, value, q);
789 break;
790 case 'v': /* value */
791 query_add_value(key, value, q);
792 break;
793 case 'f':
794 break;
795 default:
796 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("applier: key %@ invalid"), key);
797 break;
798 }
799 } else {
800 SecError(errSecItemInvalidKey, &q->q_error, CFSTR("applier: key %@ invalid length"), key);
801 }
802 } else if (key_id == CFNumberGetTypeID()) {
803 /* Numeric keys are always (extended) attributes. */
804 /* TODO: Why is this here? query_add_attribute() doesn't take numbers. */
805 query_add_attribute(key, value, q);
806 } else {
807 /* We only support string and number type keys. */
808 SecError(errSecItemInvalidKeyType, &q->q_error, CFSTR("applier: key %@ neither string nor number"), key);
809 }
810 }
811
812 static CFStringRef query_infer_keyclass(Query *q, CFStringRef agrp) {
813 /* apsd are always dku. */
814 if (CFEqual(agrp, CFSTR("com.apple.apsd"))) {
815 return kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate;
816 }
817 /* All other certs or in the apple agrp is dk. */
818 if (q->q_class == cert_class()) {
819 /* third party certs are always dk. */
820 return kSecAttrAccessibleAlwaysPrivate;
821 }
822 /* The rest defaults to ak. */
823 return kSecAttrAccessibleWhenUnlocked;
824 }
825
826 void query_ensure_access_control(Query *q, CFStringRef agrp) {
827 if (q->q_access_control == 0) {
828 CFStringRef accessible = query_infer_keyclass(q, agrp);
829 query_add_attribute(kSecAttrAccessible, accessible, q);
830 }
831 }
832
833 bool query_error(Query *q, CFErrorRef *error) {
834 CFErrorRef tmp = q->q_error;
835 q->q_error = NULL;
836 return SecErrorPropagate(tmp, error);
837 }
838
839 bool query_destroy(Query *q, CFErrorRef *error) {
840 bool ok = query_error(q, error);
841 CFIndex ix, attr_count = query_attr_count(q);
842 for (ix = 0; ix < attr_count; ++ix) {
843 CFReleaseSafe(query_attr_at(q, ix).value);
844 }
845 CFReleaseSafe(q->q_item);
846 CFReleaseSafe(q->q_musrView);
847 CFReleaseSafe(q->q_primary_key_digest);
848 CFReleaseSafe(q->q_match_issuer);
849 CFReleaseSafe(q->q_access_control);
850 CFReleaseSafe(q->q_use_cred_handle);
851 CFReleaseSafe(q->q_caller_access_groups);
852 CFReleaseSafe(q->q_match_policy);
853 CFReleaseSafe(q->q_match_valid_on_date);
854 CFReleaseSafe(q->q_match_trusted_only);
855 CFReleaseSafe(q->q_token_object_id);
856
857 free(q);
858 return ok;
859 }
860
861 bool query_notify_and_destroy(Query *q, bool ok, CFErrorRef *error) {
862 if (ok && !q->q_error && (q->q_sync_changed || (q->q_changed && !SecMUSRIsSingleUserView(q->q_musrView)))) {
863 SecKeychainChanged();
864 }
865 return query_destroy(q, error) && ok;
866 }
867
868 /* Allocate and initialize a Query object for query. */
869 Query *query_create(const SecDbClass *qclass,
870 CFDataRef musr,
871 CFDictionaryRef query,
872 SecurityClient* client,
873 CFErrorRef *error)
874 {
875 if (!qclass) {
876 if (error && !*error)
877 SecError(errSecItemClassMissing, error, CFSTR("Missing class"));
878 return NULL;
879 }
880
881 if (musr == NULL)
882 musr = SecMUSRGetSingleUserKeychainUUID();
883
884 /* Number of pairs we need is the number of attributes in this class
885 plus the number of keys in the dictionary, minus one for each key in
886 the dictionary that is a regular attribute. */
887 CFIndex key_count = SecDbClassAttrCount(qclass);
888 if (key_count == 0) {
889 // Identities claim to have 0 attributes, but they really support any keys or cert attribute.
890 key_count = SecDbClassAttrCount(cert_class()) + SecDbClassAttrCount(keys_class());
891 }
892
893 if (query) {
894 key_count += CFDictionaryGetCount(query);
895 SecDbForEachAttr(qclass, attr) {
896 if (CFDictionaryContainsKey(query, attr->name))
897 --key_count;
898 }
899 }
900
901 if (key_count > QUERY_KEY_LIMIT) {
902 if (error && !*error)
903 {
904 secerror("key_count: %ld, QUERY_KEY_LIMIT: %d", (long)key_count, QUERY_KEY_LIMIT);
905 SecError(errSecItemIllegalQuery, error, CFSTR("Past query key limit"));
906 }
907 return NULL;
908 }
909
910 Query *q = calloc(1, sizeof(Query) + sizeof(Pair) * key_count);
911 if (q == NULL) {
912 if (error && !*error)
913 SecError(errSecAllocate, error, CFSTR("Out of memory"));
914 return NULL;
915 }
916
917 q->q_pairs_count = key_count;
918 q->q_musrView = (CFDataRef)CFRetain(musr);
919 q->q_uuid_from_primary_key = false;
920 q->q_keybag = KEYBAG_DEVICE;
921 q->q_class = qclass;
922 q->q_match_begin = q->q_match_end = key_count;
923 q->q_item = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
924
925 if (client) {
926 // If not, don't do anything. Parent apps, schema migration, etc. all need access to all items, clip or no
927 if (client->isAppClip) {
928 secdebug("query", "Client is app clip, adding restriction to query attribute");
929 CFDictionaryAddValue(q->q_item, kSecAttrAppClipItem, kCFBooleanTrue);
930 }
931 } else {
932 secdebug("query", "no client information specified so not tweaking query attributes");
933 }
934
935 return q;
936 }
937
938 /* Parse query for a Query object q. */
939 static bool query_parse_with_applier(Query *q, CFDictionaryRef query,
940 CFDictionaryApplierFunction applier,
941 CFErrorRef *error) {
942 CFDictionaryApplyFunction(query, applier, q);
943 return query_error(q, error);
944 }
945
946 /* Parse query for a Query object q. */
947 static bool query_parse(Query *q, CFDictionaryRef query,
948 CFErrorRef *error) {
949 return query_parse_with_applier(q, query, query_applier, error);
950 }
951
952 /* Parse query for a Query object q. */
953 bool query_update_parse(Query *q, CFDictionaryRef update,
954 CFErrorRef *error) {
955 return query_parse_with_applier(q, update, query_update_applier, error);
956 }
957
958 Query *query_create_with_limit(CFDictionaryRef query, CFDataRef musr, CFIndex limit, SecurityClient* client, CFErrorRef *error) {
959 Query *q;
960 q = query_create(query_get_class(query, error), musr, query, client, error);
961 if (q) {
962 q->q_limit = limit;
963 if (!query_parse(q, query, error)) {
964 query_destroy(q, error);
965 return NULL;
966 }
967 if (!q->q_sync && !q->q_row_id && !q->q_token_object_id) {
968 /* query did not specify a kSecAttrSynchronizable attribute,
969 * and did not contain a persistent reference. */
970 query_add_attribute(kSecAttrSynchronizable, kCFBooleanFalse, q);
971 }
972 }
973 return q;
974 }
975
976
977 void
978 query_set_caller_access_groups(Query *q, CFArrayRef caller_access_groups) {
979 CFRetainAssign(q->q_caller_access_groups, caller_access_groups);
980 }
981
982 void
983 query_set_policy(Query *q, SecPolicyRef policy) {
984 CFRetainAssign(q->q_match_policy, policy);
985 }
986
987 void query_set_valid_on_date(Query *q, CFDateRef date) {
988 CFRetainAssign(q->q_match_valid_on_date, date);
989 }
990
991 void query_set_trusted_only(Query *q, CFBooleanRef trusted_only) {
992 CFRetainAssign(q->q_match_trusted_only, trusted_only);
993 }