SecDbItem.c
1 /* 2 * Copyright (c) 2012-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 * SecDbItem.c - CoreFoundation-based constants and functions representing 26 * database items (certificates, keys, identities, and passwords.) 27 */ 28 29 #if defined(TARGET_DARWINOS) && TARGET_DARWINOS 30 #undef OCTAGON 31 #undef SECUREOBJECTSYNC 32 #undef SHAREDWEBCREDENTIALS 33 #endif 34 35 #include "keychain/securityd/SecDbItem.h" 36 #include "keychain/securityd/SecDbKeychainItem.h" 37 #include "keychain/securityd/SecItemDb.h" 38 #include <utilities/SecCFWrappers.h> 39 #include <utilities/SecCFCCWrappers.h> 40 #include <utilities/der_date.h> 41 #include <utilities/der_plist.h> 42 #include <utilities/debugging.h> 43 44 #include <Security/SecBasePriv.h> 45 #include <Security/SecInternal.h> 46 #include <corecrypto/ccsha1.h> 47 #include <Security/SecItem.h> 48 #include <Security/SecItemPriv.h> 49 #include <Security/SecAccessControl.h> 50 #include <Security/SecAccessControlPriv.h> 51 #include "keychain/securityd/SecItemSchema.h" 52 53 #include <keychain/ckks/CKKS.h> 54 55 // MARK: type converters 56 57 CFStringRef copyString(CFTypeRef obj) { 58 CFTypeID tid = CFGetTypeID(obj); 59 if (tid == CFStringGetTypeID()) { 60 return CFStringCreateCopy(0, obj); 61 }else if (tid == CFDataGetTypeID()) { 62 return CFStringCreateFromExternalRepresentation(0, obj, kCFStringEncodingUTF8); 63 } else if (tid == CFUUIDGetTypeID()) { 64 return CFUUIDCreateString(NULL, obj); 65 } else { 66 return NULL; 67 } 68 } 69 70 CFDataRef copyData(CFTypeRef obj) { 71 CFTypeID tid = CFGetTypeID(obj); 72 if (tid == CFDataGetTypeID()) { 73 return CFDataCreateCopy(0, obj); 74 } else if (tid == CFStringGetTypeID()) { 75 return CFStringCreateExternalRepresentation(0, obj, kCFStringEncodingUTF8, 0); 76 } else if (tid == CFNumberGetTypeID()) { 77 SInt32 value; 78 CFNumberGetValue(obj, kCFNumberSInt32Type, &value); 79 return CFDataCreate(0, (const UInt8 *)&value, sizeof(value)); 80 } else { 81 return NULL; 82 } 83 } 84 85 CFTypeRef copyUUID(CFTypeRef obj) { 86 CFTypeID tid = CFGetTypeID(obj); 87 if (tid == CFDataGetTypeID()) { 88 CFIndex length = CFDataGetLength(obj); 89 if (length != 0 && length != 16) 90 return NULL; 91 return CFDataCreateCopy(NULL, obj); 92 } else if (tid == CFNullGetTypeID()) { 93 return CFDataCreate(NULL, NULL, 0); 94 } else if (tid == CFUUIDGetTypeID()) { 95 CFUUIDBytes uuidbytes = CFUUIDGetUUIDBytes(obj); 96 CFDataRef uuiddata = CFDataCreate(NULL, (void*) &uuidbytes, sizeof(uuidbytes)); 97 return uuiddata; 98 } else { 99 return NULL; 100 } 101 } 102 103 104 CFTypeRef copyBlob(CFTypeRef obj) { 105 CFTypeID tid = CFGetTypeID(obj); 106 if (tid == CFDataGetTypeID()) { 107 return CFDataCreateCopy(0, obj); 108 } else if (tid == CFStringGetTypeID()) { 109 return CFStringCreateCopy(0, obj); 110 } else if (tid == CFNumberGetTypeID()) { 111 CFRetain(obj); 112 return obj; 113 } else { 114 return NULL; 115 } 116 } 117 118 CFDataRef copySHA1(CFTypeRef obj) { 119 CFTypeID tid = CFGetTypeID(obj); 120 if (tid == CFDataGetTypeID() && CFDataGetLength(obj) == CCSHA1_OUTPUT_SIZE) { 121 return CFDataCreateCopy(CFGetAllocator(obj), obj); 122 } else { 123 return NULL; 124 } 125 } 126 127 CFTypeRef copyNumber(CFTypeRef obj) { 128 CFTypeID tid = CFGetTypeID(obj); 129 if (tid == CFNumberGetTypeID()) { 130 CFRetain(obj); 131 return obj; 132 } else if (tid == CFBooleanGetTypeID()) { 133 SInt32 value = CFBooleanGetValue(obj); 134 return CFNumberCreate(0, kCFNumberSInt32Type, &value); 135 } else if (tid == CFStringGetTypeID()) { 136 SInt32 value = CFStringGetIntValue(obj); 137 CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value); 138 /* If a string converted to an int isn't equal to the int printed as 139 a string, return a CFStringRef instead. */ 140 if (!CFEqual(t, obj)) { 141 CFRelease(t); 142 return CFStringCreateCopy(0, obj); 143 } 144 CFRelease(t); 145 return CFNumberCreate(0, kCFNumberSInt32Type, &value); 146 } else 147 return NULL; 148 } 149 150 CFDateRef copyDate(CFTypeRef obj) { 151 CFTypeID tid = CFGetTypeID(obj); 152 if (tid == CFDateGetTypeID()) { 153 CFRetain(obj); 154 return obj; 155 } else 156 return NULL; 157 } 158 159 // MARK: SecDbColumn accessors, to retrieve values as CF types in SecDbStep. 160 161 static CFDataRef SecDbColumnCopyData(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) { 162 return CFDataCreate(allocator, sqlite3_column_blob(stmt, col), 163 sqlite3_column_bytes(stmt, col)); 164 //return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt, col), 165 // sqlite3_column_bytes(stmt, col), 166 // kCFAllocatorNull); 167 } 168 169 static CFDateRef SecDbColumnCopyDate(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) { 170 return CFDateCreate(allocator, sqlite3_column_double(stmt, col)); 171 } 172 173 static CFNumberRef SecDbColumnCopyDouble(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) { 174 double number = sqlite3_column_double(stmt, col); 175 return CFNumberCreate(allocator, kCFNumberDoubleType, &number); 176 } 177 178 static CFNumberRef SecDbColumnCopyNumber64(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) { 179 sqlite_int64 number = sqlite3_column_int64(stmt, col); 180 return CFNumberCreate(allocator, kCFNumberSInt64Type, &number); 181 } 182 183 static CFNumberRef SecDbColumnCopyNumber(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) { 184 sqlite_int64 number = sqlite3_column_int64(stmt, col); 185 if (INT32_MIN <= number && number <= INT32_MAX) { 186 int32_t num32 = (int32_t)number; 187 return CFNumberCreate(allocator, kCFNumberSInt32Type, &num32); 188 } else { 189 return CFNumberCreate(allocator, kCFNumberSInt64Type, &number); 190 } 191 } 192 193 static CFTypeRef SecDbColumnCopyString(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error, 194 CFOptionFlags flags) { 195 const unsigned char *text = sqlite3_column_text(stmt, col); 196 if (!text || 0 == strlen((const char *)text)) { 197 if (flags & kSecDbDefaultEmptyFlag) { 198 return CFSTR(""); 199 } else if (flags & kSecDbDefault0Flag) { 200 return CFSTR("0"); 201 } else { 202 return kCFNull; 203 } 204 } 205 return CFStringCreateWithBytes(allocator, text, strlen((const char *)text), kCFStringEncodingUTF8, false); 206 } 207 208 // MARK: SecDbClass helpers 209 210 const SecDbAttr *SecDbClassAttrWithKind(const SecDbClass *class, SecDbAttrKind kind, CFErrorRef *error) { 211 const SecDbAttr *result = NULL; 212 SecDbForEachAttr(class, desc) { 213 if (desc->kind == kind) 214 result = desc; 215 } 216 217 if (!result) 218 SecError(errSecInternal, error, CFSTR("Can't find attribute of kind %d in class %@"), kind, class->name); 219 220 return result; 221 } 222 223 // MARK: SecDbAttr helpers 224 225 static bool SecDbIsTombstoneDbSelectAttr(const SecDbAttr *attr) { 226 return attr->flags & kSecDbPrimaryKeyFlag || attr->kind == kSecDbTombAttr; 227 } 228 229 #if 0 230 static bool SecDbIsTombstoneDbInsertAttr(const SecDbAttr *attr) { 231 return SecDbIsTombstoneDbSelectAttr(attr) || attr->kind == kSecDbAccessAttr || attr->kind == kSecDbCreationDateAttr || attr->kind == kSecDbModificationDateAttr; 232 } 233 #endif 234 235 static bool SecDbIsTombstoneDbUpdateAttr(const SecDbAttr *attr) { 236 // We add AuthenticatedData to include UUIDs, which can't be primary keys 237 return SecDbIsTombstoneDbSelectAttr(attr) || attr->kind == kSecDbAccessAttr || attr->kind == kSecDbCreationDateAttr || attr->kind == kSecDbRowIdAttr || (attr->flags & kSecDbInAuthenticatedDataFlag); 238 } 239 240 CFTypeRef SecDbAttrCopyDefaultValue(const SecDbAttr *attr, CFErrorRef *error) { 241 CFTypeRef value = NULL; 242 switch (attr->kind) { 243 case kSecDbAccessAttr: 244 case kSecDbStringAttr: 245 case kSecDbAccessControlAttr: 246 value = CFSTR(""); 247 break; 248 case kSecDbBlobAttr: 249 case kSecDbDataAttr: 250 value = CFDataCreate(kCFAllocatorDefault, NULL, 0); 251 break; 252 case kSecDbUUIDAttr: 253 value = CFDataCreate(kCFAllocatorDefault, NULL, 0); 254 break; 255 case kSecDbNumberAttr: 256 case kSecDbSyncAttr: 257 case kSecDbTombAttr: 258 { 259 int32_t zero = 0; 260 value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &zero); 261 break; 262 } 263 case kSecDbDateAttr: 264 value = CFDateCreate(kCFAllocatorDefault, 0.0); 265 break; 266 case kSecDbCreationDateAttr: 267 case kSecDbModificationDateAttr: 268 value = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()); 269 break; 270 default: 271 SecError(errSecInternal, error, CFSTR("attr %@ has no default value"), attr->name); 272 value = NULL; 273 } 274 275 return value; 276 } 277 278 static CFTypeRef SecDbAttrCopyValueForDb(const SecDbAttr *attr, CFTypeRef value, CFErrorRef *error) { 279 CFDataRef data = NULL; 280 CFTypeRef result = NULL; 281 282 if (value == NULL) 283 value = kCFNull; 284 285 if (CFEqual(value, kCFNull) && attr->flags & kSecDbPrimaryKeyFlag) { 286 // SQLITE3 doesn't like NULL for primary key attributes, pretend kSecDbDefaultEmptyFlag was specified 287 require_quiet(result = SecDbAttrCopyDefaultValue(attr, error), out); 288 } else { 289 result = CFRetain(value); 290 } 291 292 if (attr->flags & kSecDbSHA1ValueInFlag && !CFEqual(result, kCFNull)) { 293 require_action_quiet(data = copyData(result), out, 294 SecError(errSecInternal, error, CFSTR("failed to get attribute %@ data"), attr->name); 295 CFReleaseNull(result)); 296 CFAssignRetained(result, CFDataCopySHA1Digest(data, error)); 297 } 298 299 out: 300 CFReleaseSafe(data); 301 return result; 302 } 303 304 static CFStringRef SecDbAttrGetHashName(const SecDbAttr *attr) { 305 if ((attr->flags & kSecDbSHA1ValueInFlag) == 0) { 306 return attr->name; 307 } 308 309 static dispatch_once_t once; 310 static CFMutableDictionaryRef hash_store; 311 static dispatch_queue_t queue; 312 dispatch_once(&once, ^{ 313 queue = dispatch_queue_create("secd-hash-name", NULL); 314 hash_store = CFDictionaryCreateMutableForCFTypes(NULL); 315 }); 316 317 __block CFStringRef name; 318 dispatch_sync(queue, ^{ 319 name = CFDictionaryGetValue(hash_store, attr->name); 320 if (name == NULL) { 321 name = CFStringCreateWithFormat(NULL, NULL, CFSTR("#%@"), attr->name); 322 CFDictionarySetValue(hash_store, attr->name, name); 323 CFRelease(name); 324 } 325 }); 326 return name; 327 } 328 329 // MARK: SecDbItem 330 331 CFTypeRef SecDbItemGetCachedValueWithName(SecDbItemRef item, CFStringRef name) { 332 return CFDictionaryGetValue(item->attributes, name); 333 } 334 335 static CFTypeRef SecDbItemGetCachedValue(SecDbItemRef item, const SecDbAttr *desc) { 336 return CFDictionaryGetValue(item->attributes, desc->name); 337 } 338 339 CFMutableDictionaryRef SecDbItemCopyPListWithMask(SecDbItemRef item, CFOptionFlags mask, CFErrorRef *error) { 340 return SecDbItemCopyPListWithFlagAndSkip(item, mask, 0, error); 341 } 342 343 CFMutableDictionaryRef SecDbItemCopyPListWithFlagAndSkip(SecDbItemRef item, 344 CFOptionFlags mask, 345 CFOptionFlags flagsToSkip, 346 CFErrorRef *error) 347 { 348 CFMutableDictionaryRef dict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 349 SecDbForEachAttrWithMask(item->class, desc, mask) { 350 if((desc->flags & flagsToSkip) != 0) { 351 break; 352 } 353 354 CFTypeRef value = SecDbItemGetValue(item, desc, error); 355 if (value) { 356 if (!CFEqual(kCFNull, value)) { 357 CFDictionarySetValue(dict, desc->name, value); 358 } else if (desc->flags & kSecDbNotNullFlag) { 359 SecError(errSecDecode, error, CFSTR("attribute %@ has NULL value"), desc->name); 360 secerror("%@", error ? *error : (CFErrorRef)CFSTR("error == NULL")); 361 CFReleaseNull(dict); 362 break; 363 } 364 } else { 365 CFReleaseNull(dict); 366 break; 367 } 368 } 369 return dict; 370 } 371 372 void SecDbItemSetCredHandle(SecDbItemRef item, CFTypeRef cred_handle) { 373 CFRetainAssign(item->credHandle, cred_handle); 374 } 375 376 void SecDbItemSetCallerAccessGroups(SecDbItemRef item, CFArrayRef caller_access_groups) { 377 CFRetainAssign(item->callerAccessGroups, caller_access_groups); 378 } 379 380 CFDataRef SecDbItemCopyEncryptedDataToBackup(SecDbItemRef item, uint64_t handle, CFErrorRef *error) { 381 CFDataRef edata = NULL; 382 keybag_handle_t keybag = (keybag_handle_t)handle; 383 CFMutableDictionaryRef attributes = SecDbItemCopyPListWithMask(item, kSecDbInCryptoDataFlag, error); 384 CFMutableDictionaryRef auth_attributes = SecDbItemCopyPListWithMask(item, kSecDbInAuthenticatedDataFlag, error); 385 if (attributes || auth_attributes) { 386 SecAccessControlRef access_control = SecDbItemCopyAccessControl(item, error); 387 if (access_control) { 388 if (ks_encrypt_data_legacy(keybag, access_control, item->credHandle, attributes, auth_attributes, &edata, false, error)) { 389 item->_edataState = kSecDbItemEncrypting; 390 } else { 391 seccritical("ks_encrypt_data (db): failed: %@", error ? *error : (CFErrorRef)CFSTR("")); 392 } 393 CFRelease(access_control); 394 } 395 CFReleaseNull(attributes); 396 CFReleaseNull(auth_attributes); 397 } 398 399 return edata; 400 } 401 402 bool SecDbItemEnsureDecrypted(SecDbItemRef item, bool decryptSecretData, CFErrorRef *error) { 403 404 // If we haven't yet decrypted the item, make sure we do so now 405 bool result = true; 406 if (item->_edataState == kSecDbItemEncrypted || (decryptSecretData && item->_edataState == kSecDbItemSecretEncrypted)) { 407 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, error); 408 if (attr) { 409 CFDataRef edata = SecDbItemGetCachedValue(item, attr); 410 if (!edata) 411 return SecError(errSecInternal, error, CFSTR("state= encrypted but edata is NULL")); 412 // Decrypt calls set value a bunch of times which clears our edata and changes our state. 413 item->_edataState = kSecDbItemDecrypting; 414 result = SecDbItemDecrypt(item, decryptSecretData, edata, error); 415 if (result) 416 item->_edataState = decryptSecretData ? kSecDbItemClean : kSecDbItemSecretEncrypted; 417 else 418 item->_edataState = kSecDbItemEncrypted; 419 } 420 } 421 return result; 422 } 423 424 // Only called if cached value is not found. 425 static CFTypeRef SecDbItemCopyValue(SecDbItemRef item, const SecDbAttr *attr, CFErrorRef *error) { 426 if (attr->copyValue) { 427 return attr->copyValue(item, attr, error); 428 } 429 430 CFTypeRef value = NULL; 431 switch (attr->kind) { 432 // These have an explicit copyValue; here to shut up compiler 433 case kSecDbSHA1Attr: 434 case kSecDbEncryptedDataAttr: 435 case kSecDbPrimaryKeyAttr: 436 value = NULL; 437 break; 438 case kSecDbAccessAttr: 439 case kSecDbStringAttr: 440 case kSecDbBlobAttr: 441 case kSecDbAccessControlAttr: 442 if (attr->flags & kSecDbNotNullFlag) { 443 if (attr->flags & kSecDbDefault0Flag) { 444 value = CFSTR("0"); 445 break; 446 } else if (attr->kind != kSecDbBlobAttr && attr->flags & kSecDbDefaultEmptyFlag) { 447 // blob drops through to data everything else is empty string 448 value = CFSTR(""); 449 break; 450 } 451 } 452 //DROPTHROUGH 453 case kSecDbDataAttr: 454 if (attr->flags & kSecDbNotNullFlag && attr->flags & kSecDbDefaultEmptyFlag) { 455 value = CFDataCreate(CFGetAllocator(item), NULL, 0); 456 } else { 457 value = kCFNull; 458 } 459 break; 460 case kSecDbUUIDAttr: 461 value = CFDataCreate(CFGetAllocator(item), NULL, 0); 462 break; 463 case kSecDbNumberAttr: 464 case kSecDbSyncAttr: 465 case kSecDbTombAttr: 466 if (attr->flags & kSecDbNotNullFlag) { 467 int32_t zero = 0; 468 value = CFNumberCreate(CFGetAllocator(item), kCFNumberSInt32Type, &zero); 469 } else { 470 value = kCFNull; 471 } 472 break; 473 case kSecDbDateAttr: 474 if (attr->flags & kSecDbNotNullFlag && attr->flags & kSecDbDefault0Flag) { 475 value = CFDateCreate(kCFAllocatorDefault, 0.0); 476 } else { 477 value = kCFNull; 478 } 479 break; 480 case kSecDbRowIdAttr: 481 if (attr->flags & kSecDbNotNullFlag) { 482 // No can do, error? 483 } 484 value = kCFNull; 485 break; 486 case kSecDbCreationDateAttr: 487 case kSecDbModificationDateAttr: 488 value = CFDateCreate(CFGetAllocator(item), CFAbsoluteTimeGetCurrent()); 489 break; 490 case kSecDbUTombAttr: 491 value = kCFNull; 492 break; 493 } 494 495 return value; 496 } 497 498 // SecDbItemGetValue will return kCFNull if there is no value for an attribute and this was not 499 // an error. It will return NULL and optionally set *error if there was an error computing an 500 // attribute, or if a required attribute was missing a value and had no known way to compute 501 // it's value. 502 CFTypeRef SecDbItemGetValue(SecDbItemRef item, const SecDbAttr *desc, CFErrorRef *error) { 503 // Propagate chained errors 504 if (!desc) 505 return NULL; 506 507 if (desc->flags & kSecDbInCryptoDataFlag || desc->flags & kSecDbInAuthenticatedDataFlag || desc->flags & kSecDbReturnDataFlag) { 508 if (!SecDbItemEnsureDecrypted(item, desc->flags & kSecDbReturnDataFlag, error)) 509 return NULL; 510 } 511 512 CFTypeRef value = SecDbItemGetCachedValue(item, desc); 513 if (!value) { 514 value = SecDbItemCopyValue(item, desc, error); 515 if (value) { 516 if (CFEqual(kCFNull, value)) { 517 CFRelease(value); // This is redundant but it shuts clang's static analyzer up. 518 value = kCFNull; 519 } else { 520 SecDbItemSetValue(item, desc, value, error); 521 CFRelease(value); 522 value = SecDbItemGetCachedValue(item, desc); 523 } 524 } 525 } 526 return value; 527 } 528 529 CFTypeRef SecDbItemGetValueKind(SecDbItemRef item, SecDbAttrKind descKind, CFErrorRef *error) { 530 CFTypeRef result = NULL; 531 532 const SecDbClass * itemClass = SecDbItemGetClass(item); 533 const SecDbAttr * desc = SecDbClassAttrWithKind(itemClass, descKind, error); 534 535 if (desc) { 536 result = SecDbItemGetValue(item, desc, error); 537 } 538 539 return result; 540 } 541 542 543 // Similar as SecDbItemGetValue, but if attr represents attribute stored into DB field as hash, returns 544 // hashed value for the attribute. 545 static CFTypeRef SecDbItemCopyValueForDb(SecDbItemRef item, const SecDbAttr *desc, CFErrorRef *error) { 546 CFTypeRef value = NULL; 547 CFStringRef hash_name = NULL; 548 hash_name = SecDbAttrGetHashName(desc); 549 if ((desc->flags & kSecDbSHA1ValueInFlag) && (desc->flags & kSecDbInFlag)) { 550 value = CFRetainSafe(CFDictionaryGetValue(item->attributes, hash_name)); 551 } 552 553 if (value == NULL) { 554 require_quiet(value = SecDbItemGetValue(item, desc, error), out); 555 require_action_quiet(value = SecDbAttrCopyValueForDb(desc, value, error), out, CFReleaseNull(value)); 556 if ((desc->flags & kSecDbSHA1ValueInFlag) != 0) { 557 CFDictionarySetValue(item->attributes, hash_name, value); 558 } 559 } 560 561 out: 562 return value; 563 } 564 565 static bool SecDbItemGetBoolValue(SecDbItemRef item, const SecDbAttr *desc, bool *bvalue, CFErrorRef *error) { 566 CFTypeRef value = SecDbItemGetValue(item, desc, error); 567 if (!value) 568 return false; 569 char cvalue; 570 *bvalue = (isNumber(value) && CFNumberGetValue(value, kCFNumberCharType, &cvalue) && cvalue == 1); 571 return true; 572 } 573 574 static void SecDbItemAppendAttributeToDescription(SecDbItemRef item, const SecDbAttr *attr, CFMutableStringRef mdesc) 575 { 576 // In non-debug builds, the following attributes aren't very useful. 577 #ifndef DEBUG 578 if (CFEqual(CFSTR("data"), attr->name)|| 579 CFEqual(CFSTR("v_pk"), attr->name)) { 580 return; 581 } 582 #endif 583 584 CFTypeRef value = SecDbItemGetValue(item, attr, NULL); 585 if (value && value != kCFNull) { 586 CFStringAppend(mdesc, CFSTR(",")); 587 CFStringAppend(mdesc, attr->name); 588 CFStringAppend(mdesc, CFSTR("=")); 589 if (CFEqual(CFSTR("data"), attr->name)) { 590 CFStringAppendEncryptedData(mdesc, value); 591 } else if (CFEqual(CFSTR("v_Data"), attr->name)) { 592 CFStringAppend(mdesc, CFSTR("<?>")); 593 } else if (isData(value)) { 594 CFStringAppendHexData(mdesc, value); 595 } else { 596 CFStringAppendFormat(mdesc, 0, CFSTR("%@"), value); 597 } 598 } 599 } 600 601 static CFStringRef SecDbItemCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { 602 CFStringRef desc; 603 if (isDictionary(formatOptions) && CFDictionaryContainsKey(formatOptions, kSecDebugFormatOption)) { 604 SecDbItemRef item = (SecDbItemRef)cf; 605 CFMutableStringRef mdesc = CFStringCreateMutable(CFGetAllocator(cf), 0); 606 CFStringAppendFormat(mdesc, NULL, CFSTR("<%@"), item->class->name); 607 608 // First, the primary key attributes 609 SecDbForEachAttrWithMask(item->class, attr, kSecDbPrimaryKeyFlag) { 610 SecDbItemAppendAttributeToDescription(item, attr, mdesc); 611 } 612 613 CFStringAppend(mdesc, CFSTR(", |otherAttr")); 614 // tombstone values are very important, and should print next 615 SecDbForEachAttr(item->class, attr) { 616 if(CFEqualSafe(CFSTR("tomb"), attr->name)) { 617 SecDbItemAppendAttributeToDescription(item, attr, mdesc); 618 } 619 } 620 621 // And finally, everything else 622 SecDbForEachAttr(item->class, attr) { 623 if((attr->flags & kSecDbPrimaryKeyFlag) != 0) { 624 continue; 625 } 626 if(CFEqualSafe(CFSTR("tomb"), attr->name)) { 627 continue; 628 } 629 SecDbItemAppendAttributeToDescription(item, attr, mdesc); 630 } 631 CFStringAppend(mdesc, CFSTR(">")); 632 desc = mdesc; 633 } else { 634 SecDbItemRef item = (SecDbItemRef)cf; 635 const UInt8 zero4[4] = {}; 636 const UInt8 *pk = &zero4[0], *sha1 = &zero4[0]; 637 char sync = 0; 638 char tomb = 0; 639 SInt64 rowid = 0; 640 CFStringRef access = NULL; 641 uint8_t mdatbuf[32] = {}; 642 uint8_t *mdat = &mdatbuf[0]; 643 CFMutableStringRef attrs = CFStringCreateMutable(kCFAllocatorDefault, 0); 644 CFStringRef agrp = NULL; 645 CFBooleanRef utomb = NULL; 646 647 SecDbForEachAttr(item->class, attr) { 648 CFTypeRef value; 649 switch (attr->kind) { 650 case kSecDbBlobAttr: 651 case kSecDbDataAttr: 652 case kSecDbStringAttr: 653 case kSecDbNumberAttr: 654 case kSecDbDateAttr: 655 case kSecDbEncryptedDataAttr: 656 if (attr->flags & (kSecDbReturnAttrFlag | kSecDbReturnDataFlag) && (value = SecDbItemGetValue(item, attr, NULL)) && !CFEqual(value, kCFNull)) { 657 if (isString(value) && CFEqual(attr->name, kSecAttrAccessGroup)) { 658 agrp = value; 659 } else { 660 // We don't log these, just record that we saw the attribute. 661 CFStringAppend(attrs, CFSTR(",")); 662 CFStringAppend(attrs, attr->name); 663 } 664 } 665 break; 666 case kSecDbUUIDAttr: 667 if ((value = SecDbItemGetValue(item, attr, NULL))) { 668 if (CFEqual(attr->name, kSecAttrMultiUser)) { 669 if (isData(value)) { 670 CFStringAppend(attrs, CFSTR(",")); 671 if (CFDataGetLength(value)) { 672 CFStringAppendHexData(attrs, value); 673 } else { 674 CFStringAppend(attrs, attr->name); 675 } 676 } 677 } 678 } 679 break; 680 case kSecDbCreationDateAttr: 681 // We don't care about this and every object has one. 682 break; 683 case kSecDbModificationDateAttr: 684 value = SecDbItemGetValue(item, attr, NULL); 685 if (isDate(value)) 686 mdat = der_encode_generalizedtime_body(CFDateGetAbsoluteTime(value), NULL, mdat, &mdatbuf[31]); 687 break; 688 case kSecDbSHA1Attr: 689 value = SecDbItemGetValue(item, attr, NULL); 690 if (isData(value) && CFDataGetLength(value) >= (CFIndex)sizeof(zero4)) 691 sha1 = CFDataGetBytePtr(value); 692 break; 693 case kSecDbRowIdAttr: 694 value = SecDbItemGetValue(item, attr, NULL); 695 if (isNumber(value)) 696 CFNumberGetValue(value, kCFNumberSInt64Type, &rowid); 697 break; 698 case kSecDbPrimaryKeyAttr: 699 value = SecDbItemGetValue(item, attr, NULL); 700 if (isData(value)) 701 pk = CFDataGetBytePtr(value); 702 break; 703 case kSecDbSyncAttr: 704 value = SecDbItemGetValue(item, attr, NULL); 705 if (isNumber(value)) 706 CFNumberGetValue(value, kCFNumberCharType, &sync); 707 break; 708 case kSecDbTombAttr: 709 value = SecDbItemGetValue(item, attr, NULL); 710 if (isNumber(value)) 711 CFNumberGetValue(value, kCFNumberCharType, &tomb); 712 break; 713 case kSecDbAccessAttr: 714 value = SecDbItemGetValue(item, attr, NULL); 715 if (isString(value)) 716 access = value; 717 break; 718 case kSecDbUTombAttr: 719 value = SecDbItemGetValue(item, attr, NULL); 720 if (isBoolean(value)) 721 utomb = value; 722 case kSecDbAccessControlAttr: 723 /* TODO: Add formatting of ACLs. */ 724 break; 725 } 726 } 727 728 desc = CFStringCreateWithFormat(CFGetAllocator(cf), NULL, 729 CFSTR( 730 "%s," 731 "%@," 732 "%02X%02X%02X%02X," 733 "%s," 734 "%@," 735 "%@," 736 "%"PRId64 737 "%@," 738 "%s," 739 "%s" 740 "%02X%02X%02X%02X"), 741 tomb ? "T" : "O", 742 item->class->name, 743 pk[0], pk[1], pk[2], pk[3], 744 sync ? "S" : "L", 745 access, 746 agrp, 747 rowid, 748 attrs, 749 mdat, 750 utomb ? (CFEqual(utomb, kCFBooleanFalse) ? "F," : "T,") : "", 751 sha1[0], sha1[1], sha1[2], sha1[3]); 752 CFReleaseSafe(attrs); 753 } 754 755 return desc; 756 } 757 758 static void SecDbItemDestroy(CFTypeRef cf) { 759 SecDbItemRef item = (SecDbItemRef)cf; 760 CFReleaseSafe(item->attributes); 761 CFReleaseSafe(item->credHandle); 762 CFReleaseSafe(item->callerAccessGroups); 763 CFReleaseSafe(item->cryptoOp); 764 } 765 766 static CFHashCode SecDbItemHash(CFTypeRef cf) { 767 SecDbItemRef item = (SecDbItemRef)cf; 768 CFDataRef digest = SecDbItemGetSHA1(item, NULL); 769 CFHashCode code; 770 const UInt8 *p = CFDataGetBytePtr(digest); 771 // Read first 8 bytes of digest in order 772 code = p[0] + ((p[1] + ((p[2] + ((p[3] + ((p[4] + ((p[5] + ((p[6] + (p[7] << 8)) << 8)) << 8)) << 8)) << 8)) << 8)) << 8); 773 return code; 774 } 775 776 static Boolean SecDbItemCompare(CFTypeRef cf1, CFTypeRef cf2) { 777 SecDbItemRef item1 = (SecDbItemRef)cf1; 778 SecDbItemRef item2 = (SecDbItemRef)cf2; 779 CFDataRef digest1 = NULL; 780 CFDataRef digest2 = NULL; 781 if (item1) 782 digest1 = SecDbItemGetSHA1(item1, NULL); 783 if (item2) 784 digest2 = SecDbItemGetSHA1(item2, NULL); 785 Boolean equal = CFEqual(digest1, digest2); 786 return equal; 787 } 788 789 CFGiblisWithHashFor(SecDbItem) 790 791 static SecDbItemRef SecDbItemCreate(CFAllocatorRef allocator, const SecDbClass *class, keybag_handle_t keybag) { 792 SecDbItemRef item = CFTypeAllocate(SecDbItem, struct SecDbItem, allocator); 793 item->class = class; 794 item->attributes = CFDictionaryCreateMutableForCFTypes(allocator); 795 item->keybag = keybag; 796 item->_edataState = kSecDbItemDirty; 797 item->cryptoOp = kAKSKeyOpDecrypt; 798 799 return item; 800 } 801 802 const SecDbClass *SecDbItemGetClass(SecDbItemRef item) { 803 return item->class; 804 } 805 806 keybag_handle_t SecDbItemGetKeybag(SecDbItemRef item) { 807 return item->keybag; 808 } 809 810 bool SecDbItemSetKeybag(SecDbItemRef item, keybag_handle_t keybag, CFErrorRef *error) { 811 if (!SecDbItemEnsureDecrypted(item, true, error)) 812 return false; 813 if (item->keybag != keybag) { 814 item->keybag = keybag; 815 if (item->_edataState == kSecDbItemClean) { 816 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, NULL), kCFNull, NULL); 817 } 818 } 819 820 return true; 821 } 822 823 bool SecDbItemSetValue(SecDbItemRef item, const SecDbAttr *desc, CFTypeRef value, CFErrorRef *error) { 824 // Propagate chained errors. 825 if (!desc) 826 return false; 827 828 if (!value) 829 value = kCFNull; 830 831 if (desc->setValue) 832 return desc->setValue(item, desc, value, error); 833 834 if (desc->flags & kSecDbInCryptoDataFlag || desc->flags & kSecDbInAuthenticatedDataFlag) { 835 if (!SecDbItemEnsureDecrypted(item, true, error)) { 836 return false; 837 } 838 } 839 840 bool changed = false; 841 CFTypeRef attr = NULL; 842 switch (desc->kind) { 843 case kSecDbPrimaryKeyAttr: 844 case kSecDbDataAttr: 845 attr = copyData(value); 846 break; 847 case kSecDbEncryptedDataAttr: 848 attr = copyData(value); 849 if (attr) { 850 if (item->_edataState == kSecDbItemEncrypting) 851 item->_edataState = kSecDbItemClean; 852 else 853 item->_edataState = kSecDbItemEncrypted; 854 } else if (!value || CFEqual(kCFNull, value)) { 855 item->_edataState = kSecDbItemDirty; 856 } 857 break; 858 case kSecDbBlobAttr: 859 case kSecDbAccessControlAttr: 860 attr = copyBlob(value); 861 break; 862 case kSecDbDateAttr: 863 case kSecDbCreationDateAttr: 864 case kSecDbModificationDateAttr: 865 attr = copyDate(value); 866 break; 867 case kSecDbNumberAttr: 868 case kSecDbSyncAttr: 869 case kSecDbTombAttr: 870 case kSecDbRowIdAttr: 871 attr = copyNumber(value); 872 break; 873 case kSecDbAccessAttr: 874 case kSecDbStringAttr: 875 attr = copyString(value); 876 break; 877 case kSecDbSHA1Attr: 878 attr = copySHA1(value); 879 break; 880 case kSecDbUTombAttr: 881 attr = CFRetainSafe(asBoolean(value, NULL)); 882 break; 883 case kSecDbUUIDAttr: 884 attr = copyUUID(value); 885 break; 886 } 887 888 if (attr) { 889 CFTypeRef ovalue = CFDictionaryGetValue(item->attributes, desc->name); 890 changed = (!ovalue || !CFEqual(ovalue, attr)); 891 CFDictionarySetValue(item->attributes, desc->name, attr); 892 CFRelease(attr); 893 } else { 894 if (value && !CFEqual(kCFNull, value)) { 895 SecError(errSecItemInvalidValue, error, CFSTR("attribute %@: value: %@ failed to convert"), desc->name, value); 896 return false; 897 } 898 CFTypeRef ovalue = CFDictionaryGetValue(item->attributes, desc->name); 899 changed = (ovalue && !CFEqual(ovalue, kCFNull)); 900 CFDictionaryRemoveValue(item->attributes, desc->name); 901 } 902 903 if (changed) { 904 if (desc->flags & kSecDbInHashFlag) 905 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, NULL), kCFNull, NULL); 906 if (desc->flags & kSecDbPrimaryKeyFlag) 907 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbPrimaryKeyAttr, NULL), kCFNull, NULL); 908 if ((desc->flags & kSecDbInCryptoDataFlag || desc->flags & kSecDbInAuthenticatedDataFlag) && (item->_edataState == kSecDbItemClean || (item->_edataState == kSecDbItemSecretEncrypted && (desc->flags & kSecDbReturnDataFlag) == 0))) 909 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, NULL), kCFNull, NULL); 910 if (desc->flags & kSecDbSHA1ValueInFlag) 911 CFDictionaryRemoveValue(item->attributes, SecDbAttrGetHashName(desc)); 912 } 913 914 return true; 915 } 916 917 bool SecDbItemSetValues(SecDbItemRef item, CFDictionaryRef values, CFErrorRef *error) { 918 SecDbForEachAttr(item->class, attr) { 919 CFTypeRef value = CFDictionaryGetValue(values, attr->name); 920 if (value && !SecDbItemSetValue(item, attr, value, error)) 921 return false; 922 } 923 return true; 924 } 925 926 bool SecDbItemSetValueWithName(SecDbItemRef item, CFStringRef name, CFTypeRef value, CFErrorRef *error) { 927 SecDbForEachAttr(item->class, attr) { 928 if (CFEqual(attr->name, name)) { 929 return SecDbItemSetValue(item, attr, value, error); 930 } 931 } 932 return false; 933 } 934 935 bool SecItemPreserveAttribute(SecDbItemRef target, SecDbItemRef source, const SecDbAttr* attr) { 936 CFErrorRef cferror = nil; 937 CFTypeRef v = SecDbItemGetValue(source, attr, &cferror); 938 if(cferror) { 939 secnotice("secitem", "Merging: unable to get attribute (%@) : %@", attr->name, cferror); 940 CFReleaseNull(cferror); 941 return false; 942 } 943 if(!v || CFEqualSafe(v, kCFNull)) { 944 return true; 945 } 946 secnotice("secitem", "Preserving existing data for %@", attr->name); 947 SecDbItemSetValue(target, attr, v, &cferror); 948 if(cferror) { 949 secnotice("secitem", "Unable to set attribute (%@) : %@", attr->name, cferror); 950 CFReleaseNull(cferror); 951 return false; 952 } 953 return true; 954 } 955 956 957 bool SecDbItemSetAccessControl(SecDbItemRef item, SecAccessControlRef access_control, CFErrorRef *error) { 958 bool ok = true; 959 if (item->_edataState == kSecDbItemClean) 960 ok = SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, error), kCFNull, error); 961 if (ok && access_control) { //added check for access_control because ks_decrypt_data can leave NULL in access_control in case of error 962 item->_edataState = kSecDbItemDirty; 963 CFDataRef data = SecAccessControlCopyData(access_control); 964 ok = SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbAccessControlAttr, error), data, error); 965 CFRelease(data); 966 } 967 return ok; 968 } 969 970 SecDbItemRef SecDbItemCreateWithAttributes(CFAllocatorRef allocator, const SecDbClass *class, CFDictionaryRef attributes, keybag_handle_t keybag, CFErrorRef *error) { 971 SecDbItemRef item = SecDbItemCreate(kCFAllocatorDefault, class, keybag); 972 if (item && !SecDbItemSetValues(item, attributes, error)) 973 CFReleaseNull(item); 974 return item; 975 } 976 977 static CFTypeRef 978 SecDbColumnCopyValueWithAttr(CFAllocatorRef allocator, sqlite3_stmt *stmt, const SecDbAttr *attr, int col, CFErrorRef *error) { 979 CFTypeRef value = NULL; 980 switch (attr->kind) { 981 case kSecDbDateAttr: 982 case kSecDbCreationDateAttr: 983 case kSecDbModificationDateAttr: 984 value = SecDbColumnCopyDate(allocator, stmt, col, error); 985 break; 986 case kSecDbBlobAttr: 987 case kSecDbNumberAttr: 988 switch (sqlite3_column_type(stmt, col)) { 989 case SQLITE_INTEGER: 990 value = SecDbColumnCopyNumber(allocator, stmt, col, error); 991 break; 992 case SQLITE_FLOAT: 993 value = SecDbColumnCopyDouble(allocator, stmt, col, error); 994 break; 995 case SQLITE_TEXT: 996 value = SecDbColumnCopyString(allocator, stmt, col, error, 997 attr->flags); 998 break; 999 case SQLITE_BLOB: 1000 value = SecDbColumnCopyData(allocator, stmt, col, error); 1001 break; 1002 case SQLITE_NULL: 1003 value = kCFNull; 1004 break; 1005 } 1006 break; 1007 case kSecDbAccessAttr: 1008 case kSecDbStringAttr: 1009 value = SecDbColumnCopyString(allocator, stmt, col, error, 1010 attr->flags); 1011 break; 1012 case kSecDbDataAttr: 1013 case kSecDbUUIDAttr: 1014 case kSecDbSHA1Attr: 1015 case kSecDbPrimaryKeyAttr: 1016 case kSecDbEncryptedDataAttr: 1017 value = SecDbColumnCopyData(allocator, stmt, col, error); 1018 break; 1019 case kSecDbSyncAttr: 1020 case kSecDbTombAttr: 1021 value = SecDbColumnCopyNumber(allocator, stmt, col, error); 1022 break; 1023 case kSecDbRowIdAttr: 1024 value = SecDbColumnCopyNumber64(allocator, stmt, col, error); 1025 break; 1026 case kSecDbAccessControlAttr: 1027 case kSecDbUTombAttr: 1028 /* This attributes does not have any database column associated, exists only inside encrypted blob as metadata. */ 1029 break; 1030 } 1031 return value; 1032 } 1033 1034 SecDbItemRef SecDbItemCreateWithStatement(CFAllocatorRef allocator, const SecDbClass *class, sqlite3_stmt *stmt, keybag_handle_t keybag, CFErrorRef *error, bool (^return_attr)(const SecDbAttr *attr)) { 1035 SecDbItemRef item = SecDbItemCreate(allocator, class, keybag); 1036 int col = 0; 1037 SecDbForEachAttr(class, attr) { 1038 if (return_attr(attr)) { 1039 CFTypeRef value = SecDbColumnCopyValueWithAttr(allocator, stmt, attr, col++, error); 1040 require_action_quiet(value, errOut, CFReleaseNull(item)); 1041 1042 CFDictionarySetValue(item->attributes, SecDbAttrGetHashName(attr), value); 1043 CFRelease(value); 1044 } 1045 1046 const SecDbAttr *data_attr = SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr, NULL); 1047 if (data_attr != NULL && CFDictionaryGetValue(item->attributes, data_attr->name) != NULL) { 1048 item->_edataState = kSecDbItemEncrypted; 1049 } 1050 } 1051 1052 errOut: 1053 return item; 1054 } 1055 1056 SecDbItemRef SecDbItemCreateWithEncryptedData(CFAllocatorRef allocator, const SecDbClass *class, 1057 CFDataRef edata, keybag_handle_t keybag, CFErrorRef *error) { 1058 SecDbItemRef item = SecDbItemCreate(allocator, class, keybag); 1059 const SecDbAttr *edata_attr = SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr, error); 1060 if (edata_attr) { 1061 if (!SecDbItemSetValue(item, edata_attr, edata, error)) 1062 CFReleaseNull(item); 1063 } 1064 return item; 1065 } 1066 1067 // TODO: Hack -- Replace with real filtering 1068 1069 // Return true iff an item for which SecDbItemIsSyncable() already returns true should be part of the v2 view. 1070 bool SecDbItemInV2(SecDbItemRef item) { 1071 const SecDbClass *iclass = SecDbItemGetClass(item); 1072 return (SecDbItemGetCachedValueWithName(item, kSecAttrSyncViewHint) == NULL && 1073 (iclass == genp_class() || iclass == inet_class() || iclass == keys_class() || iclass == cert_class())); 1074 } 1075 1076 // Return true iff an item for which SecDbItemIsSyncable() and SecDbItemInV2() already return true should be part of the v0 view. 1077 bool SecDbItemInV2AlsoInV0(SecDbItemRef item) { 1078 return (SecDbItemGetCachedValueWithName(item, kSecAttrTokenID) == NULL && SecDbItemGetClass(item) != cert_class()); 1079 } 1080 1081 SecDbItemRef SecDbItemCopyWithUpdates(SecDbItemRef item, CFDictionaryRef updates, CFErrorRef *error) { 1082 SecDbItemRef new_item = SecDbItemCreate(CFGetAllocator(item), item->class, item->keybag); 1083 SecDbItemSetCredHandle(new_item, item->credHandle); 1084 SecDbForEachAttr(item->class, attr) { 1085 // Copy each attribute, except the mod date attribute (it will be reset to now when needed), 1086 // from the updates dict unless it's not there in which case we copy the attribute from the passed in item. 1087 if (attr->kind != kSecDbModificationDateAttr && attr->kind != kSecDbEncryptedDataAttr && attr->kind != kSecDbSHA1Attr && attr->kind != kSecDbPrimaryKeyAttr) { 1088 CFTypeRef value = NULL; 1089 if (CFDictionaryGetValueIfPresent(updates, attr->name, &value)) { 1090 if (!value) 1091 SecError(errSecParam, error, CFSTR("NULL value in dictionary")); 1092 } else { 1093 value = SecDbItemGetValue(item, attr, error); 1094 } 1095 if (!value || !SecDbItemSetValue(new_item, attr, value, error)) { 1096 CFReleaseNull(new_item); 1097 break; 1098 } 1099 } 1100 } 1101 return new_item; 1102 } 1103 1104 // Ensure that the date value of attr of new_item is greater than that of old_item. 1105 static bool SecDbItemMakeAttrYounger(SecDbItemRef new_item, SecDbItemRef old_item, const SecDbAttr *attr, CFErrorRef *error) { 1106 CFDateRef old_date = SecDbItemGetValue(old_item, attr, error); 1107 if (!old_date) 1108 return false; 1109 CFDateRef new_date = SecDbItemGetValue(new_item, attr, error); 1110 if (!new_date) 1111 return false; 1112 bool ok = true; 1113 if (CFDateCompare(new_date, old_date, NULL) != kCFCompareGreaterThan) { 1114 CFDateRef adjusted_date = CFDateCreate(kCFAllocatorDefault, CFDateGetAbsoluteTime(old_date) + 0.001); 1115 if (adjusted_date) { 1116 ok = SecDbItemSetValue(new_item, attr, adjusted_date, error); 1117 CFRelease(adjusted_date); 1118 } 1119 } 1120 return ok; 1121 } 1122 1123 // Ensure that the mod date of new_item is greater than that of old_item. 1124 static bool SecDbItemMakeYounger(SecDbItemRef new_item, SecDbItemRef old_item, CFErrorRef *error) { 1125 const SecDbAttr *attr = SecDbClassAttrWithKind(new_item->class, kSecDbModificationDateAttr, error); 1126 return attr && SecDbItemMakeAttrYounger(new_item, old_item, attr, error); 1127 } 1128 1129 static SecDbItemRef SecDbItemCopyTombstone(SecDbItemRef item, CFBooleanRef makeTombStone, bool tombstone_time_from_item, CFErrorRef *error) { 1130 SecDbItemRef new_item = SecDbItemCreate(CFGetAllocator(item), item->class, item->keybag); 1131 SecDbForEachAttr(item->class, attr) { 1132 if (attr->kind == kSecDbTombAttr) { 1133 // Set the tomb attr to true to indicate a tombstone. 1134 if (!SecDbItemSetValue(new_item, attr, kCFBooleanTrue, error)) { 1135 CFReleaseNull(new_item); 1136 break; 1137 } 1138 } else if (SecDbIsTombstoneDbUpdateAttr(attr)) { 1139 // Copy all primary key attributes and creation timestamps from the original item. 1140 CFTypeRef value = SecDbItemGetValue(item, attr, error); 1141 if (!value || (!CFEqual(kCFNull, value) && !SecDbItemSetValue(new_item, attr, value, error))) { 1142 CFReleaseNull(new_item); 1143 break; 1144 } 1145 } else if (attr->kind == kSecDbModificationDateAttr) { 1146 if(tombstone_time_from_item) { 1147 SecItemPreserveAttribute(new_item, item, attr); 1148 } 1149 1150 if (!SecDbItemMakeAttrYounger(new_item, item, attr, error)) { 1151 CFReleaseNull(new_item); 1152 break; 1153 } 1154 } else if (makeTombStone && attr->kind == kSecDbUTombAttr) { 1155 if (makeTombStone) 1156 SecDbItemSetValue(new_item, attr, makeTombStone, error); 1157 } 1158 } 1159 1160 return new_item; 1161 } 1162 1163 bool SecDbItemIsEngineInternalState(SecDbItemRef itemObject) { 1164 // Only used for controlling logging 1165 // Use agrp=com.apple.security.sos, since it is not encrypted 1166 if (!itemObject) { 1167 return false; 1168 } 1169 const SecDbAttr *agrp = SecDbAttrWithKey(SecDbItemGetClass(itemObject), kSecAttrAccessGroup, NULL); 1170 CFTypeRef cfval = SecDbItemGetValue(itemObject, agrp, NULL); 1171 return cfval && CFStringCompareSafe(cfval, kSOSInternalAccessGroup, NULL) == kCFCompareEqualTo; 1172 } 1173 1174 1175 // MARK: - 1176 // MARK: SQL Construction helpers -- These should become private in the future 1177 1178 void SecDbAppendElement(CFMutableStringRef sql, CFStringRef value, bool *needComma) { 1179 assert(needComma); 1180 if (*needComma) { 1181 CFStringAppend(sql, CFSTR(",")); 1182 } else { 1183 *needComma = true; 1184 } 1185 CFStringAppend(sql, value); 1186 } 1187 1188 static void SecDbAppendElementEquals(CFMutableStringRef sql, CFStringRef value, bool *needComma) { 1189 SecDbAppendElement(sql, value, needComma); 1190 CFStringAppend(sql, CFSTR("=?")); 1191 } 1192 1193 /* Append AND is needWhere is NULL or *needWhere is false. Append WHERE 1194 otherwise. Upon return *needWhere will be false. */ 1195 void 1196 SecDbAppendWhereOrAnd(CFMutableStringRef sql, bool *needWhere) { 1197 if (!needWhere || !*needWhere) { 1198 CFStringAppend(sql, CFSTR(" AND ")); 1199 } else { 1200 CFStringAppend(sql, CFSTR(" WHERE ")); 1201 *needWhere = false; 1202 } 1203 } 1204 1205 void 1206 SecDbAppendWhereOrAndEquals(CFMutableStringRef sql, CFStringRef col, bool *needWhere) { 1207 SecDbAppendWhereOrAnd(sql, needWhere); 1208 CFStringAppend(sql, col); 1209 CFStringAppend(sql, CFSTR("=?")); 1210 } 1211 1212 void 1213 SecDbAppendWhereOrAndNotEquals(CFMutableStringRef sql, CFStringRef col, bool *needWhere) { 1214 SecDbAppendWhereOrAnd(sql, needWhere); 1215 CFStringAppend(sql, col); 1216 CFStringAppend(sql, CFSTR("!=?")); 1217 } 1218 1219 static void SecDbAppendCountArgsAndCloseParen(CFMutableStringRef sql, CFIndex count) { 1220 bool needComma = false; 1221 while (count-- > 0) 1222 SecDbAppendElement(sql, CFSTR("?"), &needComma); 1223 CFStringAppend(sql, CFSTR(")")); 1224 } 1225 1226 void 1227 SecDbAppendWhereOrAndIn(CFMutableStringRef sql, CFStringRef col, bool *needWhere, CFIndex count) { 1228 if (count == 1) 1229 return SecDbAppendWhereOrAndEquals(sql, col, needWhere); 1230 SecDbAppendWhereOrAnd(sql, needWhere); 1231 CFStringAppend(sql, col); 1232 CFStringAppend(sql, CFSTR(" IN (")); 1233 SecDbAppendCountArgsAndCloseParen(sql, count); 1234 } 1235 1236 void 1237 SecDbAppendWhereOrAndNotIn(CFMutableStringRef sql, CFStringRef col, bool *needWhere, CFIndex count) { 1238 if (count == 1) 1239 return SecDbAppendWhereOrAndNotEquals(sql, col, needWhere); 1240 SecDbAppendWhereOrAnd(sql, needWhere); 1241 CFStringAppend(sql, col); 1242 CFStringAppend(sql, CFSTR(" NOT IN (")); 1243 SecDbAppendCountArgsAndCloseParen(sql, count); 1244 } 1245 1246 static CFStringRef SecDbItemCopyInsertSQL(SecDbItemRef item, bool(^use_attr)(const SecDbAttr *attr)) { 1247 CFMutableStringRef sql = CFStringCreateMutable(CFGetAllocator(item), 0); 1248 CFStringAppend(sql, CFSTR("INSERT INTO ")); 1249 CFStringAppend(sql, item->class->name); 1250 CFStringAppend(sql, CFSTR("(")); 1251 bool needComma = false; 1252 CFIndex used_attr = 0; 1253 SecDbForEachAttr(item->class, attr) { 1254 if (use_attr(attr)) { 1255 ++used_attr; 1256 SecDbAppendElement(sql, attr->name, &needComma); 1257 } 1258 } 1259 CFStringAppend(sql, CFSTR(")VALUES(?")); 1260 while (used_attr-- > 1) { 1261 CFStringAppend(sql, CFSTR(",?")); 1262 } 1263 CFStringAppend(sql, CFSTR(")")); 1264 return sql; 1265 1266 } 1267 1268 static bool SecDbItemInsertBind(SecDbItemRef item, sqlite3_stmt *stmt, CFErrorRef *error, bool(^use_attr)(const SecDbAttr *attr)) { 1269 bool ok = true; 1270 int param = 0; 1271 SecDbForEachAttr(item->class, attr) { 1272 if (use_attr(attr)) { 1273 CFTypeRef value = SecDbItemCopyValueForDb(item, attr, error); 1274 ok = value && SecDbBindObject(stmt, ++param, value, error); 1275 CFReleaseSafe(value); 1276 if (!ok) 1277 break; 1278 } 1279 } 1280 return ok; 1281 } 1282 1283 sqlite3_int64 SecDbItemGetRowId(SecDbItemRef item, CFErrorRef *error) { 1284 sqlite3_int64 row_id = 0; 1285 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error); 1286 if (attr) { 1287 CFNumberRef number = SecDbItemGetValue(item, attr, error); 1288 if (!isNumber(number)|| !CFNumberGetValue(number, kCFNumberSInt64Type, &row_id)) 1289 SecDbError(SQLITE_ERROR, error, CFSTR("rowid %@ is not a 64 bit number"), number); 1290 } 1291 1292 return row_id; 1293 } 1294 1295 static CFNumberRef SecDbItemCreateRowId(SecDbItemRef item, sqlite3_int64 rowid, CFErrorRef *error) { 1296 return CFNumberCreate(CFGetAllocator(item), kCFNumberSInt64Type, &rowid); 1297 } 1298 1299 bool SecDbItemSetRowId(SecDbItemRef item, sqlite3_int64 rowid, CFErrorRef *error) { 1300 bool ok = true; 1301 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error); 1302 if (attr) { 1303 CFNumberRef value = SecDbItemCreateRowId(item, rowid, error); 1304 if (!value) 1305 return false; 1306 1307 ok = SecDbItemSetValue(item, attr, value, error); 1308 CFRelease(value); 1309 } 1310 return ok; 1311 } 1312 1313 bool SecDbItemClearRowId(SecDbItemRef item, CFErrorRef *error) { 1314 bool ok = true; 1315 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error); 1316 if (attr) { 1317 CFDictionaryRemoveValue(item->attributes, attr->name); 1318 //ok = SecDbItemSetValue(item, attr, kCFNull, error); 1319 } 1320 return ok; 1321 } 1322 1323 static bool SecDbItemSetLastInsertRowId(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) { 1324 sqlite3_int64 rowid = sqlite3_last_insert_rowid(SecDbHandle(dbconn)); 1325 return SecDbItemSetRowId(item, rowid, error); 1326 } 1327 1328 bool SecDbItemIsSyncableOrCorrupted(SecDbItemRef item) { 1329 bool is_syncable_or_corrupted = false; 1330 CFErrorRef localError = NULL; 1331 if (!SecDbItemGetBoolValue(item, SecDbClassAttrWithKind(item->class, kSecDbSyncAttr, &localError), 1332 &is_syncable_or_corrupted, &localError)) { 1333 is_syncable_or_corrupted = SecErrorGetOSStatus(localError) == errSecDecode; 1334 } 1335 CFReleaseSafe(localError); 1336 return is_syncable_or_corrupted; 1337 } 1338 1339 bool SecDbItemIsSyncable(SecDbItemRef item) { 1340 bool is_syncable; 1341 if (SecDbItemGetBoolValue(item, SecDbClassAttrWithKind(item->class, kSecDbSyncAttr, NULL), &is_syncable, NULL)) 1342 return is_syncable; 1343 return false; 1344 } 1345 1346 bool SecDbItemSetSyncable(SecDbItemRef item, bool sync, CFErrorRef *error) 1347 { 1348 return SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbSyncAttr, error), sync ? kCFBooleanTrue : kCFBooleanFalse, error); 1349 } 1350 1351 bool SecDbItemIsTombstone(SecDbItemRef item) { 1352 bool is_tomb; 1353 if (SecDbItemGetBoolValue(item, SecDbClassAttrWithKind(item->class, kSecDbTombAttr, NULL), &is_tomb, NULL)) 1354 return is_tomb; 1355 return false; 1356 } 1357 1358 CFDataRef SecDbItemGetPrimaryKey(SecDbItemRef item, CFErrorRef *error) { 1359 return SecDbItemGetValue(item, SecDbClassAttrWithKind(item->class, kSecDbPrimaryKeyAttr, error), error); 1360 } 1361 1362 CFDataRef SecDbItemGetSHA1(SecDbItemRef item, CFErrorRef *error) { 1363 return SecDbItemGetValue(item, SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, error), error); 1364 } 1365 1366 static SecDbQueryRef SecDbQueryCreateWithItemPrimaryKey(SecDbItemRef item, CFErrorRef *error) { 1367 CFMutableDictionaryRef dict = SecDbItemCopyPListWithMask(item, kSecDbPrimaryKeyFlag, error); 1368 if (!dict) 1369 return NULL; 1370 1371 SecDbQueryRef query = query_create(item->class, NULL, NULL, NULL, error); 1372 if (query) { 1373 CFReleaseSafe(query->q_item); 1374 query->q_item = dict; 1375 } 1376 else 1377 CFRelease(dict); 1378 1379 return query; 1380 } 1381 1382 static bool SecDbItemIsCorrupt(SecDbItemRef item, bool *is_corrupt, CFErrorRef *error) { 1383 CFErrorRef localError = NULL; 1384 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications. 1385 const struct SecDbAttr *sha1attr = SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, &localError); 1386 CFDataRef storedSHA1 = CFRetainSafe(SecDbItemGetValue(item, sha1attr, &localError)); 1387 bool akpu = false; 1388 1389 if (localError || !SecDbItemEnsureDecrypted(item, true, &localError)) { 1390 if (SecErrorGetOSStatus(localError) == errSecDecode) { 1391 // We failed to decrypt the item 1392 const SecDbAttr *desc = SecDbClassAttrWithKind(item->class, kSecDbAccessControlAttr, &localError); 1393 SecAccessControlRef accc = NULL; 1394 CFDataRef acccData = NULL; 1395 1396 acccData = (CFDataRef)SecDbItemGetValue(item, desc, &localError); 1397 if (isData(acccData)) { 1398 accc = SecAccessControlCreateFromData(CFGetAllocator(item), acccData, &localError); 1399 } 1400 1401 if (accc && CFEqualSafe(SecAccessControlGetProtection(accc), kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly)) { 1402 akpu = true; 1403 secwarning("cannot decrypt item %@, item is irrecoverably lost with older passcode (error %@)", item, localError); 1404 } else { 1405 secerror("error %@ reading item %@ (corrupted)", localError, item); 1406 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem); 1407 } 1408 CFReleaseNull(localError); 1409 *is_corrupt = true; 1410 } 1411 } 1412 1413 // Recompute sha1 hash attribute and compare with the cached one. 1414 CFDataRef computedSHA1 = SecDbItemCopyValue(item, sha1attr, &localError); 1415 if (storedSHA1 && computedSHA1 && !CFEqual(storedSHA1, computedSHA1)) { 1416 CFStringRef storedHex = CFDataCopyHexString(storedSHA1), computedHex = CFDataCopyHexString(computedSHA1); 1417 secerror("error %@ %@ != %@ item %@ (corrupted)", sha1attr->name, storedHex, computedHex, item); 1418 // Do not simulate crash for this condition. 1419 // The keychain hashes floating point numbers which causes many false positives, this is not fixable except by major surgery 1420 CFReleaseSafe(storedHex); 1421 CFReleaseSafe(computedHex); 1422 *is_corrupt = true; 1423 } 1424 1425 // Sanity check that all attributes that must not be NULL actually aren't 1426 if (!localError) SecDbForEachAttr(item->class, attr) { 1427 if (attr->flags & (kSecDbInCryptoDataFlag | kSecDbInAuthenticatedDataFlag)) { 1428 CFTypeRef value = SecDbItemGetValue(item, attr, &localError); 1429 if (value) { 1430 if (CFEqual(kCFNull, value) && attr->flags & kSecDbNotNullFlag) { 1431 secerror("error attribute %@ has NULL value in item %@ (corrupted)", attr->name, item); 1432 __security_simulatecrash(CFSTR("Corrupted item (attr NULL) found in keychain"), __sec_exception_code_CorruptItem); 1433 *is_corrupt = true; 1434 break; 1435 } 1436 } else { 1437 if (SecErrorGetOSStatus(localError) == errSecDecode) { 1438 // We failed to decrypt the item 1439 if (akpu) { 1440 secwarning("attribute %@: %@ item %@ (item lost with older passcode)", attr->name, localError, item); 1441 } else { 1442 secerror("error attribute %@: %@ item %@ (corrupted)", attr->name, localError, item); 1443 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem); 1444 } 1445 *is_corrupt = true; 1446 CFReleaseNull(localError); 1447 } 1448 break; 1449 } 1450 } 1451 } 1452 1453 CFReleaseSafe(computedSHA1); 1454 CFReleaseSafe(storedSHA1); 1455 return SecErrorPropagate(localError, error); 1456 } 1457 1458 static void SecDbItemRecordUpdate(SecDbConnectionRef dbconn, SecDbItemRef deleted, SecDbItemRef inserted) { 1459 SecDbRecordChange(dbconn, deleted, inserted); 1460 } 1461 1462 static bool SecDbItemDoInsert(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) { 1463 bool (^use_attr)(const SecDbAttr *attr) = ^bool(const SecDbAttr *attr) { 1464 return (attr->flags & kSecDbInFlag); 1465 }; 1466 1467 if (!SecDbItemEnsureDecrypted(item, true, error)) { 1468 return false; 1469 } 1470 1471 CFStringRef sql = SecDbItemCopyInsertSQL(item, use_attr); 1472 __block bool ok = sql; 1473 if (sql) { 1474 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) { 1475 ok = (SecDbItemInsertBind(item, stmt, error, use_attr) && 1476 SecDbStep(dbconn, stmt, error, NULL) && 1477 SecDbItemSetLastInsertRowId(item, dbconn, error)); 1478 }); 1479 CFRelease(sql); 1480 } 1481 if (ok) { 1482 secnotice("item", "inserted %@", item); 1483 SecDbItemRecordUpdate(dbconn, NULL, item); 1484 } else { 1485 if (SecDbItemIsEngineInternalState(item)) { 1486 secdebug ("item", "insert failed for item %@ with %@", item, error ? *error : NULL); 1487 } else { 1488 secnotice("item", "insert failed for item %@ with %@", item, error ? *error : NULL); 1489 } 1490 } 1491 1492 return ok; 1493 } 1494 1495 bool SecErrorIsSqliteDuplicateItemError(CFErrorRef error) { 1496 return error && CFErrorGetCode(error) == SQLITE_CONSTRAINT && CFEqual(kSecDbErrorDomain, CFErrorGetDomain(error)); 1497 } 1498 1499 bool SecDbItemInsertOrReplace(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error, void(^duplicate)(SecDbItemRef item, SecDbItemRef *replace)) { 1500 __block CFErrorRef localError = NULL; 1501 __block bool ok = SecDbItemDoInsert(item, dbconn, &localError); 1502 if (!ok && localError && CFErrorGetCode(localError) == SQLITE_CONSTRAINT && CFEqual(kSecDbErrorDomain, CFErrorGetDomain(localError))) { 1503 SecDbQueryRef query = SecDbQueryCreateWithItemPrimaryKey(item, error); 1504 if (query) { 1505 CFRetainAssign(query->q_use_cred_handle, item->credHandle); 1506 SecDbItemSelect(query, dbconn, error, NULL, ^bool(const SecDbAttr *attr) { 1507 return attr->flags & kSecDbPrimaryKeyFlag; 1508 }, NULL, NULL, ^(SecDbItemRef old_item, bool *stop) { 1509 bool is_corrupt = false; 1510 ok = SecDbItemIsCorrupt(old_item, &is_corrupt, error); 1511 SecDbItemRef replace = NULL; 1512 if (is_corrupt) { 1513 // If old_item is corrupted pretend it's not there and just replace it. 1514 replace = item; 1515 CFRetain(replace); 1516 if(error) 1517 CFReleaseNull(*error); //item is corrupted and will be replaced, so drop the error 1518 } else if (ok && duplicate) { 1519 duplicate(old_item, &replace); 1520 } 1521 if (replace) { 1522 const SecDbAttr *rowid_attr = SecDbClassAttrWithKind(old_item->class, kSecDbRowIdAttr, error); 1523 CFNumberRef oldrowid = SecDbItemGetCachedValue(old_item, rowid_attr); 1524 if (oldrowid) { 1525 ok = SecDbItemSetValue(replace, rowid_attr, oldrowid, &localError); 1526 if (ok && !is_corrupt) { 1527 ok = SecDbItemMakeYounger(replace, old_item, error); 1528 } 1529 ok = ok && SecDbItemDoUpdate(old_item, replace, dbconn, &localError, ^bool (const SecDbAttr *attr) { 1530 return attr->kind == kSecDbRowIdAttr; 1531 }); 1532 } else { 1533 ok = SecError(errSecInternal, &localError, CFSTR("no rowid for %@"), old_item); 1534 } 1535 CFRelease(replace); 1536 if (ok) 1537 CFReleaseNull(localError); // Clear the error, since we replaced the item. 1538 } 1539 }); 1540 SecDbItemSetCredHandle(item, query->q_use_cred_handle); 1541 ok &= query_destroy(query, error); 1542 } 1543 } 1544 1545 return ok & SecErrorPropagate(localError, error); // Don't use && here! 1546 } 1547 1548 bool SecDbItemInsert(SecDbItemRef item, SecDbConnectionRef dbconn, bool always_use_uuid_from_new_item, CFErrorRef *error) { 1549 return SecDbItemInsertOrReplace(item, dbconn, error, ^(SecDbItemRef old_item, SecDbItemRef *replace) { 1550 if (SecDbItemIsTombstone(old_item)) { 1551 CFRetain(item); 1552 *replace = item; 1553 1554 // If the caller doesn't care about the UUID, then use old_item's UUID 1555 // Note: this will modify item! 1556 if(!always_use_uuid_from_new_item) { 1557 SecDbForEachAttr(SecDbItemGetClass(item), attr) { 1558 if(CFEqual(attr->name, v10itemuuid.name)) { 1559 SecItemPreserveAttribute(item, old_item, attr); 1560 } 1561 } 1562 } 1563 } 1564 }); 1565 } 1566 1567 static CFStringRef SecDbItemCopyUpdateSQL(SecDbItemRef old_item, SecDbItemRef new_item, bool(^use_attr_in_where)(const SecDbAttr *attr)) { 1568 CFMutableStringRef sql = CFStringCreateMutable(CFGetAllocator(new_item), 0); 1569 CFStringAppend(sql, CFSTR("UPDATE ")); 1570 CFStringAppend(sql, new_item->class->name); 1571 CFStringAppend(sql, CFSTR(" SET ")); 1572 bool needComma = false; 1573 CFIndex used_attr = 0; 1574 SecDbForEachAttrWithMask(new_item->class, attr, kSecDbInFlag) { 1575 ++used_attr; 1576 SecDbAppendElementEquals(sql, attr->name, &needComma); 1577 } 1578 1579 bool needWhere = true; 1580 SecDbForEachAttr(old_item->class, attr) { 1581 if (use_attr_in_where(attr)) { 1582 SecDbAppendWhereOrAndEquals(sql, attr->name, &needWhere); 1583 } 1584 } 1585 1586 return sql; 1587 } 1588 1589 static bool SecDbItemUpdateBind(SecDbItemRef old_item, SecDbItemRef new_item, sqlite3_stmt *stmt, CFErrorRef *error, bool(^use_attr_in_where)(const SecDbAttr *attr)) { 1590 bool ok = true; 1591 int param = 0; 1592 SecDbForEachAttrWithMask(new_item->class, attr, kSecDbInFlag) { 1593 CFTypeRef value = SecDbItemCopyValueForDb(new_item, attr, error); 1594 ok &= value && SecDbBindObject(stmt, ++param, value, error); 1595 CFReleaseSafe(value); 1596 if (!ok) 1597 break; 1598 } 1599 SecDbForEachAttr(old_item->class, attr) { 1600 if (use_attr_in_where(attr)) { 1601 CFTypeRef value = SecDbItemCopyValueForDb(old_item, attr, error); 1602 ok &= value && SecDbBindObject(stmt, ++param, value, error); 1603 CFReleaseSafe(value); 1604 if (!ok) 1605 break; 1606 } 1607 } 1608 return ok; 1609 } 1610 1611 // Primary keys are the same -- do an update 1612 bool SecDbItemDoUpdate(SecDbItemRef old_item, SecDbItemRef new_item, SecDbConnectionRef dbconn, CFErrorRef *error, bool (^use_attr_in_where)(const SecDbAttr *attr)) { 1613 CFStringRef sql = SecDbItemCopyUpdateSQL(old_item, new_item, use_attr_in_where); 1614 __block bool ok = sql; 1615 if (sql) { 1616 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) { 1617 ok = SecDbItemUpdateBind(old_item, new_item, stmt, error, use_attr_in_where) && SecDbStep(dbconn, stmt, error, NULL); 1618 }); 1619 CFRelease(sql); 1620 } 1621 if (ok) { 1622 if (SecDbItemIsEngineInternalState(old_item)) { 1623 secdebug ("item", "replaced %@ in %@", old_item, dbconn); 1624 secdebug ("item", " with %@ in %@", new_item, dbconn); 1625 } else { 1626 secnotice("item", "replaced %@ in %@", old_item, dbconn); 1627 secnotice("item", " with %@ in %@", new_item, dbconn); 1628 } 1629 SecDbItemRecordUpdate(dbconn, old_item, new_item); 1630 } 1631 return ok; 1632 } 1633 1634 static CFStringRef SecDbItemCopyDeleteSQL(SecDbItemRef item, bool(^use_attr_in_where)(const SecDbAttr *attr)) { 1635 CFMutableStringRef sql = CFStringCreateMutable(CFGetAllocator(item), 0); 1636 CFStringAppend(sql, CFSTR("DELETE FROM ")); 1637 CFStringAppend(sql, item->class->name); 1638 bool needWhere = true; 1639 SecDbForEachAttr(item->class, attr) { 1640 if (use_attr_in_where(attr)) { 1641 SecDbAppendWhereOrAndEquals(sql, attr->name, &needWhere); 1642 } 1643 } 1644 1645 return sql; 1646 } 1647 1648 static bool SecDbItemDeleteBind(SecDbItemRef item, sqlite3_stmt *stmt, CFErrorRef *error, bool(^use_attr_in_where)(const SecDbAttr *attr)) { 1649 bool ok = true; 1650 int param = 0; 1651 SecDbForEachAttr(item->class, attr) { 1652 if (use_attr_in_where(attr)) { 1653 CFTypeRef value = SecDbItemCopyValueForDb(item, attr, error); 1654 ok &= value && SecDbBindObject(stmt, ++param, value, error); 1655 CFReleaseSafe(value); 1656 if (!ok) 1657 break; 1658 } 1659 } 1660 return ok; 1661 } 1662 1663 static bool SecDbItemDoDeleteOnly(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error, bool (^use_attr_in_where)(const SecDbAttr *attr)) { 1664 CFStringRef sql = SecDbItemCopyDeleteSQL(item, use_attr_in_where); 1665 __block bool ok = sql; 1666 if (sql) { 1667 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) { 1668 ok = SecDbItemDeleteBind(item, stmt, error, use_attr_in_where) && SecDbStep(dbconn, stmt, error, NULL); 1669 }); 1670 CFRelease(sql); 1671 } 1672 return ok; 1673 } 1674 1675 bool SecDbItemDoDeleteSilently(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) { 1676 return SecDbItemDoDeleteOnly(item, dbconn, error, ^bool(const SecDbAttr *attr) { 1677 return attr->kind == kSecDbRowIdAttr; 1678 }); 1679 } 1680 1681 static bool SecDbItemDoDelete(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error, bool (^use_attr_in_where)(const SecDbAttr *attr)) { 1682 bool ok = SecDbItemDoDeleteOnly(item, dbconn, error, use_attr_in_where); 1683 if (ok) { 1684 secnotice("item", "deleted %@ from %@", item, dbconn); 1685 SecDbItemRecordUpdate(dbconn, item, NULL); 1686 } 1687 return ok; 1688 } 1689 1690 #if 0 1691 static bool SecDbItemDeleteTombstone(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) { 1692 bool ok = true; 1693 // TODO: Treat non decryptable items like tombstones here too and delete them 1694 SecDbItemRef tombstone = SecDbItemCopyTombstone(item, error); 1695 ok = tombstone; 1696 if (tombstone) { 1697 ok = SecDbItemClearRowId(tombstone, error); 1698 if (ok) { 1699 ok = SecDbItemDoDelete(tombstone, dbconn, error, ^bool (const SecDbAttr *attr) { 1700 return SecDbIsTombstoneDbSelectAttr(attr); 1701 }); 1702 } 1703 CFRelease(tombstone); 1704 } 1705 return ok; 1706 } 1707 #endif 1708 1709 static bool 1710 isCKKSEnabled(void) 1711 { 1712 #if OCTAGON 1713 return SecCKKSIsEnabled(); 1714 #else 1715 return false; 1716 #endif 1717 } 1718 1719 // Replace old_item with new_item. If primary keys are the same this does an update otherwise it does a delete + add 1720 bool SecDbItemUpdate(SecDbItemRef old_item, SecDbItemRef new_item, SecDbConnectionRef dbconn, CFBooleanRef makeTombstone, bool uuid_from_primary_key, CFErrorRef *error) { 1721 __block bool ok = true; 1722 __block CFErrorRef localError = NULL; 1723 1724 CFDataRef old_pk = SecDbItemGetPrimaryKey(old_item, error); 1725 CFDataRef new_pk = SecDbItemGetPrimaryKey(new_item, error); 1726 1727 ok = old_pk && new_pk; 1728 1729 bool pk_equal = ok && CFEqual(old_pk, new_pk); 1730 if (pk_equal) { 1731 ok = SecDbItemMakeYounger(new_item, old_item, error); 1732 } else if(!CFEqualSafe(makeTombstone, kCFBooleanFalse) && isCKKSEnabled()) { 1733 // The primary keys aren't equal, and we're going to make a tombstone. 1734 // Help CKKS out: the tombstone should have the existing item's UUID, and the newly updated item should have a new UUID. 1735 1736 s3dl_item_make_new_uuid(new_item, uuid_from_primary_key, error); 1737 } 1738 ok = ok && SecDbItemDoUpdate(old_item, new_item, dbconn, &localError, ^bool(const SecDbAttr *attr) { 1739 return attr->kind == kSecDbRowIdAttr; 1740 }); 1741 1742 if (localError) { 1743 if(CFErrorGetCode(localError) == SQLITE_CONSTRAINT && CFEqual(kSecDbErrorDomain, CFErrorGetDomain(localError))) { 1744 /* Update failed because we changed the PrimaryKey and there was a dup. 1745 Find the dup and see if it is a tombstone or corrupted item. */ 1746 SecDbQueryRef query = SecDbQueryCreateWithItemPrimaryKey(new_item, error); 1747 ok = query; 1748 if (query) { 1749 ok &= SecDbItemSelect(query, dbconn, error, NULL, ^bool(const SecDbAttr *attr) { 1750 return attr->flags & kSecDbPrimaryKeyFlag; 1751 }, NULL, NULL, ^(SecDbItemRef duplicate_item, bool *stop) { 1752 bool is_corrupt = false; 1753 bool is_tomb = false; 1754 ok = SecDbItemIsCorrupt(duplicate_item, &is_corrupt, error); 1755 if (ok && !is_corrupt) { 1756 if ((is_tomb = SecDbItemIsTombstone(duplicate_item))) 1757 ok = SecDbItemMakeYounger(new_item, duplicate_item, error); 1758 } 1759 if (ok && (is_corrupt || is_tomb)) { 1760 ok = SecDbItemDoDelete(old_item, dbconn, error, ^bool (const SecDbAttr *attr) { 1761 return attr->kind == kSecDbRowIdAttr; 1762 }); 1763 ok = ok && SecDbItemDoUpdate(duplicate_item, new_item, dbconn, error, ^bool (const SecDbAttr *attr) { 1764 return attr->kind == kSecDbRowIdAttr; 1765 }); 1766 CFReleaseNull(localError); 1767 } 1768 }); 1769 ok &= query_destroy(query, error); 1770 } 1771 } 1772 1773 if (localError) { 1774 ok = false; 1775 if (error && *error == NULL) { 1776 *error = localError; 1777 localError = NULL; 1778 } 1779 CFReleaseSafe(localError); 1780 } 1781 } 1782 1783 if (ok && !pk_equal && !CFEqualSafe(makeTombstone, kCFBooleanFalse)) { 1784 /* The primary key of new_item is different than that of old_item, we 1785 have been asked to make a tombstone so leave one for the old_item. */ 1786 SecDbItemRef tombstone = SecDbItemCopyTombstone(old_item, makeTombstone, false, error); 1787 ok = tombstone; 1788 if (tombstone) { 1789 ok = (SecDbItemClearRowId(tombstone, error) && 1790 SecDbItemDoInsert(tombstone, dbconn, error)); 1791 CFRelease(tombstone); 1792 } 1793 } 1794 1795 return ok; 1796 } 1797 1798 // Replace the object with a tombstone 1799 bool SecDbItemDelete(SecDbItemRef item, SecDbConnectionRef dbconn, CFBooleanRef makeTombstone, bool tombstone_time_from_item, CFErrorRef *error) { 1800 bool ok = false; 1801 if (!CFEqualSafe(makeTombstone, kCFBooleanFalse)) { 1802 SecDbItemRef tombstone = SecDbItemCopyTombstone(item, makeTombstone, tombstone_time_from_item, error); 1803 if (tombstone) { 1804 ok = SecDbItemDoUpdate(item, tombstone, dbconn, error, ^bool(const SecDbAttr *attr) { 1805 return attr->kind == kSecDbRowIdAttr; 1806 }); 1807 CFRelease(tombstone); 1808 } 1809 } else { 1810 ok = SecDbItemDoDelete(item, dbconn, error, ^bool(const SecDbAttr *attr) { 1811 return attr->kind == kSecDbRowIdAttr; 1812 }); 1813 } 1814 return ok; 1815 } 1816 1817 CFStringRef SecDbItemCopySelectSQL(SecDbQueryRef query, 1818 bool (^return_attr)(const SecDbAttr *attr), 1819 bool (^use_attr_in_where)(const SecDbAttr *attr), 1820 bool (^add_where_sql)(CFMutableStringRef sql, bool *needWhere)) { 1821 CFMutableStringRef sql = CFStringCreateMutable(kCFAllocatorDefault, 0); 1822 CFStringAppend(sql, CFSTR("SELECT ")); 1823 // What are we selecting? 1824 bool needComma = false; 1825 SecDbForEachAttr(query->q_class, attr) { 1826 if (return_attr(attr)) 1827 SecDbAppendElement(sql, attr->name, &needComma); 1828 } 1829 1830 // From which table? 1831 CFStringAppend(sql, CFSTR(" FROM ")); 1832 CFStringAppend(sql, query->q_class->name); 1833 1834 // And which elements do we want to select 1835 bool needWhere = true; 1836 SecDbForEachAttr(query->q_class, attr) { 1837 if (use_attr_in_where(attr)) { 1838 CFTypeRef value = CFDictionaryGetValue(query->q_item, attr->name); 1839 if (isArray(value)) { 1840 CFArrayRef array = (CFArrayRef)value; 1841 CFIndex length = CFArrayGetCount(array); 1842 if (length > 0) { 1843 CFTypeRef head = CFArrayGetValueAtIndex(array, 0); 1844 if (CFEqualSafe(head, kCFNull)) { 1845 SecDbAppendWhereOrAndNotIn(sql, attr->name, &needWhere, length - 1); 1846 } else { 1847 SecDbAppendWhereOrAndIn(sql, attr->name, &needWhere, length); 1848 } 1849 } 1850 } else { 1851 SecDbAppendWhereOrAndEquals(sql, attr->name, &needWhere); 1852 } 1853 } 1854 } 1855 // Append SQL for access groups and limits. 1856 if (add_where_sql) 1857 add_where_sql(sql, &needWhere); 1858 1859 return sql; 1860 } 1861 1862 static bool SecDbItemSelectBindValue(SecDbQueryRef query, sqlite3_stmt *stmt, int param, const SecDbAttr *attr, CFTypeRef inValue, CFErrorRef *error) { 1863 bool ok = true; 1864 CFTypeRef value = NULL; 1865 if (attr->kind == kSecDbRowIdAttr) { 1866 // TODO: Ignores inValue and uses rowid directly instead HACK should go 1867 value = CFNumberCreate(NULL, kCFNumberSInt64Type, &query->q_row_id); 1868 } else { 1869 value = SecDbAttrCopyValueForDb(attr, inValue, error); 1870 } 1871 ok = ok && value != NULL && SecDbBindObject(stmt, ++param, value, error); 1872 CFReleaseSafe(value); 1873 return ok; 1874 } 1875 1876 bool SecDbItemSelectBind(SecDbQueryRef query, sqlite3_stmt *stmt, CFErrorRef *error, 1877 bool (^use_attr_in_where)(const SecDbAttr *attr), 1878 bool (^bind_added_where)(sqlite3_stmt *stmt, int col)) { 1879 __block bool ok = true; 1880 __block int param = 0; 1881 SecDbForEachAttr(query->q_class, attr) { 1882 if (use_attr_in_where(attr)) { 1883 CFTypeRef value = CFDictionaryGetValue(query->q_item, attr->name); 1884 if (isArray(value)) { 1885 CFArrayRef array = (CFArrayRef)value; 1886 CFRange range = {.location = 0, .length = CFArrayGetCount(array) }; 1887 if (range.length > 0) { 1888 CFTypeRef head = CFArrayGetValueAtIndex(array, 0); 1889 if (CFEqualSafe(head, kCFNull)) { 1890 range.length--; 1891 range.location++; 1892 } 1893 } 1894 CFArrayApplyFunction(array, range, apply_block_1, (void (^)(const void *value)) ^(const void *arrayValue) { 1895 ok = SecDbItemSelectBindValue(query, stmt, param++, attr, arrayValue, error); 1896 }); 1897 } else { 1898 ok = SecDbItemSelectBindValue(query, stmt, param++, attr, value, error); 1899 } 1900 1901 if (!ok) 1902 break; 1903 } 1904 } 1905 // TODO: Bind arguments for access groups and limits. 1906 if (bind_added_where) 1907 bind_added_where(stmt, ++param); 1908 1909 return ok; 1910 } 1911 1912 bool SecDbItemSelect(SecDbQueryRef query, SecDbConnectionRef dbconn, CFErrorRef *error, 1913 bool (^return_attr)(const SecDbAttr *attr), 1914 bool (^use_attr_in_where)(const SecDbAttr *attr), 1915 bool (^add_where_sql)(CFMutableStringRef sql, bool *needWhere), 1916 bool (^bind_added_where)(sqlite3_stmt *stmt, int col), 1917 void (^handle_row)(SecDbItemRef item, bool *stop)) { 1918 __block bool ok = true; 1919 if (return_attr == NULL) { 1920 return_attr = ^bool (const SecDbAttr * attr) { 1921 return attr->kind == kSecDbRowIdAttr || attr->kind == kSecDbEncryptedDataAttr || attr->kind == kSecDbSHA1Attr; 1922 }; 1923 } 1924 if (use_attr_in_where == NULL) { 1925 use_attr_in_where = ^bool (const SecDbAttr* attr) { return false; }; 1926 } 1927 1928 CFStringRef sql = SecDbItemCopySelectSQL(query, return_attr, use_attr_in_where, add_where_sql); 1929 if (sql) { 1930 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) { 1931 ok = (SecDbItemSelectBind(query, stmt, error, use_attr_in_where, bind_added_where) && 1932 SecDbStep(dbconn, stmt, error, ^(bool *stop) { 1933 SecDbItemRef item = SecDbItemCreateWithStatement(kCFAllocatorDefault, query->q_class, stmt, query->q_keybag, error, return_attr); 1934 if (item) { 1935 CFRetainAssign(item->credHandle, query->q_use_cred_handle); 1936 handle_row(item, stop); 1937 CFRelease(item); 1938 } else { 1939 //*stop = true; 1940 //ok = false; 1941 } 1942 })); 1943 }); 1944 CFRelease(sql); 1945 } else { 1946 ok = false; 1947 } 1948 return ok; 1949 } 1950