1/* 2 * Copyright (c) 2012 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 * Created by Michael Brouwer on 11/15/12. 28 */ 29 30#include <securityd/SecDbItem.h> 31#include <utilities/SecCFWrappers.h> 32#include <utilities/der_date.h> 33#include <utilities/debugging.h> 34 35#include <Security/SecBasePriv.h> 36#include <Security/SecInternal.h> 37#include <corecrypto/ccsha1.h> 38#include <Security/SecItem.h> 39#include <Security/SecItemPriv.h> 40 41// MARK: type converters 42 43CFStringRef copyString(CFTypeRef obj) { 44 CFTypeID tid = CFGetTypeID(obj); 45 if (tid == CFStringGetTypeID()) 46 return CFStringCreateCopy(0, obj); 47 else if (tid == CFDataGetTypeID()) 48 return CFStringCreateFromExternalRepresentation(0, obj, kCFStringEncodingUTF8); 49 else 50 return NULL; 51} 52 53CFDataRef copyData(CFTypeRef obj) { 54 CFTypeID tid = CFGetTypeID(obj); 55 if (tid == CFDataGetTypeID()) { 56 return CFDataCreateCopy(0, obj); 57 } else if (tid == CFStringGetTypeID()) { 58 return CFStringCreateExternalRepresentation(0, obj, kCFStringEncodingUTF8, 0); 59 } else if (tid == CFNumberGetTypeID()) { 60 SInt32 value; 61 CFNumberGetValue(obj, kCFNumberSInt32Type, &value); 62 return CFDataCreate(0, (const UInt8 *)&value, sizeof(value)); 63 } else { 64 return NULL; 65 } 66} 67 68CFTypeRef copyBlob(CFTypeRef obj) { 69 CFTypeID tid = CFGetTypeID(obj); 70 if (tid == CFDataGetTypeID()) { 71 return CFDataCreateCopy(0, obj); 72 } else if (tid == CFStringGetTypeID()) { 73 return CFStringCreateCopy(0, obj); 74 } else if (tid == CFNumberGetTypeID()) { 75 CFRetain(obj); 76 return obj; 77 } else { 78 return NULL; 79 } 80} 81 82CFDataRef copySHA1(CFTypeRef obj) { 83 CFTypeID tid = CFGetTypeID(obj); 84 if (tid == CFDataGetTypeID() && CFDataGetLength(obj) == CCSHA1_OUTPUT_SIZE) { 85 return CFDataCreateCopy(CFGetAllocator(obj), obj); 86 } else { 87 return NULL; 88 } 89} 90 91CFTypeRef copyNumber(CFTypeRef obj) { 92 CFTypeID tid = CFGetTypeID(obj); 93 if (tid == CFNumberGetTypeID()) { 94 CFRetain(obj); 95 return obj; 96 } else if (tid == CFBooleanGetTypeID()) { 97 SInt32 value = CFBooleanGetValue(obj); 98 return CFNumberCreate(0, kCFNumberSInt32Type, &value); 99 } else if (tid == CFStringGetTypeID()) { 100 SInt32 value = CFStringGetIntValue(obj); 101 CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value); 102 /* If a string converted to an int isn't equal to the int printed as 103 a string, return a CFStringRef instead. */ 104 if (!CFEqual(t, obj)) { 105 CFRelease(t); 106 return CFStringCreateCopy(0, obj); 107 } 108 CFRelease(t); 109 return CFNumberCreate(0, kCFNumberSInt32Type, &value); 110 } else 111 return NULL; 112} 113 114CFDateRef copyDate(CFTypeRef obj) { 115 CFTypeID tid = CFGetTypeID(obj); 116 if (tid == CFDateGetTypeID()) { 117 CFRetain(obj); 118 return obj; 119 } else 120 return NULL; 121} 122 123// MARK: SecDbColumn accessors, to retrieve values as CF types in SecDbStep. 124 125static CFDataRef SecDbColumnCopyData(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) { 126 return CFDataCreate(allocator, sqlite3_column_blob(stmt, col), 127 sqlite3_column_bytes(stmt, col)); 128 //return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt, col), 129 // sqlite3_column_bytes(stmt, col), 130 // kCFAllocatorNull); 131} 132 133static CFDateRef SecDbColumnCopyDate(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) { 134 return CFDateCreate(allocator, sqlite3_column_double(stmt, col)); 135} 136 137static CFNumberRef SecDbColumnCopyDouble(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) { 138 double number = sqlite3_column_double(stmt, col); 139 return CFNumberCreate(allocator, kCFNumberDoubleType, &number); 140} 141 142static CFNumberRef SecDbColumnCopyNumber64(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) { 143 sqlite_int64 number = sqlite3_column_int64(stmt, col); 144 return CFNumberCreate(allocator, kCFNumberSInt64Type, &number); 145} 146 147static CFNumberRef SecDbColumnCopyNumber(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) { 148 sqlite_int64 number = sqlite3_column_int64(stmt, col); 149 if (INT32_MIN <= number && number <= INT32_MAX) { 150 int32_t num32 = (int32_t)number; 151 return CFNumberCreate(allocator, kCFNumberSInt32Type, &num32); 152 } else { 153 return CFNumberCreate(allocator, kCFNumberSInt64Type, &number); 154 } 155} 156 157static CFStringRef SecDbColumnCopyString(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) { 158 const unsigned char *text = sqlite3_column_text(stmt, col); 159 return CFStringCreateWithBytes(allocator, text, strlen((const char *)text), kCFStringEncodingUTF8, false); 160} 161 162// MARK: SecDbClass helpers 163 164const SecDbAttr *SecDbClassAttrWithKind(const SecDbClass *class, SecDbAttrKind kind, CFErrorRef *error) { 165 const SecDbAttr *result = NULL; 166 SecDbForEachAttr(class, desc) { 167 if (desc->kind == kind) 168 result = desc; 169 } 170 171 if (!result) 172 SecError(errSecInternal, error, CFSTR("Can't find attribute of kind %d in class %@"), kind, class->name); 173 174 return result; 175} 176 177// MARK: SecDbAttr helpers 178 179static bool SecDbIsTombstoneDbSelectAttr(const SecDbAttr *attr) { 180 return attr->flags & kSecDbPrimaryKeyFlag || attr->kind == kSecDbTombAttr; 181} 182 183#if 0 184static bool SecDbIsTombstoneDbInsertAttr(const SecDbAttr *attr) { 185 return SecDbIsTombstoneDbSelectAttr(attr) || attr->kind == kSecDbAccessAttr || attr->kind == kSecDbCreationDateAttr || attr->kind == kSecDbModificationDateAttr; 186} 187#endif 188 189static bool SecDbIsTombstoneDbUpdateAttr(const SecDbAttr *attr) { 190 return SecDbIsTombstoneDbSelectAttr(attr) || attr->kind == kSecDbAccessAttr || attr->kind == kSecDbCreationDateAttr || attr->kind == kSecDbRowIdAttr; 191} 192 193static bool SecDbAttrBind(const SecDbAttr *attr, sqlite3_stmt *stmt, int col, CFTypeRef value, CFErrorRef *error) { 194 bool ok = true; 195 if (value && !CFEqual(kCFNull, value) && attr->flags & kSecDbSHA1ValueInFlag) { 196 CFDataRef data = copyData(value); 197 if (!data) { 198 SecError(errSecInternal, error, CFSTR("failed to get attribute %@ data"), attr->name); 199 return false; 200 } 201 202 CFMutableDataRef digest = CFDataCreateMutable(kCFAllocatorDefault, CCSHA1_OUTPUT_SIZE); 203 CFDataSetLength(digest, CCSHA1_OUTPUT_SIZE); 204 /* 64 bits cast: worst case is we generate the wrong hash */ 205 assert((unsigned long)CFDataGetLength(data)<UINT32_MAX); /* Debug check. Correct as long as CFIndex is long */ 206 ccdigest(ccsha1_di(), CFDataGetLength(data), CFDataGetBytePtr(data), CFDataGetMutableBytePtr(digest)); 207 CFRelease(data); 208 ok &= SecDbBindObject(stmt, col, digest, error); 209 CFRelease(digest); 210 } else { 211 ok &= SecDbBindObject(stmt, col, value, error); 212 } 213 return ok; 214} 215 216// MARK: SecDbItem 217 218CFTypeRef SecDbItemGetCachedValueWithName(SecDbItemRef item, CFStringRef name) { 219 return CFDictionaryGetValue(item->attributes, name); 220} 221 222static CFTypeRef SecDbItemGetCachedValue(SecDbItemRef item, const SecDbAttr *desc) { 223 return CFDictionaryGetValue(item->attributes, desc->name); 224} 225 226CFMutableDictionaryRef SecDbItemCopyPListWithMask(SecDbItemRef item, CFOptionFlags mask, CFErrorRef *error) { 227 CFMutableDictionaryRef dict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 228 SecDbForEachAttrWithMask(item->class, desc, mask) { 229 CFTypeRef value = SecDbItemGetValue(item, desc, error); 230 if (value) { 231 if (!CFEqual(kCFNull, value)) { 232 CFDictionarySetValue(dict, desc->name, value); 233 } else if (desc->flags & kSecDbNotNullFlag) { 234 SecError(errSecInternal, error, CFSTR("attribute %@ has NULL value"), desc->name); 235 secerror("%@", error ? *error : (CFErrorRef)CFSTR("error == NULL")); 236 CFReleaseNull(dict); 237 break; 238 } 239 } else { 240 CFReleaseNull(dict); 241 break; 242 } 243 } 244 return dict; 245} 246 247static CFDataRef SecDbItemCopyDERWithMask(SecDbItemRef item, CFOptionFlags mask, CFErrorRef *error) { 248 CFDataRef der = NULL; 249 CFMutableDictionaryRef dict = SecDbItemCopyPListWithMask(item, mask, error); 250 if (dict) { 251 der = kc_plist_copy_der(dict, error); 252 CFRelease(dict); 253 } 254 return der; 255} 256 257static CFDataRef SecDbItemCopyDigestWithMask(SecDbItemRef item, CFOptionFlags mask, CFErrorRef *error) { 258 CFDataRef digest = NULL; 259 CFDataRef der = SecDbItemCopyDERWithMask(item, mask, error); 260 if (der) { 261 digest = kc_copy_sha1(CFDataGetLength(der), CFDataGetBytePtr(der), error); 262 CFRelease(der); 263 } 264 return digest; 265} 266 267static CFDataRef SecDbItemCopyPrimaryKey(SecDbItemRef item, CFErrorRef *error) { 268 return SecDbItemCopyDigestWithMask(item, kSecDbPrimaryKeyFlag, error); 269} 270 271static CFDataRef SecDbItemCopySHA1(SecDbItemRef item, CFErrorRef *error) { 272 return SecDbItemCopyDigestWithMask(item, kSecDbInHashFlag, error); 273} 274 275static CFDataRef SecDbItemCopyUnencryptedData(SecDbItemRef item, CFErrorRef *error) { 276 return SecDbItemCopyDERWithMask(item, kSecDbInCryptoDataFlag, error); 277} 278 279static keyclass_t SecDbItemParseKeyclass(CFTypeRef value, CFErrorRef *error) { 280 if (!isString(value)) { 281 SecError(errSecParam, error, CFSTR("accessible attribute %@ not a string"), value); 282 } else if (CFEqual(value, kSecAttrAccessibleWhenUnlocked)) { 283 return key_class_ak; 284 } else if (CFEqual(value, kSecAttrAccessibleAfterFirstUnlock)) { 285 return key_class_ck; 286 } else if (CFEqual(value, kSecAttrAccessibleAlways)) { 287 return key_class_dk; 288 } else if (CFEqual(value, kSecAttrAccessibleWhenUnlockedThisDeviceOnly)) { 289 return key_class_aku; 290 } else if (CFEqual(value, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)) { 291 return key_class_cku; 292 } else if (CFEqual(value, kSecAttrAccessibleAlwaysThisDeviceOnly)) { 293 return key_class_dku; 294 } else { 295 SecError(errSecParam, error, CFSTR("accessible attribute %@ unknown"), value); 296 } 297 return 0; 298} 299 300keyclass_t SecDbItemGetKeyclass(SecDbItemRef item, CFErrorRef *error) { 301 if (!item->keyclass) { 302 const SecDbAttr *desc = SecDbClassAttrWithKind(item->class, kSecDbAccessAttr, error); 303 if (desc) { 304 CFTypeRef value = SecDbItemGetValue(item, desc, error); 305 if (value) { 306 item->keyclass = SecDbItemParseKeyclass(value, error); 307 } 308 } 309 } 310 return item->keyclass; 311} 312 313static CFDataRef SecDbItemCopyEncryptedData(SecDbItemRef item, CFErrorRef *error) { 314 CFDataRef edata = NULL; 315 CFDataRef plain = SecDbItemCopyUnencryptedData(item, error); 316 if (plain) { 317 keyclass_t keyclass = SecDbItemGetKeyclass(item, error); 318 if (keyclass) { 319 if (ks_encrypt_data(item->keybag, keyclass, plain, &edata, error)) { 320 item->_edataState = kSecDbItemEncrypting; 321 } else { 322 seccritical("ks_encrypt_data (db): failed: %@", error ? *error : (CFErrorRef)CFSTR("")); 323 } 324 } 325 CFRelease(plain); 326 } 327 328 return edata; 329} 330 331CFDataRef SecDbItemCopyEncryptedDataToBackup(SecDbItemRef item, uint64_t handle, CFErrorRef *error) { 332 CFDataRef edata = NULL; 333 keybag_handle_t keybag = (keybag_handle_t)handle; 334 CFDataRef plain = SecDbItemCopyUnencryptedData(item, error); 335 if (plain) { 336 keyclass_t keyclass = SecDbItemGetKeyclass(item, error); 337 if (keyclass) { 338 if (!ks_encrypt_data(keybag, keyclass, plain, &edata, error)) 339 seccritical("ks_encrypt_data (db): failed: %@", error ? *error : (CFErrorRef)CFSTR("")); 340 } 341 CFRelease(plain); 342 } 343 return edata; 344} 345 346static bool SecDbItemEnsureDecrypted(SecDbItemRef item, CFErrorRef *error) { 347 348 // If we haven't yet decrypted the item, make sure we do so now 349 bool result = true; 350 if (item->_edataState == kSecDbItemEncrypted) { 351 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, error); 352 if (attr) { 353 CFDataRef edata = SecDbItemGetCachedValue(item, attr); 354 if (!edata) 355 return SecError(errSecInternal, error, CFSTR("state= encrypted but edata is NULL")); 356 // Decrypt calls set value a bunch of times which clears our edata and changes our state. 357 item->_edataState = kSecDbItemDecrypting; 358 result = SecDbItemDecrypt(item, edata, error); 359 if (result) 360 item->_edataState = kSecDbItemClean; 361 else 362 item->_edataState = kSecDbItemEncrypted; 363 } 364 } 365 return result; 366} 367 368// Only called if cached value is not found. 369static CFTypeRef SecDbItemCopyValue(SecDbItemRef item, const SecDbAttr *attr, CFErrorRef *error) { 370 CFTypeRef value = NULL; 371 switch (attr->kind) { 372 case kSecDbSHA1Attr: 373 value = SecDbItemCopySHA1(item, error); 374 break; 375 case kSecDbEncryptedDataAttr: 376 value = SecDbItemCopyEncryptedData(item, error); 377 break; 378 case kSecDbPrimaryKeyAttr: 379 value = SecDbItemCopyPrimaryKey(item, error); 380 break; 381 case kSecDbAccessAttr: 382 case kSecDbStringAttr: 383 case kSecDbBlobAttr: 384 if (attr->flags & kSecDbNotNullFlag) { 385 if (attr->flags & kSecDbDefault0Flag) { 386 value = CFSTR("0"); 387 break; 388 } else if (attr->kind != kSecDbBlobAttr && attr->flags & kSecDbDefaultEmptyFlag) { 389 // blob drops through to data everything else is empty string 390 value = CFSTR(""); 391 break; 392 } 393 } 394 //DROPTHROUGH 395 case kSecDbDataAttr: 396 if (attr->flags & kSecDbNotNullFlag && attr->flags & kSecDbDefaultEmptyFlag) { 397 value = CFDataCreate(CFGetAllocator(item), NULL, 0); 398 } else { 399 value = kCFNull; 400 } 401 break; 402 case kSecDbNumberAttr: 403 case kSecDbSyncAttr: 404 case kSecDbTombAttr: 405 if (attr->flags & kSecDbNotNullFlag) { 406 int32_t zero = 0; 407 value = CFNumberCreate(CFGetAllocator(item), kCFNumberSInt32Type, &zero); 408 } else { 409 value = kCFNull; 410 } 411 break; 412 case kSecDbDateAttr: 413 if (attr->flags & kSecDbNotNullFlag && attr->flags & kSecDbDefault0Flag) { 414 value = CFDateCreate(kCFAllocatorDefault, 0.0); 415 } else { 416 value = kCFNull; 417 } 418 break; 419 case kSecDbRowIdAttr: 420 if (attr->flags & kSecDbNotNullFlag) { 421 // No can do, error? 422 } 423 value = kCFNull; 424 break; 425 case kSecDbCreationDateAttr: 426 case kSecDbModificationDateAttr: 427 value = CFDateCreate(CFGetAllocator(item), CFAbsoluteTimeGetCurrent()); 428 break; 429 } 430 431 return value; 432} 433 434// SecDbItemGetValue will return kCFNull if there is no value for an attribute and this was not 435// an error. It will return NULL and optionally set *error if there was an error computing an 436// attribute, or if a required attribute was missing a value and had no known way to compute 437// it's value. 438CF_RETURNS_NOT_RETAINED CFTypeRef SecDbItemGetValue(SecDbItemRef item, const SecDbAttr *desc, CFErrorRef *error) { 439 // Propagate chained errors 440 if (!desc) 441 return NULL; 442 443 if (desc->flags & kSecDbInCryptoDataFlag) { 444 if (!SecDbItemEnsureDecrypted(item, error)) 445 return NULL; 446 } 447 448 CFTypeRef value = SecDbItemGetCachedValue(item, desc); 449 if (!value) { 450 value = SecDbItemCopyValue(item, desc, error); 451 if (value) { 452 if (!CFEqual(kCFNull, value)) { 453 SecDbItemSetValue(item, desc, value, error); 454 CFRelease(value); 455 value = SecDbItemGetCachedValue(item, desc); 456 } 457 } 458 } 459 return value; 460} 461 462static bool SecDbItemGetBoolValue(SecDbItemRef item, const SecDbAttr *desc, bool *bvalue, CFErrorRef *error) { 463 CFTypeRef value = SecDbItemGetValue(item, desc, error); 464 if (!value) 465 return false; 466 char cvalue; 467 *bvalue = (isNumber(value) && CFNumberGetValue(value, kCFNumberCharType, &cvalue) && cvalue == 1); 468 return true; 469} 470 471static CFStringRef SecDbItemCopyDescription(CFTypeRef cf) { 472#if 0 //defined(DEBUG) && DEBUG != 0 473 SecDbItemRef item = (SecDbItemRef)cf; 474 CFMutableStringRef desc = CFStringCreateMutable(CFGetAllocator(cf), 0); 475 CFStringAppendFormat(desc, NULL, CFSTR("<%@"), item->class->name); 476 SecDbForEachAttr(item->class, attr) { 477 CFTypeRef value = SecDbItemGetValue(item, attr, NULL); 478 if (value) { 479 CFStringAppend(desc, CFSTR(",")); 480 CFStringAppend(desc, attr->name); 481 CFStringAppend(desc, CFSTR("=")); 482 if (CFEqual(CFSTR("data"), attr->name)) { 483 CFStringAppendEncryptedData(desc, value); 484 } else if (CFEqual(CFSTR("v_Data"), attr->name)) { 485 CFStringAppend(desc, CFSTR("<?>")); 486 } else if (isData(value)) { 487 CFStringAppendHexData(desc, value); 488 } else { 489 CFStringAppendFormat(desc, 0, CFSTR("%@"), value); 490 } 491 } 492 } 493 CFStringAppend(desc, CFSTR(">")); 494#else 495 SecDbItemRef item = (SecDbItemRef)cf; 496 const UInt8 zero4[4] = {}; 497 const UInt8 *pk = &zero4[0], *sha1 = &zero4[0]; 498 char sync = 0; 499 char tomb = 0; 500 SInt64 rowid = 0; 501 CFStringRef access = NULL; 502 uint8_t mdatbuf[32] = {}; 503 uint8_t *mdat = &mdatbuf[0]; 504 CFMutableStringRef attrs = CFStringCreateMutable(kCFAllocatorDefault, 0); 505 CFStringRef agrp = NULL; 506 507 SecDbForEachAttr(item->class, attr) { 508 CFTypeRef value; 509 switch (attr->kind) { 510 case kSecDbBlobAttr: 511 case kSecDbDataAttr: 512 case kSecDbStringAttr: 513 case kSecDbNumberAttr: 514 case kSecDbDateAttr: 515 case kSecDbEncryptedDataAttr: 516 if (attr->flags & (kSecDbReturnAttrFlag | kSecDbReturnDataFlag) && (value = SecDbItemGetValue(item, attr, NULL)) && !CFEqual(value, kCFNull)) { 517 if (isString(value) && CFEqual(attr->name, kSecAttrAccessGroup)) { 518 agrp = value; 519 } else { 520 // We don't log these, just record that we saw the attribute. 521 CFStringAppend(attrs, CFSTR(",")); 522 CFStringAppend(attrs, attr->name); 523 } 524 } 525 break; 526 case kSecDbCreationDateAttr: 527 // We don't care about this and every object has one. 528 break; 529 case kSecDbModificationDateAttr: 530 value = SecDbItemGetValue(item, attr, NULL); 531 if (isDate(value)) 532 mdat = der_encode_generalizedtime_body(CFDateGetAbsoluteTime(value), NULL, mdat, &mdatbuf[31]); 533 break; 534 case kSecDbSHA1Attr: 535 value = SecDbItemGetValue(item, attr, NULL); 536 if (isData(value)) 537 sha1 = CFDataGetBytePtr(value); 538 break; 539 case kSecDbRowIdAttr: 540 value = SecDbItemGetValue(item, attr, NULL); 541 if (isNumber(value)) 542 CFNumberGetValue(value, kCFNumberSInt64Type, &rowid); 543 break; 544 case kSecDbPrimaryKeyAttr: 545 value = SecDbItemGetValue(item, attr, NULL); 546 if (isData(value)) 547 pk = CFDataGetBytePtr(value); 548 break; 549 case kSecDbSyncAttr: 550 value = SecDbItemGetValue(item, attr, NULL); 551 if (isNumber(value)) 552 CFNumberGetValue(value, kCFNumberCharType, &sync); 553 break; 554 case kSecDbTombAttr: 555 value = SecDbItemGetValue(item, attr, NULL); 556 if (isNumber(value)) 557 CFNumberGetValue(value, kCFNumberCharType, &tomb); 558 break; 559 case kSecDbAccessAttr: 560 value = SecDbItemGetValue(item, attr, NULL); 561 if (isString(value)) 562 access = value; 563 break; 564 } 565 } 566 567 CFStringRef desc = CFStringCreateWithFormat(CFGetAllocator(cf), NULL, 568 CFSTR( 569 "%s," 570 "%@," 571 "%02X%02X%02X%02X," 572 "%s," 573 "%@," 574 "%@," 575 "%"PRId64 576 "%@," 577 "%s," 578 "%02X%02X%02X%02X"), 579 tomb ? "T" : "O", 580 item->class->name, 581 pk[0], pk[1], pk[2], pk[3], 582 sync ? "S" : "L", 583 access, 584 agrp, 585 rowid, 586 attrs, 587 mdat, 588 sha1[0], sha1[1], sha1[2], sha1[3]); 589 CFReleaseSafe(attrs); 590#endif 591 592 return desc; 593} 594 595static void SecDbItemDestroy(CFTypeRef cf) { 596 SecDbItemRef item = (SecDbItemRef)cf; 597 CFReleaseSafe(item->attributes); 598} 599 600static CFHashCode SecDbItemHash(CFTypeRef cf) { 601 SecDbItemRef item = (SecDbItemRef)cf; 602 CFDataRef digest = SecDbItemGetSHA1(item, NULL); 603 CFHashCode code; 604 const UInt8 *p = CFDataGetBytePtr(digest); 605 // Read first 8 bytes of digest in order 606 code = p[0] + ((p[1] + ((p[2] + ((p[3] + ((p[4] + ((p[5] + ((p[6] + (p[7] << 8)) << 8)) << 8)) << 8)) << 8)) << 8)) << 8); 607 return code; 608} 609 610static Boolean SecDbItemCompare(CFTypeRef cf1, CFTypeRef cf2) { 611 SecDbItemRef item1 = (SecDbItemRef)cf1; 612 SecDbItemRef item2 = (SecDbItemRef)cf2; 613 CFDataRef digest1 = NULL; 614 CFDataRef digest2 = NULL; 615 if (item1) 616 digest1 = SecDbItemGetSHA1(item1, NULL); 617 if (item2) 618 digest2 = SecDbItemGetSHA1(item2, NULL); 619 Boolean equal = CFEqual(digest1, digest2); 620 return equal; 621} 622 623CFGiblisWithHashFor(SecDbItem) 624 625static SecDbItemRef SecDbItemCreate(CFAllocatorRef allocator, const SecDbClass *class, keybag_handle_t keybag) { 626 SecDbItemRef item = CFTypeAllocate(SecDbItem, struct SecDbItem, allocator); 627 item->class = class; 628 item->attributes = CFDictionaryCreateMutableForCFTypes(allocator); 629 item->keybag = keybag; 630 item->_edataState = kSecDbItemDirty; 631 return item; 632} 633 634const SecDbClass *SecDbItemGetClass(SecDbItemRef item) { 635 return item->class; 636} 637 638const keybag_handle_t SecDbItemGetKeybag(SecDbItemRef item) { 639 return item->keybag; 640} 641 642bool SecDbItemSetKeybag(SecDbItemRef item, keybag_handle_t keybag, CFErrorRef *error) { 643 if (!SecDbItemEnsureDecrypted(item, error)) 644 return false; 645 if (item->keybag != keybag) { 646 item->keybag = keybag; 647 if (item->_edataState == kSecDbItemClean) { 648 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, NULL), kCFNull, NULL); 649 } 650 } 651 652 return true; 653} 654 655bool SecDbItemSetValue(SecDbItemRef item, const SecDbAttr *desc, CFTypeRef value, CFErrorRef *error) { 656 // Propagate chained errors. 657 if (!desc) 658 return false; 659 660 bool changed = false; 661 CFTypeRef attr = NULL; 662 if (desc->flags & kSecDbInCryptoDataFlag) 663 if (!SecDbItemEnsureDecrypted(item, error)) 664 return false; 665 666 switch (desc->kind) { 667 case kSecDbPrimaryKeyAttr: 668 case kSecDbDataAttr: 669 attr = copyData(value); 670 break; 671 case kSecDbEncryptedDataAttr: 672 attr = copyData(value); 673 if (attr) { 674 if (item->_edataState == kSecDbItemEncrypting) 675 item->_edataState = kSecDbItemClean; 676 else 677 item->_edataState = kSecDbItemEncrypted; 678 } else if (!value || CFEqual(kCFNull, value)) { 679 item->_edataState = kSecDbItemDirty; 680 } 681 break; 682 case kSecDbBlobAttr: 683 attr = copyBlob(value); 684 break; 685 case kSecDbDateAttr: 686 case kSecDbCreationDateAttr: 687 case kSecDbModificationDateAttr: 688 attr = copyDate(value); 689 break; 690 case kSecDbNumberAttr: 691 case kSecDbSyncAttr: 692 case kSecDbTombAttr: 693 case kSecDbRowIdAttr: 694 attr = copyNumber(value); 695 break; 696 case kSecDbAccessAttr: 697 case kSecDbStringAttr: 698 attr = copyString(value); 699 break; 700 case kSecDbSHA1Attr: 701 attr = copySHA1(value); 702 break; 703 } 704 705 if (attr) { 706 CFTypeRef ovalue = CFDictionaryGetValue(item->attributes, desc->name); 707 changed = (!ovalue || !CFEqual(ovalue, attr)); 708 CFDictionarySetValue(item->attributes, desc->name, attr); 709 CFRelease(attr); 710 } else { 711 if (value && !CFEqual(kCFNull, value)) { 712 SecError(errSecItemInvalidValue, error, CFSTR("attribute %@: value: %@ failed to convert"), desc->name, value); 713 return false; 714 } 715 CFTypeRef ovalue = CFDictionaryGetValue(item->attributes, desc->name); 716 changed = (ovalue && !CFEqual(ovalue, kCFNull)); 717 CFDictionaryRemoveValue(item->attributes, desc->name); 718 } 719 720 if (changed) { 721 if (desc->flags & kSecDbInHashFlag) 722 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, NULL), kCFNull, NULL); 723 if (desc->flags & kSecDbPrimaryKeyFlag) 724 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbPrimaryKeyAttr, NULL), kCFNull, NULL); 725 if (desc->flags & kSecDbInCryptoDataFlag && item->_edataState == kSecDbItemClean) 726 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, NULL), kCFNull, NULL); 727 } 728 729 return true; 730} 731 732bool SecDbItemSetValues(SecDbItemRef item, CFDictionaryRef values, CFErrorRef *error) { 733 SecDbForEachAttr(item->class, attr) { 734 CFTypeRef value = CFDictionaryGetValue(values, attr->name); 735 if (value && !SecDbItemSetValue(item, attr, value, error)) 736 return false; 737 } 738 return true; 739} 740 741bool SecDbItemSetValueWithName(SecDbItemRef item, CFStringRef name, CFTypeRef value, CFErrorRef *error) { 742 SecDbForEachAttr(item->class, attr) { 743 if (CFEqual(attr->name, name)) { 744 return SecDbItemSetValue(item, attr, value, error); 745 } 746 } 747 return false; 748} 749 750static bool SecDbItemSetNumber(SecDbItemRef item, const SecDbAttr *desc, int32_t number, CFErrorRef *error) { 751 bool ok = true; 752 CFNumberRef value = CFNumberCreate(CFGetAllocator(item), kCFNumberSInt32Type, &number); 753 if (value) { 754 ok = SecDbItemSetValue(item, desc, value, error); 755 CFRelease(value); 756 } else { 757 ok = SecError(errSecInternal, error, CFSTR("Failed to create CFNumber for %" PRId32), number); 758 } 759 return ok; 760} 761 762bool SecDbItemSetKeyclass(SecDbItemRef item, keyclass_t keyclass, CFErrorRef *error) { 763 bool ok = SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, error), kCFNull, error); 764 if (ok) { 765 item->_edataState = kSecDbItemDirty; 766 ok = SecDbItemSetNumber(item, SecDbClassAttrWithKind(item->class, kSecDbAccessAttr, error), keyclass, error); 767 } 768 return ok; 769} 770 771SecDbItemRef SecDbItemCreateWithAttributes(CFAllocatorRef allocator, const SecDbClass *class, CFDictionaryRef attributes, keybag_handle_t keybag, CFErrorRef *error) { 772 SecDbItemRef item = SecDbItemCreate(kCFAllocatorDefault, class, keybag); 773 if (item && !SecDbItemSetValues(item, attributes, error)) 774 CFReleaseNull(item); 775 return item; 776} 777 778static CFTypeRef 779SecDbColumnCopyValueWithAttr(CFAllocatorRef allocator, sqlite3_stmt *stmt, const SecDbAttr *attr, int col, CFErrorRef *error) { 780 CFTypeRef value = NULL; 781 switch (attr->kind) { 782 case kSecDbDateAttr: 783 case kSecDbCreationDateAttr: 784 case kSecDbModificationDateAttr: 785 value = SecDbColumnCopyDate(allocator, stmt, col, error); 786 break; 787 case kSecDbBlobAttr: 788 switch (sqlite3_column_type(stmt, col)) { 789 case SQLITE_INTEGER: 790 value = SecDbColumnCopyNumber(allocator, stmt, col, error); 791 break; 792 case SQLITE_FLOAT: 793 value = SecDbColumnCopyDouble(allocator, stmt, col, error); 794 break; 795 case SQLITE_TEXT: 796 value = SecDbColumnCopyString(allocator, stmt, col, error); 797 break; 798 case SQLITE_BLOB: 799 value = SecDbColumnCopyData(allocator, stmt, col, error); 800 break; 801 case SQLITE_NULL: 802 value = kCFNull; 803 break; 804 } 805 break; 806 case kSecDbAccessAttr: 807 case kSecDbStringAttr: 808 value = SecDbColumnCopyString(allocator, stmt, col, error); 809 break; 810 case kSecDbDataAttr: 811 case kSecDbSHA1Attr: 812 case kSecDbPrimaryKeyAttr: 813 value = SecDbColumnCopyData(allocator, stmt, col, error); 814 break; 815 case kSecDbEncryptedDataAttr: 816 value = SecDbColumnCopyData(allocator, stmt, col, error); 817 break; 818 case kSecDbSyncAttr: 819 case kSecDbTombAttr: 820 case kSecDbNumberAttr: 821 value = SecDbColumnCopyNumber(allocator, stmt, col, error); 822 break; 823 case kSecDbRowIdAttr: 824 value = SecDbColumnCopyNumber64(allocator, stmt, col, error); 825 break; 826 } 827 return value; 828} 829 830SecDbItemRef SecDbItemCreateWithStatement(CFAllocatorRef allocator, const SecDbClass *class, sqlite3_stmt *stmt, keybag_handle_t keybag, CFErrorRef *error, bool (^return_attr)(const SecDbAttr *attr)) { 831 SecDbItemRef item = SecDbItemCreate(allocator, class, keybag); 832 int col = 0; 833 SecDbForEachAttr(class, attr) { 834 if (return_attr(attr)) { 835 CFTypeRef value = SecDbColumnCopyValueWithAttr(allocator, stmt, attr, col++, error); 836 if (value) { 837 SecDbItemSetValue(item, attr, value, error); 838 CFRelease(value); 839 } 840 } 841 } 842 843 return item; 844} 845 846SecDbItemRef SecDbItemCreateWithEncryptedData(CFAllocatorRef allocator, const SecDbClass *class, 847 CFDataRef edata, keybag_handle_t keybag, CFErrorRef *error) { 848 SecDbItemRef item = SecDbItemCreate(allocator, class, keybag); 849 const SecDbAttr *edata_attr = SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr, error); 850 if (edata_attr) { 851 if (!SecDbItemSetValue(item, edata_attr, edata, error)) 852 CFReleaseNull(item); 853 } 854 return item; 855} 856 857#if 0 858SecDbItemRef SecDbItemCreateWithRowId(CFAllocatorRef allocator, const SecDbClass *class, sqlite_int64 row_id, keybag_handle_t keybag, CFErrorRef *error) { 859 SecDbItemRef item = SecDbItemCreate(allocator, class, keybag); 860 if (!SecDbItemSetRowId(item, row_id, error)) 861 CFReleaseNull(item); 862 return item; 863} 864#endif 865 866SecDbItemRef SecDbItemCopyWithUpdates(SecDbItemRef item, CFDictionaryRef updates, CFErrorRef *error) { 867 SecDbItemRef new_item = SecDbItemCreate(CFGetAllocator(item), item->class, item->keybag); 868 SecDbForEachAttr(item->class, attr) { 869 // Copy each attribute, except the mod date attribute (it will be reset to now when needed), 870 // from the updates dict unless it's not there in which case we copy the attribute from the passed in item. 871 if (attr->kind != kSecDbModificationDateAttr && attr->kind != kSecDbEncryptedDataAttr && attr->kind != kSecDbSHA1Attr && attr->kind != kSecDbPrimaryKeyAttr) { 872 CFTypeRef value = NULL; 873 if (CFDictionaryGetValueIfPresent(updates, attr->name, &value)) { 874 if (!value) 875 SecError(errSecParam, error, CFSTR("NULL value in dictionary")); 876 } else { 877 value = SecDbItemGetValue(item, attr, error); 878 } 879 if (!value || !SecDbItemSetValue(new_item, attr, value, error)) { 880 CFReleaseNull(new_item); 881 break; 882 } 883 } 884 } 885 return new_item; 886} 887 888// Ensure that the date value of attr of new_item is greater than that of old_item. 889static bool SecDbItemMakeAttrYounger(SecDbItemRef new_item, SecDbItemRef old_item, const SecDbAttr *attr, CFErrorRef *error) { 890 CFDateRef old_date = SecDbItemGetValue(old_item, attr, error); 891 if (!old_date) 892 return false; 893 CFDateRef new_date = SecDbItemGetValue(new_item, attr, error); 894 if (!new_date) 895 return false; 896 bool ok = true; 897 if (CFDateCompare(new_date, old_date, NULL) != kCFCompareGreaterThan) { 898 CFDateRef adjusted_date = CFDateCreate(kCFAllocatorDefault, CFDateGetAbsoluteTime(old_date) + 0.001); 899 if (adjusted_date) { 900 ok = SecDbItemSetValue(new_item, attr, adjusted_date, error); 901 CFRelease(adjusted_date); 902 } 903 } 904 return ok; 905} 906 907// Ensure that the mod date of new_item is greater than that of old_item. 908static bool SecDbItemMakeYounger(SecDbItemRef new_item, SecDbItemRef old_item, CFErrorRef *error) { 909 const SecDbAttr *attr = SecDbClassAttrWithKind(new_item->class, kSecDbModificationDateAttr, error); 910 return attr && SecDbItemMakeAttrYounger(new_item, old_item, attr, error); 911} 912 913SecDbItemRef SecDbItemCopyTombstone(SecDbItemRef item, CFErrorRef *error) { 914 SecDbItemRef new_item = SecDbItemCreate(CFGetAllocator(item), item->class, item->keybag); 915 SecDbForEachAttr(item->class, attr) { 916 if (attr->kind == kSecDbTombAttr) { 917 // Set the tomb attr to true to indicate a tombstone. 918 if (!SecDbItemSetValue(new_item, attr, kCFBooleanTrue, error)) { 919 CFReleaseNull(new_item); 920 break; 921 } 922 } else if (SecDbIsTombstoneDbUpdateAttr(attr)) { 923 // Copy all primary key attributes and creation timestamps from the original item. 924 CFTypeRef value = SecDbItemGetValue(item, attr, error); 925 if (!value || (!CFEqual(kCFNull, value) && !SecDbItemSetValue(new_item, attr, value, error))) { 926 CFReleaseNull(new_item); 927 break; 928 } 929 } else if (attr->kind == kSecDbModificationDateAttr) { 930 if (!SecDbItemMakeAttrYounger(new_item, item, attr, error)) { 931 CFReleaseNull(new_item); 932 break; 933 } 934 } 935 } 936 937 return new_item; 938} 939 940// MARK: - 941// MARK: SQL Construction helpers -- These should become private in the future 942 943void SecDbAppendElement(CFMutableStringRef sql, CFStringRef value, bool *needComma) { 944 assert(needComma); 945 if (*needComma) { 946 CFStringAppend(sql, CFSTR(",")); 947 } else { 948 *needComma = true; 949 } 950 CFStringAppend(sql, value); 951} 952 953static void SecDbAppendElementEquals(CFMutableStringRef sql, CFStringRef value, bool *needComma) { 954 SecDbAppendElement(sql, value, needComma); 955 CFStringAppend(sql, CFSTR("=?")); 956} 957 958/* Append AND is needWhere is NULL or *needWhere is false. Append WHERE 959 otherwise. Upon return *needWhere will be false. */ 960void 961SecDbAppendWhereOrAnd(CFMutableStringRef sql, bool *needWhere) { 962 if (!needWhere || !*needWhere) { 963 CFStringAppend(sql, CFSTR(" AND ")); 964 } else { 965 CFStringAppend(sql, CFSTR(" WHERE ")); 966 *needWhere = false; 967 } 968} 969 970void 971SecDbAppendWhereOrAndEquals(CFMutableStringRef sql, CFStringRef col, bool *needWhere) { 972 SecDbAppendWhereOrAnd(sql, needWhere); 973 CFStringAppend(sql, col); 974 CFStringAppend(sql, CFSTR("=?")); 975} 976 977static CFStringRef SecDbItemCopyInsertSQL(SecDbItemRef item, bool(^use_attr)(const SecDbAttr *attr)) { 978 CFMutableStringRef sql = CFStringCreateMutable(CFGetAllocator(item), 0); 979 CFStringAppend(sql, CFSTR("INSERT INTO ")); 980 CFStringAppend(sql, item->class->name); 981 CFStringAppend(sql, CFSTR("(")); 982 bool needComma = false; 983 CFIndex used_attr = 0; 984 SecDbForEachAttr(item->class, attr) { 985 if (use_attr(attr)) { 986 ++used_attr; 987 SecDbAppendElement(sql, attr->name, &needComma); 988 } 989 } 990 CFStringAppend(sql, CFSTR(")VALUES(?")); 991 while (used_attr-- > 1) { 992 CFStringAppend(sql, CFSTR(",?")); 993 } 994 CFStringAppend(sql, CFSTR(")")); 995 return sql; 996 997} 998 999static bool SecDbItemInsertBind(SecDbItemRef item, sqlite3_stmt *stmt, CFErrorRef *error, bool(^use_attr)(const SecDbAttr *attr)) { 1000 bool ok = true; 1001 int param = 0; 1002 SecDbForEachAttr(item->class, attr) { 1003 if (use_attr(attr)) { 1004 CFTypeRef value = SecDbItemGetValue(item, attr, error); 1005 if (!value || !SecDbAttrBind(attr, stmt, ++param, value, error)) { 1006 ok = false; 1007 break; 1008 } 1009 } 1010 } 1011 return ok; 1012} 1013 1014sqlite3_int64 SecDbItemGetRowId(SecDbItemRef item, CFErrorRef *error) { 1015 sqlite3_int64 row_id = 0; 1016 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error); 1017 if (attr) { 1018 CFNumberRef number = SecDbItemGetValue(item, attr, error); 1019 if (!isNumber(number)|| !CFNumberGetValue(number, kCFNumberSInt64Type, &row_id)) 1020 SecDbError(SQLITE_ERROR, error, CFSTR("rowid %@ is not a 64 bit number"), number); 1021 } 1022 1023 return row_id; 1024} 1025 1026static CFNumberRef SecDbItemCreateRowId(SecDbItemRef item, sqlite3_int64 rowid, CFErrorRef *error) { 1027 return CFNumberCreate(CFGetAllocator(item), kCFNumberSInt64Type, &rowid); 1028} 1029 1030bool SecDbItemSetRowId(SecDbItemRef item, sqlite3_int64 rowid, CFErrorRef *error) { 1031 bool ok = true; 1032 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error); 1033 if (attr) { 1034 CFNumberRef value = SecDbItemCreateRowId(item, rowid, error); 1035 if (!value) 1036 return false; 1037 1038 ok = SecDbItemSetValue(item, attr, value, error); 1039 CFRelease(value); 1040 } 1041 return ok; 1042} 1043 1044static bool SecDbItemClearRowId(SecDbItemRef item, CFErrorRef *error) { 1045 bool ok = true; 1046 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error); 1047 if (attr) { 1048 CFDictionaryRemoveValue(item->attributes, attr->name); 1049 //ok = SecDbItemSetValue(item, attr, kCFNull, error); 1050 } 1051 return ok; 1052} 1053 1054static bool SecDbItemSetLastInsertRowId(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) { 1055 sqlite3_int64 rowid = sqlite3_last_insert_rowid(SecDbHandle(dbconn)); 1056 return SecDbItemSetRowId(item, rowid, error); 1057} 1058 1059bool SecDbItemIsSyncable(SecDbItemRef item) { 1060 bool is_syncable; 1061 if (SecDbItemGetBoolValue(item, SecDbClassAttrWithKind(item->class, kSecDbSyncAttr, NULL), &is_syncable, NULL)) 1062 return is_syncable; 1063 return false; 1064} 1065 1066bool SecDbItemSetSyncable(SecDbItemRef item, bool sync, CFErrorRef *error) 1067{ 1068 return SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbSyncAttr, error), sync ? kCFBooleanTrue : kCFBooleanFalse, error); 1069} 1070 1071bool SecDbItemIsTombstone(SecDbItemRef item) { 1072 bool is_tomb; 1073 if (SecDbItemGetBoolValue(item, SecDbClassAttrWithKind(item->class, kSecDbTombAttr, NULL), &is_tomb, NULL)) 1074 return is_tomb; 1075 return false; 1076} 1077 1078CFDataRef SecDbItemGetPrimaryKey(SecDbItemRef item, CFErrorRef *error) { 1079 return SecDbItemGetValue(item, SecDbClassAttrWithKind(item->class, kSecDbPrimaryKeyAttr, error), error); 1080} 1081 1082CFDataRef SecDbItemGetSHA1(SecDbItemRef item, CFErrorRef *error) { 1083 return SecDbItemGetValue(item, SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, error), error); 1084} 1085 1086static SecDbQueryRef SecDbQueryCreateWithItemPrimaryKey(SecDbItemRef item, CFErrorRef *error) { 1087 CFMutableDictionaryRef dict = SecDbItemCopyPListWithMask(item, kSecDbPrimaryKeyFlag, error); 1088 if (!dict) 1089 return NULL; 1090 1091 SecDbQueryRef query = query_create(item->class, NULL, error); 1092 if (query) 1093 query->q_item = dict; 1094 else 1095 CFRelease(dict); 1096 1097 return query; 1098} 1099 1100static bool SecDbItemIsCorrupt(SecDbItemRef item, bool *is_corrupt, CFErrorRef *error) { 1101 CFErrorRef localError = NULL; 1102 bool ok = SecDbItemEnsureDecrypted(item, &localError); 1103 if (localError) { 1104 if (SecErrorGetOSStatus(localError) == errSecDecode) { 1105 // We failed to decrypt the item 1106 secerror("error %@ reading item %@ (corrupted)", localError, item); 1107 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem); 1108 *is_corrupt = true; 1109 ok = true; 1110 } else if (error && *error == NULL) { 1111 *error = localError; 1112 localError = NULL; 1113 } 1114 CFReleaseSafe(localError); 1115 } 1116 return ok; 1117} 1118 1119static bool SecDbItemDoInsert(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) { 1120 bool (^use_attr)(const SecDbAttr *attr) = ^bool(const SecDbAttr *attr) { 1121 return (attr->flags & kSecDbInFlag); 1122 }; 1123 CFStringRef sql = SecDbItemCopyInsertSQL(item, use_attr); 1124 __block bool ok = sql; 1125 if (sql) { 1126 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) { 1127 ok = (SecDbItemInsertBind(item, stmt, error, use_attr) && 1128 SecDbStep(dbconn, stmt, error, NULL) && 1129 SecDbItemSetLastInsertRowId(item, dbconn, error)); 1130 }); 1131 CFRelease(sql); 1132 } 1133 if (ok) 1134 secnotice("item", "inserted %@", item); 1135 1136 return ok; 1137} 1138 1139bool SecDbItemInsertOrReplace(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error, void(^duplicate)(SecDbItemRef item, SecDbItemRef *replace)) { 1140 __block CFErrorRef localError = NULL; 1141 __block bool ok = SecDbItemDoInsert(item, dbconn, &localError); 1142 if (!ok && localError && CFErrorGetCode(localError) == SQLITE_CONSTRAINT && CFEqual(kSecDbErrorDomain, CFErrorGetDomain(localError))) { 1143 SecDbQueryRef query = SecDbQueryCreateWithItemPrimaryKey(item, error); 1144 if (query) { 1145 SecDbItemSelect(query, dbconn, error, ^bool(const SecDbAttr *attr) { 1146 return attr->flags & kSecDbPrimaryKeyFlag; 1147 }, NULL, NULL, ^(SecDbItemRef old_item, bool *stop) { 1148 bool is_corrupt = false; 1149 ok = SecDbItemIsCorrupt(old_item, &is_corrupt, error); 1150 SecDbItemRef replace = NULL; 1151 if (is_corrupt) { 1152 // If old_item is corrupted pretend it's not there and just replace it. 1153 replace = item; 1154 CFRetain(replace); 1155 } else if (ok && duplicate) { 1156 duplicate(old_item, &replace); 1157 } 1158 if (replace) { 1159 const SecDbAttr *rowid_attr = SecDbClassAttrWithKind(old_item->class, kSecDbRowIdAttr, error); 1160 CFNumberRef oldrowid = SecDbItemGetCachedValue(old_item, rowid_attr); 1161 if (oldrowid) { 1162 ok = SecDbItemSetValue(replace, rowid_attr, oldrowid, &localError); 1163 if (ok && !is_corrupt) { 1164 ok = SecDbItemMakeYounger(replace, old_item, error); 1165 } 1166 ok = ok && SecDbItemDoUpdate(old_item, replace, dbconn, &localError, ^bool (const SecDbAttr *attr) { 1167 return attr->kind == kSecDbRowIdAttr; 1168 }); 1169 } else { 1170 ok = SecError(errSecInternal, &localError, CFSTR("no rowid for %@"), old_item); 1171 } 1172 CFRelease(replace); 1173 if (ok) 1174 CFReleaseNull(localError); // Clear the error, since we replaced the item. 1175 } 1176 }); 1177 ok &= query_destroy(query, error); 1178 } 1179 if (localError) 1180 ok = false; // We didn't clear the error so we're failing again. 1181 } 1182 1183 if (!ok) { 1184 /* 1185 CFErrorRef e = localError ? localError : error ? *error : NULL; 1186 secerror("INSERT %@ failed: %@%s", item, e, (e && CFErrorGetCode(e) == SQLITE_CONSTRAINT) ? 1187 " and SELECT failed to find tombstone" : ""); 1188 */ 1189 secerror("INSERT failed: %@", localError); 1190 if (localError) { 1191 if (error && *error == NULL) { 1192 *error = localError; 1193 localError = NULL; 1194 } else { 1195 CFReleaseNull(localError); 1196 } 1197 } 1198 } 1199 1200 return ok; 1201} 1202 1203bool SecDbItemInsert(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) { 1204 return SecDbItemInsertOrReplace(item, dbconn, error, ^(SecDbItemRef old_item, SecDbItemRef *replace) { 1205 if (SecDbItemIsTombstone(old_item)) { 1206 CFRetain(item); 1207 *replace = item; 1208 } 1209 }); 1210} 1211 1212static CFStringRef SecDbItemCopyUpdateSQL(SecDbItemRef old_item, SecDbItemRef new_item, bool(^use_attr_in_where)(const SecDbAttr *attr)) { 1213 CFMutableStringRef sql = CFStringCreateMutable(CFGetAllocator(new_item), 0); 1214 CFStringAppend(sql, CFSTR("UPDATE ")); 1215 CFStringAppend(sql, new_item->class->name); 1216 CFStringAppend(sql, CFSTR(" SET ")); 1217 bool needComma = false; 1218 CFIndex used_attr = 0; 1219 SecDbForEachAttrWithMask(new_item->class, attr, kSecDbInFlag) { 1220 ++used_attr; 1221 SecDbAppendElementEquals(sql, attr->name, &needComma); 1222 } 1223 1224 bool needWhere = true; 1225 SecDbForEachAttr(old_item->class, attr) { 1226 if (use_attr_in_where(attr)) { 1227 SecDbAppendWhereOrAndEquals(sql, attr->name, &needWhere); 1228 } 1229 } 1230 1231 return sql; 1232} 1233 1234static bool SecDbItemUpdateBind(SecDbItemRef old_item, SecDbItemRef new_item, sqlite3_stmt *stmt, CFErrorRef *error, bool(^use_attr_in_where)(const SecDbAttr *attr)) { 1235 bool ok = true; 1236 int param = 0; 1237 SecDbForEachAttrWithMask(new_item->class, attr, kSecDbInFlag) { 1238 CFTypeRef value = SecDbItemGetValue(new_item, attr, error); 1239 ok &= value && SecDbAttrBind(attr, stmt, ++param, value, error); 1240 if (!ok) 1241 break; 1242 } 1243 SecDbForEachAttr(old_item->class, attr) { 1244 if (use_attr_in_where(attr)) { 1245 CFTypeRef value = SecDbItemGetValue(old_item, attr, error); 1246 ok &= value && SecDbAttrBind(attr, stmt, ++param, value, error); 1247 if (!ok) 1248 break; 1249 } 1250 } 1251 return ok; 1252} 1253 1254// Primary keys are the same -- do an update 1255bool SecDbItemDoUpdate(SecDbItemRef old_item, SecDbItemRef new_item, SecDbConnectionRef dbconn, CFErrorRef *error, bool (^use_attr_in_where)(const SecDbAttr *attr)) { 1256 CFStringRef sql = SecDbItemCopyUpdateSQL(old_item, new_item, use_attr_in_where); 1257 __block bool ok = sql; 1258 if (sql) { 1259 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) { 1260 ok = SecDbItemUpdateBind(old_item, new_item, stmt, error, use_attr_in_where) && SecDbStep(dbconn, stmt, error, NULL); 1261 }); 1262 CFRelease(sql); 1263 } 1264 if (ok) 1265 secnotice("item", "replaced %@ with %@ in %@", old_item, new_item, dbconn); 1266 return ok; 1267} 1268 1269static CFStringRef SecDbItemCopyDeleteSQL(SecDbItemRef item, bool(^use_attr_in_where)(const SecDbAttr *attr)) { 1270 CFMutableStringRef sql = CFStringCreateMutable(CFGetAllocator(item), 0); 1271 CFStringAppend(sql, CFSTR("DELETE FROM ")); 1272 CFStringAppend(sql, item->class->name); 1273 bool needWhere = true; 1274 SecDbForEachAttr(item->class, attr) { 1275 if (use_attr_in_where(attr)) { 1276 SecDbAppendWhereOrAndEquals(sql, attr->name, &needWhere); 1277 } 1278 } 1279 1280 return sql; 1281} 1282 1283static bool SecDbItemDeleteBind(SecDbItemRef item, sqlite3_stmt *stmt, CFErrorRef *error, bool(^use_attr_in_where)(const SecDbAttr *attr)) { 1284 bool ok = true; 1285 int param = 0; 1286 SecDbForEachAttr(item->class, attr) { 1287 if (use_attr_in_where(attr)) { 1288 CFTypeRef value = SecDbItemGetValue(item, attr, error); 1289 ok &= value && SecDbAttrBind(attr, stmt, ++param, value, error); 1290 if (!ok) 1291 break; 1292 } 1293 } 1294 return ok; 1295} 1296 1297static bool SecDbItemDoDelete(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error, bool (^use_attr_in_where)(const SecDbAttr *attr)) { 1298 CFStringRef sql = SecDbItemCopyDeleteSQL(item, use_attr_in_where); 1299 __block bool ok = sql; 1300 if (sql) { 1301 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) { 1302 ok = SecDbItemDeleteBind(item, stmt, error, use_attr_in_where) && SecDbStep(dbconn, stmt, error, NULL); 1303 }); 1304 CFRelease(sql); 1305 } 1306 if (ok) 1307 secnotice("item", "deleted %@ from %@", item, dbconn); 1308 return ok; 1309} 1310 1311#if 0 1312static bool SecDbItemDeleteTombstone(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) { 1313 bool ok = true; 1314 // TODO: Treat non decryptable items like tombstones here too and delete them 1315 SecDbItemRef tombstone = SecDbItemCopyTombstone(item, error); 1316 ok = tombstone; 1317 if (tombstone) { 1318 ok = SecDbItemClearRowId(tombstone, error); 1319 if (ok) { 1320 ok = SecDbItemDoDelete(tombstone, dbconn, error, ^bool (const SecDbAttr *attr) { 1321 return SecDbIsTombstoneDbSelectAttr(attr); 1322 }); 1323 } 1324 CFRelease(tombstone); 1325 } 1326 return ok; 1327} 1328#endif 1329 1330// Replace old_item with new_item. If primary keys are the same this does an update otherwise it does a delete + add 1331bool SecDbItemUpdate(SecDbItemRef old_item, SecDbItemRef new_item, SecDbConnectionRef dbconn, bool makeTombstone, CFErrorRef *error) { 1332 __block bool ok = true; 1333 __block CFErrorRef localError = NULL; 1334 1335 CFDataRef old_pk = SecDbItemGetPrimaryKey(old_item, error); 1336 CFDataRef new_pk = SecDbItemGetPrimaryKey(new_item, error); 1337 1338 ok = old_pk && new_pk; 1339 1340 bool pk_equal = ok && CFEqual(old_pk, new_pk); 1341 if (pk_equal) { 1342 ok = SecDbItemMakeYounger(new_item, old_item, error); 1343 } 1344 ok = ok && SecDbItemDoUpdate(old_item, new_item, dbconn, &localError, ^bool(const SecDbAttr *attr) { 1345 return attr->kind == kSecDbRowIdAttr; 1346 }); 1347 1348 if (localError) { 1349 if(CFErrorGetCode(localError) == SQLITE_CONSTRAINT && CFEqual(kSecDbErrorDomain, CFErrorGetDomain(localError))) { 1350 /* Update failed because we changed the PrimaryKey and there was a dup. 1351 Find the dup and see if it is a tombstone or corrupted item. */ 1352 SecDbQueryRef query = SecDbQueryCreateWithItemPrimaryKey(new_item, error); 1353 ok = query; 1354 if (query) { 1355 ok &= SecDbItemSelect(query, dbconn, error, ^bool(const SecDbAttr *attr) { 1356 return attr->flags & kSecDbPrimaryKeyFlag; 1357 }, NULL, NULL, ^(SecDbItemRef duplicate_item, bool *stop) { 1358 bool is_corrupt = false; 1359 bool is_tomb = false; 1360 ok = SecDbItemIsCorrupt(duplicate_item, &is_corrupt, error); 1361 if (ok && !is_corrupt) { 1362 if ((is_tomb = SecDbItemIsTombstone(duplicate_item))) 1363 ok = SecDbItemMakeYounger(new_item, duplicate_item, error); 1364 } 1365 if (ok && (is_corrupt || is_tomb)) { 1366 ok = SecDbItemDoDelete(old_item, dbconn, error, ^bool (const SecDbAttr *attr) { 1367 return attr->kind == kSecDbRowIdAttr; 1368 }); 1369 ok = ok && SecDbItemDoUpdate(duplicate_item, new_item, dbconn, error, ^bool (const SecDbAttr *attr) { 1370 return attr->kind == kSecDbRowIdAttr; 1371 }); 1372 CFReleaseNull(localError); 1373 } 1374 }); 1375 ok &= query_destroy(query, error); 1376 } 1377 } 1378 1379 if (localError) { 1380 ok = false; 1381 if (error && *error == NULL) { 1382 *error = localError; 1383 localError = NULL; 1384 } 1385 CFReleaseSafe(localError); 1386 } 1387 } 1388 1389 if (ok && !pk_equal && makeTombstone) { 1390 /* The primary key of new_item is different than that of old_item, we 1391 have been asked to make a tombstone so leave one for the old_item. */ 1392 SecDbItemRef tombstone = SecDbItemCopyTombstone(old_item, error); 1393 ok = tombstone; 1394 if (tombstone) { 1395 ok = (SecDbItemClearRowId(tombstone, error) && 1396 SecDbItemDoInsert(tombstone, dbconn, error)); 1397 CFRelease(tombstone); 1398 } 1399 } 1400 1401 return ok; 1402} 1403 1404// Replace the object with a tombstone 1405bool SecDbItemDelete(SecDbItemRef item, SecDbConnectionRef dbconn, bool makeTombstone, CFErrorRef *error) { 1406 bool ok = false; 1407 if (makeTombstone) { 1408 SecDbItemRef tombstone = SecDbItemCopyTombstone(item, error); 1409 if (tombstone) { 1410 ok = SecDbItemDoUpdate(item, tombstone, dbconn, error, ^bool(const SecDbAttr *attr) { 1411 return attr->kind == kSecDbRowIdAttr; 1412 }); 1413 CFRelease(tombstone); 1414 } 1415 } else { 1416 ok = SecDbItemDoDelete(item, dbconn, error, ^bool(const SecDbAttr *attr) { 1417 return attr->kind == kSecDbRowIdAttr; 1418 }); 1419 } 1420 return ok; 1421} 1422 1423CFStringRef SecDbItemCopySelectSQL(SecDbQueryRef query, 1424 bool (^return_attr)(const SecDbAttr *attr), 1425 bool (^use_attr_in_where)(const SecDbAttr *attr), 1426 bool (^add_where_sql)(CFMutableStringRef sql, bool *needWhere)) { 1427 CFMutableStringRef sql = CFStringCreateMutable(kCFAllocatorDefault, 0); 1428 CFStringAppend(sql, CFSTR("SELECT ")); 1429 // What are we selecting? 1430 bool needComma = false; 1431 SecDbForEachAttr(query->q_class, attr) { 1432 if (return_attr(attr)) 1433 SecDbAppendElement(sql, attr->name, &needComma); 1434 } 1435 1436 // From which table? 1437 CFStringAppend(sql, CFSTR(" FROM ")); 1438 CFStringAppend(sql, query->q_class->name); 1439 1440 // And which elements do we want to select 1441 bool needWhere = true; 1442 SecDbForEachAttr(query->q_class, attr) { 1443 if (use_attr_in_where(attr)) { 1444 SecDbAppendWhereOrAndEquals(sql, attr->name, &needWhere); 1445 } 1446 } 1447 // Append SQL for access groups and limits. 1448 if (add_where_sql) 1449 add_where_sql(sql, &needWhere); 1450 1451 return sql; 1452} 1453 1454bool SecDbItemSelectBind(SecDbQueryRef query, sqlite3_stmt *stmt, CFErrorRef *error, 1455 bool (^use_attr_in_where)(const SecDbAttr *attr), 1456 bool (^bind_added_where)(sqlite3_stmt *stmt, int col)) { 1457 bool ok = true; 1458 int param = 0; 1459 SecDbForEachAttr(query->q_class, attr) { 1460 if (use_attr_in_where(attr)) { 1461 CFTypeRef value = CFDictionaryGetValue(query->q_item, attr->name); 1462 ok &= SecDbAttrBind(attr, stmt, ++param, value, error); 1463 if (!ok) 1464 break; 1465 } 1466 } 1467 // TODO: Bind arguments for access groups and limits. 1468 if (bind_added_where) 1469 bind_added_where(stmt, ++param); 1470 1471 return ok; 1472} 1473 1474bool SecDbItemSelect(SecDbQueryRef query, SecDbConnectionRef dbconn, CFErrorRef *error, 1475 bool (^use_attr_in_where)(const SecDbAttr *attr), 1476 bool (^add_where_sql)(CFMutableStringRef sql, bool *needWhere), 1477 bool (^bind_added_where)(sqlite3_stmt *stmt, int col), 1478 void (^handle_row)(SecDbItemRef item, bool *stop)) { 1479 __block bool ok = true; 1480 bool (^return_attr)(const SecDbAttr *attr) = ^bool (const SecDbAttr * attr) { 1481 return attr->kind == kSecDbRowIdAttr || attr->kind == kSecDbEncryptedDataAttr; 1482 }; 1483 CFStringRef sql = SecDbItemCopySelectSQL(query, return_attr, use_attr_in_where, add_where_sql); 1484 if (sql) { 1485 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) { 1486 ok = (SecDbItemSelectBind(query, stmt, error, use_attr_in_where, bind_added_where) && 1487 SecDbStep(dbconn, stmt, error, ^(bool *stop) { 1488 SecDbItemRef item = SecDbItemCreateWithStatement(kCFAllocatorDefault, query->q_class, stmt, query->q_keybag, error, return_attr); 1489 if (item) { 1490 handle_row(item, stop); 1491 CFRelease(item); 1492 } else { 1493 //*stop = true; 1494 //ok = false; 1495 } 1496 })); 1497 }); 1498 CFRelease(sql); 1499 } else { 1500 ok = false; 1501 } 1502 return ok; 1503} 1504 1505