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. */ 63CFIndex query_attr_count(const Query *q) 64{ 65 return q->q_attr_end; 66} 67 68Pair query_attr_at(const Query *q, CFIndex ix) 69{ 70 return q->q_pairs[ix]; 71} 72 73CFIndex 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 85const 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 112static 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 */ 130static 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 224void 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. */ 232static 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 */ 254static 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 298static 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 314static 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 */ 342static 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 */ 355static 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 */ 392static 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 445static 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 */ 459static 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 */ 484static 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 */ 514static 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 585static 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 600void 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 607bool query_error(Query *q, CFErrorRef *error) { 608 CFErrorRef tmp = q->q_error; 609 q->q_error = NULL; 610 return CFErrorPropagate(tmp, error); 611} 612 613bool 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 632bool 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. */ 640Query *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. */ 692static 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. */ 700static 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. */ 706bool query_update_parse(Query *q, CFDictionaryRef update, 707 CFErrorRef *error) { 708 return query_parse_with_applier(q, update, query_update_applier, error); 709} 710 711Query *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. */ 735void 736query_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. */ 778void 779query_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 789void 790query_set_caller_access_groups(Query *q, CFArrayRef caller_access_groups) { 791 CFRetainAssign(q->q_caller_access_groups, caller_access_groups); 792} 793 794bool 795query_needs_authentication(Query *q) { 796 return q->q_required_access_controls && CFArrayGetCount(q->q_required_access_controls) > 0; 797} 798 799void 800query_enable_interactive(Query *q) { 801 CFAssignRetained(q->q_required_access_controls, CFArrayCreateMutableForCFTypes(kCFAllocatorDefault)); 802} 803 804bool 805query_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 838out: 839#endif 840 CFReleaseSafe(hints); 841 return ok; 842} 843