SecDb.c
1 /* 2 * Copyright (c) 2012-2017 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 #include "SecDb.h" 26 #include "SecDbInternal.h" 27 #include "debugging.h" 28 29 #include <sqlite3.h> 30 #include <sqlite3_private.h> 31 #include <CoreFoundation/CoreFoundation.h> 32 #include <libgen.h> 33 #include <sys/csr.h> 34 #include <sys/stat.h> 35 #include <AssertMacros.h> 36 #include "SecCFWrappers.h" 37 #include "SecCFError.h" 38 #include "SecIOFormat.h" 39 #include <stdio.h> 40 #include "Security/SecBase.h" 41 #include "SecAutorelease.h" 42 #include <os/assumes.h> 43 #include <xpc/private.h> // xpc_transaction_exit_clean() 44 45 #ifdef DARLING 46 #include <pthread.h> 47 #endif 48 49 // 50 // Architecturally inverted files 51 // These are in SecureObjectSync but utilities depends on them 52 // <rdar://problem/20802079> Fix layer violation (SOSDigestVector, SOSManifest, SecDB.c) 53 // 54 #include "keychain/SecureObjectSync/SOSDigestVector.h" 55 #include "keychain/SecureObjectSync/SOSManifest.h" 56 57 #define SECDB_DEBUGGING 0 58 59 struct __OpaqueSecDbStatement { 60 CFRuntimeBase _base; 61 62 SecDbConnectionRef dbconn; 63 sqlite3_stmt *stmt; 64 }; 65 66 struct __OpaqueSecDbConnection { 67 CFRuntimeBase _base; 68 69 //CFMutableDictionaryRef statements; 70 71 SecDbRef db; // NONRETAINED, since db or block retains us 72 bool readOnly; 73 bool inTransaction; 74 SecDbTransactionSource source; 75 bool isCorrupted; 76 int maybeCorruptedCode; 77 bool hasIOFailure; 78 CFErrorRef corruptionError; 79 sqlite3 *handle; 80 // Pending deletions and additions for the current transaction 81 // Entires are either: 82 // 1) a CFArrayRef of 1 element representing a deletion, 83 // 2) a CFArrayRef of 2 elements representing the element 0 having been replaced with element 1 84 // 3) a CFTypeRef that is not a CFArrayRef, representing an add of the element in question. 85 CFMutableArrayRef changes; 86 }; 87 88 struct __OpaqueSecDb { 89 CFRuntimeBase _base; 90 91 CFStringRef db_path; 92 dispatch_queue_t queue; 93 dispatch_queue_t commitQueue; 94 95 CFMutableArrayRef idleWriteConnections; // up to kSecDbMaxWriters of them (currently 1, requires locking change for >1) 96 CFMutableArrayRef idleReadConnections; // up to kSecDbMaxReaders of them 97 pthread_mutex_t writeMutex; 98 pthread_mutexattr_t writeMutexAttrs; 99 // TODO: Replace after we have rdar://problem/60961964 100 dispatch_semaphore_t readSemaphore; 101 102 bool didFirstOpen; 103 bool (^opened)(SecDbRef db, SecDbConnectionRef dbconn, bool didCreate, bool *callMeAgainForNextConnection, CFErrorRef *error); 104 bool callOpenedHandlerForNextConnection; 105 CFMutableArrayRef notifyPhase; /* array of SecDBNotifyBlock */ 106 mode_t mode; /* database file permissions */ 107 bool readWrite; /* open database read-write */ 108 bool allowRepair; /* allow database repair */ 109 bool useWAL; /* use WAL mode */ 110 bool useRobotVacuum; /* use if SecDB should manage vacuum behind your back */ 111 uint8_t maxIdleHandles; 112 void (^corruptionReset)(void); 113 }; 114 115 // MARK: Error domains and error helper functions 116 117 CFStringRef kSecDbErrorDomain = CFSTR("com.apple.utilities.sqlite3"); 118 119 bool SecDbError(int sql_code, CFErrorRef *error, CFStringRef format, ...) { 120 if (sql_code == SQLITE_OK) return true; 121 122 if (error) { 123 va_list args; 124 CFIndex code = sql_code; 125 CFErrorRef previousError = *error; 126 127 *error = NULL; 128 va_start(args, format); 129 SecCFCreateErrorWithFormatAndArguments(code, kSecDbErrorDomain, previousError, error, NULL, format, args); 130 va_end(args); 131 } 132 return false; 133 } 134 135 bool SecDbErrorWithDb(int sql_code, sqlite3 *db, CFErrorRef *error, CFStringRef format, ...) { 136 if (sql_code == SQLITE_OK) return true; 137 if (error) { 138 va_list args; 139 va_start(args, format); 140 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args); 141 va_end(args); 142 CFStringRef errno_code = NULL; 143 144 if (sql_code == SQLITE_CANTOPEN) { 145 int errno_number = sqlite3_system_errno(db); 146 errno_code = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), errno_number); 147 } else { 148 errno_code = CFRetain(CFSTR("")); 149 } 150 151 int extended_code = sqlite3_extended_errcode(db); 152 if (sql_code == extended_code) 153 SecDbError(sql_code, error, CFSTR("%@: [%d]%@ %s"), message, sql_code, errno_code, sqlite3_errmsg(db)); 154 else 155 SecDbError(sql_code, error, CFSTR("%@: [%d->%d]%@ %s"), message, sql_code, extended_code, errno_code, sqlite3_errmsg(db)); 156 CFReleaseSafe(message); 157 CFReleaseSafe(errno_code); 158 } 159 return false; 160 } 161 162 bool SecDbErrorWithStmt(int sql_code, sqlite3_stmt *stmt, CFErrorRef *error, CFStringRef format, ...) { 163 if (sql_code == SQLITE_OK) return true; 164 if (error) { 165 va_list args; 166 va_start(args, format); 167 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args); 168 va_end(args); 169 170 sqlite3 *db = sqlite3_db_handle(stmt); 171 const char *sql = sqlite3_sql(stmt); 172 int extended_code = sqlite3_extended_errcode(db); 173 if (sql_code == extended_code) 174 SecDbError(sql_code, error, CFSTR("%@: [%d] %s sql: %s"), message, sql_code, sqlite3_errmsg(db), sql); 175 else 176 SecDbError(sql_code, error, CFSTR("%@: [%d->%d] %s sql: %s"), message, sql_code, extended_code, sqlite3_errmsg(db), sql); 177 CFReleaseSafe(message); 178 } 179 return false; 180 } 181 182 // A callback for the sqlite3_log() interface. 183 static void sqlite3Log(void *pArg, int iErrCode, const char *zMsg){ 184 secdebug("sqlite3", "(%d) %s", iErrCode, zMsg); 185 } 186 187 void _SecDbServerSetup(void) 188 { 189 static dispatch_once_t onceToken; 190 dispatch_once(&onceToken, ^{ 191 int rx = sqlite3_config(SQLITE_CONFIG_LOG, sqlite3Log, NULL); 192 if (SQLITE_OK != rx) { 193 secwarning("Could not set up sqlite global error logging to syslog: %d", rx); 194 } 195 }); 196 } 197 198 199 // MARK: - 200 // MARK: Static helper functions 201 202 static bool SecDbOpenHandle(SecDbConnectionRef dbconn, bool *created, CFErrorRef *error); 203 static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn, int rc, CFErrorRef *error); 204 205 #pragma mark - 206 #pragma mark SecDbRef 207 208 static CFStringRef 209 SecDbCopyFormatDescription(CFTypeRef value, CFDictionaryRef formatOptions) 210 { 211 SecDbRef db = (SecDbRef)value; 212 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecDb path:%@ connections: %@>"), db->db_path, db->idleReadConnections); 213 } 214 215 216 static void 217 SecDbDestroy(CFTypeRef value) 218 { 219 SecDbRef db = (SecDbRef)value; 220 221 CFReleaseNull(db->db_path); 222 dispatch_sync(db->queue, ^{ 223 CFReleaseNull(db->idleWriteConnections); 224 CFReleaseNull(db->idleReadConnections); 225 }); 226 227 if (db->queue) { 228 dispatch_release(db->queue); 229 db->queue = NULL; 230 } 231 if (db->commitQueue) { 232 dispatch_release(db->commitQueue); 233 db->commitQueue = NULL; 234 } 235 236 pthread_mutex_destroy(&(db->writeMutex)); 237 238 if (db->readSemaphore) { 239 dispatch_release(db->readSemaphore); 240 db->readSemaphore = NULL; 241 } 242 243 if (db->opened) { 244 Block_release(db->opened); 245 db->opened = NULL; 246 } 247 CFReleaseNull(db->notifyPhase); 248 } 249 250 CFGiblisFor(SecDb) 251 252 SecDbRef 253 SecDbCreate(CFStringRef dbName, mode_t mode, bool readWrite, bool allowRepair, bool useWAL, bool useRobotVacuum, uint8_t maxIdleHandles, 254 bool (^opened)(SecDbRef db, SecDbConnectionRef dbconn, bool didCreate, bool *callMeAgainForNextConnection, CFErrorRef *error)) 255 { 256 SecDbRef db = NULL; 257 258 db = CFTypeAllocate(SecDb, struct __OpaqueSecDb, kCFAllocatorDefault); 259 require(db != NULL, done); 260 261 CFStringPerformWithCString(dbName, ^(const char *dbNameStr) { 262 db->queue = dispatch_queue_create(dbNameStr, DISPATCH_QUEUE_SERIAL); 263 }); 264 CFStringRef commitQueueStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-commit"), dbName); 265 CFStringPerformWithCString(commitQueueStr, ^(const char *cqNameStr) { 266 db->commitQueue = dispatch_queue_create(cqNameStr, DISPATCH_QUEUE_CONCURRENT); 267 }); 268 CFReleaseNull(commitQueueStr); 269 db->readSemaphore = dispatch_semaphore_create(kSecDbMaxReaders); 270 271 bool mutexAttrSuccess = (0 == pthread_mutexattr_init(&(db->writeMutexAttrs))); 272 if(mutexAttrSuccess) { 273 mutexAttrSuccess = (0 == pthread_mutexattr_setpolicy_np(&(db->writeMutexAttrs), PTHREAD_MUTEX_POLICY_FAIRSHARE_NP)); 274 } 275 276 if(!mutexAttrSuccess) { 277 seccritical("SecDb: SecDbCreate failed to create attributes for the write mutex; fairness properties are no longer present"); 278 } 279 280 if (pthread_mutex_init(&(db->writeMutex), (mutexAttrSuccess ? &(db->writeMutexAttrs) : NULL)) != 0) { 281 seccritical("SecDb: SecDbCreate failed to init the write mutex, this will end badly"); 282 } 283 284 db->idleWriteConnections = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 285 db->idleReadConnections = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 286 db->opened = opened ? Block_copy(opened) : NULL; 287 if (getenv("__OSINSTALL_ENVIRONMENT") != NULL) { 288 // TODO: Move this code out of this layer 289 secinfo("#SecDB", "SecDB: running from installer"); 290 291 db->db_path = CFSTR("file::memory:?cache=shared"); 292 } else { 293 db->db_path = CFStringCreateCopy(kCFAllocatorDefault, dbName); 294 } 295 db->mode = mode; 296 db->readWrite = readWrite; 297 db->allowRepair = allowRepair; 298 db->useWAL = useWAL; 299 db->useRobotVacuum = useRobotVacuum; 300 db->maxIdleHandles = maxIdleHandles; 301 db->corruptionReset = NULL; 302 303 done: 304 return db; 305 } 306 307 CFIndex 308 SecDbIdleConnectionCount(SecDbRef db) { 309 __block CFIndex count = 0; 310 dispatch_sync(db->queue, ^{ 311 count = CFArrayGetCount(db->idleReadConnections); 312 count += CFArrayGetCount(db->idleWriteConnections); 313 }); 314 return count; 315 } 316 317 void SecDbAddNotifyPhaseBlock(SecDbRef db, SecDBNotifyBlock notifyPhase) 318 { 319 #if !TARGET_OS_BRIDGE 320 // SecDbNotifyPhase seems to mostly be called on the db's commitQueue, and not the db's queue. Therefore, protect the array with that queue. 321 dispatch_sync(db->commitQueue, ^{ 322 SecDBNotifyBlock block = Block_copy(notifyPhase); /* Force the block off the stack */ 323 if (db->notifyPhase == NULL) { 324 db->notifyPhase = CFArrayCreateMutableForCFTypes(NULL); 325 } 326 CFArrayAppendValue(db->notifyPhase, block); 327 Block_release(block); 328 }); 329 #endif 330 } 331 332 static void SecDbNotifyPhase(SecDbConnectionRef dbconn, SecDbTransactionPhase phase) { 333 #if !TARGET_OS_BRIDGE 334 if (CFArrayGetCount(dbconn->changes)) { 335 CFArrayRef changes = dbconn->changes; 336 dbconn->changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 337 if (dbconn->db->notifyPhase) { 338 CFArrayForEach(dbconn->db->notifyPhase, ^(const void *value) { 339 SecDBNotifyBlock notifyBlock = (SecDBNotifyBlock)value; 340 notifyBlock(dbconn, phase, dbconn->source, changes); 341 }); 342 } 343 CFReleaseSafe(changes); 344 } 345 #endif 346 } 347 348 static void SecDbOnNotify(SecDbConnectionRef dbconn, void (^perform)(void)) { 349 perform(); 350 } 351 352 CFStringRef SecDbGetPath(SecDbRef db) { 353 if(!db) { 354 return NULL; 355 } 356 return db->db_path; 357 } 358 359 360 #pragma mark - 361 #pragma mark SecDbConnectionRef 362 363 static bool SecDbCheckCorrupted(SecDbConnectionRef dbconn) 364 { 365 __block bool checkDidRun = false; 366 __block bool isCorrupted = false; 367 __block CFErrorRef error = NULL; 368 SecDbPrepare(dbconn, CFSTR("PRAGMA integrity_check"), &error, ^(sqlite3_stmt *stmt) { 369 SecDbStep(dbconn, stmt, &error, ^(bool *stop) { 370 const char * result = (const char*)sqlite3_column_text(stmt, 0); 371 if (!result || strncasecmp(result, "ok", 3) != 0) { 372 isCorrupted = true; 373 secerror("SecDBCheckCorrupted integrity_check returned %s", (result) ? result : "NULL"); 374 } 375 checkDidRun = true; 376 }); 377 }); 378 if (!checkDidRun) { 379 // An error occurred in SecDbPrepare before we could run the block. 380 if (error) { 381 CFIndex code = CFErrorGetCode(error); 382 if (SQLITE_CORRUPT == code || SQLITE_NOTADB == code) { 383 isCorrupted = true; 384 } 385 secinfo("#SecDB", "#SecDB warning error %{public}@ when running integrity check", error); 386 } else { 387 // We don't have an error ref if SecDbPrepare has called SecDbConnectionCheckCode, 388 // which then called SecDbHandleCorrupt. That code path is only entered when the 389 // original error was SQLITE_CORRUPT or SQLITE_NOTADB. On other errors, the 390 // CFErrorRef is not cleared and we can just check the code above. 391 isCorrupted = true; 392 secinfo("#SecDB", "#SecDB warning: failed to run integrity check due to corruption"); 393 } 394 } 395 if (isCorrupted) { 396 if (checkDidRun) { 397 secerror("SecDBCheckCorrupted ran integrity_check, and that didn't return ok"); 398 } else { 399 secerror("SecDBCheckCorrupted failed to run integrity check"); 400 } 401 } 402 CFReleaseNull(error); 403 404 return isCorrupted; 405 } 406 407 static bool SecDbDidCreateFirstConnection(SecDbConnectionRef dbconn, bool didCreate, CFErrorRef *error) 408 { 409 secinfo("#SecDB", "#SecDB starting maintenance"); 410 bool ok = true; 411 412 // Historical note: this used to check for integrity but that became too slow and caused panics at boot. 413 // Now, just react to SQLite errors when doing an operation. If file on disk is borked it'll tell us right away. 414 415 if (!dbconn->isCorrupted && dbconn->db->opened) { 416 CFErrorRef localError = NULL; 417 418 dbconn->db->callOpenedHandlerForNextConnection = false; 419 ok = dbconn->db->opened(dbconn->db, dbconn, didCreate, &dbconn->db->callOpenedHandlerForNextConnection, &localError); 420 421 if (!ok) 422 secerror("opened block failed: %@", localError); 423 424 if (!dbconn->isCorrupted && error && *error == NULL) { 425 *error = localError; 426 localError = NULL; 427 } else { 428 if (localError) 429 secerror("opened block failed: error (%@) is being released and lost", localError); 430 CFReleaseNull(localError); 431 } 432 } 433 434 if (dbconn->isCorrupted) { 435 ok = SecDbHandleCorrupt(dbconn, 0, error); 436 } 437 438 secinfo("#SecDB", "#SecDB starting maintenance"); 439 return ok; 440 } 441 442 void SecDbCorrupt(SecDbConnectionRef dbconn, CFErrorRef error) 443 { 444 if (__security_simulatecrash_enabled()) { 445 os_log_fault(secLogObjForScope("SecEmergency"), "SecDBCorrupt: %@", error); 446 } 447 dbconn->isCorrupted = true; 448 CFRetainAssign(dbconn->corruptionError, error); 449 } 450 451 452 static uint8_t knownDbPathIndex(SecDbConnectionRef dbconn) 453 { 454 455 if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/keychain-2.db"))) 456 return 1; 457 if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/ocspcache.sqlite3"))) 458 return 2; 459 if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/TrustStore.sqlite3"))) 460 return 3; 461 if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/caissuercache.sqlite3"))) 462 return 4; 463 464 /* Unknown DB path */ 465 return 0; 466 } 467 468 static bool SecDbConnectionCheckCode(SecDbConnectionRef dbconn, int code, CFErrorRef *error, CFStringRef desc, ...) 469 CF_FORMAT_FUNCTION(4, 5); 470 471 // Return true if there was no error, returns false otherwise and set *error to an appropriate CFErrorRef. 472 static bool SecDbConnectionCheckCode(SecDbConnectionRef dbconn, int code, CFErrorRef *error, CFStringRef desc, ...) { 473 if (code == SQLITE_OK || code == SQLITE_DONE) 474 return true; 475 476 if (error) { 477 va_list args; 478 va_start(args, desc); 479 CFStringRef msg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, desc, args); 480 va_end(args); 481 SecDbErrorWithDb(code, dbconn->handle, error, CFSTR("%@"), msg); 482 CFRelease(msg); 483 } 484 485 dbconn->hasIOFailure |= (SQLITE_IOERR == code); 486 487 /* If it's already corrupted, don't try to recover */ 488 if (dbconn->isCorrupted) { 489 CFStringRef reason = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, 490 CFSTR("SQL DB %@ is corrupted already. Corruption error was: %d (previously %d)"), 491 dbconn->db->db_path, code, dbconn->maybeCorruptedCode); 492 secerror("%@",reason); 493 __security_simulatecrash(reason, __sec_exception_code_TwiceCorruptDb(knownDbPathIndex(dbconn))); 494 CFReleaseSafe(reason); 495 // We can't fall through to the checking case because it eventually calls SecDbConnectionCheckCode again. 496 // However, this is the second time we're seeing corruption so let's take the ultimate measure. 497 if ((SQLITE_CORRUPT == code) || (SQLITE_NOTADB == code)) { 498 secerror("SecDbConnectionCheckCode detected corruption twice: going to handle corrupt DB"); 499 (void)SecDbHandleCorrupt(dbconn, code, error); 500 } 501 return false; 502 } 503 504 // NOTADB means file is garbage, so it's functionally equivalent to corruption 505 dbconn->isCorrupted = (SQLITE_CORRUPT == code) || (SQLITE_NOTADB == code); 506 if (dbconn->isCorrupted) { 507 /* Run integrity check and only make dbconn->isCorrupted true and 508 run the corruption handler if the integrity check conclusively fails. */ 509 dbconn->maybeCorruptedCode = code; 510 dbconn->isCorrupted = SecDbCheckCorrupted(dbconn); 511 if (dbconn->isCorrupted) { 512 secerror("operation returned code: %d integrity check=fail", code); 513 (void)SecDbHandleCorrupt(dbconn, code, error); 514 } else { 515 secerror("operation returned code: %d: integrity check=pass", code); 516 } 517 } 518 519 return false; 520 } 521 522 #define BUSY_TIMEOUT_MS (5 * 60 * 1000) /* 5 minutes */ 523 524 static int sleepBackoff[] = { 10, 20, 50, 100, 250 }; 525 static int sumBackoff[] = { 10, 30, 80, 180, 430 }; 526 static int NumberOfSleepBackoff = sizeof(sleepBackoff)/sizeof(sleepBackoff[0]); 527 528 // Use these as silly hacks to encode the SQLite return code in the backtrace, for hang debugging purposes 529 static void __attribute__((noinline)) SecDbLockSleep(int ms) { 530 sqlite3_sleep(ms); 531 } 532 533 static void __attribute__((noinline)) SecDbBusySleep(int ms) { 534 sqlite3_sleep(ms); 535 } 536 537 // Return true causes the operation to be tried again. 538 // Note that we set sqlite3_busy_timeout on the connection, so anytime you're in here, it's likely due to SQLITE_LOCKED. 539 static bool SecDbWaitIfNeeded(SecDbConnectionRef dbconn, int s3e, sqlite3_stmt *stmt, CFStringRef desc, int nTries, CFErrorRef *error) { 540 if (((0xFF & s3e) == SQLITE_BUSY) || ((0xFF & s3e) == SQLITE_LOCKED)) { 541 int totaltimeout, timeout; 542 543 _Static_assert(sizeof(sumBackoff) == sizeof(sleepBackoff), "matching arrays not matching"); 544 _Static_assert(sizeof(sumBackoff[0]) == sizeof(sleepBackoff[0]), "matching arrays not matching"); 545 546 if (nTries < NumberOfSleepBackoff) { 547 timeout = sleepBackoff[nTries]; 548 totaltimeout = sumBackoff[nTries]; 549 } else { 550 timeout = sleepBackoff[NumberOfSleepBackoff - 1]; 551 totaltimeout = sumBackoff[NumberOfSleepBackoff - 1] + (timeout * (nTries - NumberOfSleepBackoff)); 552 } 553 if (totaltimeout < BUSY_TIMEOUT_MS) { 554 secinfo("#SecDB", "sqlite busy/locked: %d ntries: %d totaltimeout: %d", s3e, nTries, totaltimeout); 555 if(((0xFF & s3e) == SQLITE_LOCKED)) { 556 SecDbLockSleep(timeout); 557 } else { 558 SecDbBusySleep(timeout); 559 } 560 return true; 561 } else { 562 secinfo("#SecDB", "sqlite busy/locked: too long: %d ms, giving up", totaltimeout); 563 } 564 } 565 566 return SecDbConnectionCheckCode(dbconn, s3e, error, CFSTR("%@"), desc); 567 } 568 569 enum SecDbStepResult { 570 kSecDbErrorStep = 0, 571 kSecDbRowStep = 1, 572 kSecDbDoneStep = 2, 573 }; 574 typedef enum SecDbStepResult SecDbStepResult; 575 576 static SecDbStepResult _SecDbStep(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error) { 577 assert(stmt != NULL); 578 int s3e; 579 int ntries = 0; 580 for (;;) { 581 if (SecDbConnectionIsReadOnly(dbconn) && !sqlite3_stmt_readonly(stmt)) { 582 secerror("_SecDbStep: SecDbConnection is readonly but we're about to write: %s", sqlite3_sql(stmt)); 583 } 584 s3e = sqlite3_step(stmt); 585 if (s3e == SQLITE_ROW) { 586 return kSecDbRowStep; 587 } else if (s3e == SQLITE_DONE) { 588 /* 589 ** ^[SQLITE_DONE] means that the statement has finished executing 590 ** successfully. sqlite3_step() should not be called again on this virtual 591 ** machine without first calling [] to reset the virtual 592 ** machine back to its initial state. 593 */ 594 sqlite3_reset(stmt); 595 return kSecDbDoneStep; 596 } else if (!SecDbWaitIfNeeded(dbconn, s3e, stmt, CFSTR("step"), ntries, error)) { 597 return kSecDbErrorStep; 598 } 599 ntries++; 600 }; 601 } 602 603 bool 604 SecDbExec(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error) 605 { 606 bool ok = true; 607 CFRetain(sql); 608 while (sql) { 609 CFStringRef tail = NULL; 610 if (ok) { 611 sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, &tail, error); 612 ok = stmt != NULL; 613 if (stmt) { 614 SecDbStepResult sr; 615 while ((sr = _SecDbStep(dbconn, stmt, error)) == kSecDbRowStep); 616 if (sr == kSecDbErrorStep) 617 ok = false; 618 ok &= SecDbReleaseCachedStmt(dbconn, sql, stmt, error); 619 } 620 } else { 621 // TODO We already have an error here we really just want the left over sql in it's userData 622 ok = SecDbError(SQLITE_ERROR, error, CFSTR("Error with unexecuted sql remaining %@"), sql); 623 } 624 CFRelease(sql); 625 sql = tail; 626 } 627 return ok; 628 } 629 630 static int SecDBGetInteger(SecDbConnectionRef dbconn, CFStringRef sql) 631 { 632 __block int number = -1; 633 __block CFErrorRef error = NULL; 634 635 (void)SecDbWithSQL(dbconn, sql, &error, ^bool(sqlite3_stmt *sqlStmt) { 636 (void)SecDbStep(dbconn, sqlStmt, &error, ^(bool *stop) { 637 number = sqlite3_column_int(sqlStmt, 0); 638 *stop = true; 639 }); 640 return true; 641 }); 642 CFReleaseNull(error); 643 return number; 644 } 645 646 647 void SecDBManagementTasks(SecDbConnectionRef dbconn) 648 { 649 int64_t page_count = SecDBGetInteger(dbconn, CFSTR("pragma page_count")); 650 if (page_count <= 0) { 651 return; 652 } 653 int64_t free_count = SecDBGetInteger(dbconn, CFSTR("pragma freelist_count")); 654 if (free_count < 0) { 655 return; 656 } 657 658 int64_t max_free = 8192; 659 660 int64_t pages_in_use = page_count - free_count; 661 double loadFactor = ((double)pages_in_use/(double)page_count); 662 if (0.85 < loadFactor && free_count < max_free) { 663 /* no work yet */ 664 } else { 665 int64_t pages_to_free = (int64_t)(0.2 * free_count); 666 if (0.4 > loadFactor) { 667 pages_to_free = free_count; 668 } 669 670 char *formatString = NULL; 671 asprintf(&formatString, "pragma incremental_vacuum(%d)", (int)pages_to_free); 672 if (formatString) { 673 char *sqlerror = NULL; 674 int rc = sqlite3_exec(dbconn->handle, formatString, NULL, NULL, &sqlerror); 675 if (rc) { 676 secerror("incremental_vacuum failed with: (%d) %{public}s", rc, sqlerror); 677 } 678 sqlite3_free(sqlerror); 679 free(formatString); 680 } 681 } 682 } 683 684 685 static bool SecDbBeginTransaction(SecDbConnectionRef dbconn, SecDbTransactionType type, CFErrorRef *error) 686 { 687 bool ok = true; 688 CFStringRef query; 689 switch (type) { 690 case kSecDbImmediateTransactionType: 691 secdebug("db", "SecDbBeginTransaction SecDbBeginTransaction %p", dbconn); 692 query = CFSTR("BEGIN IMMEDIATE"); 693 break; 694 case kSecDbExclusiveRemoteSOSTransactionType: 695 secdebug("db", "SecDbBeginTransaction kSecDbExclusiveRemoteSOSTransactionType %p", dbconn); 696 dbconn->source = kSecDbSOSTransaction; 697 query = CFSTR("BEGIN EXCLUSIVE"); 698 break; 699 case kSecDbExclusiveRemoteCKKSTransactionType: 700 secdebug("db", "SecDbBeginTransaction kSecDbExclusiveRemoteCKKSTransactionType %p", dbconn); 701 dbconn->source = kSecDbCKKSTransaction; 702 query = CFSTR("BEGIN EXCLUSIVE"); 703 break; 704 case kSecDbExclusiveTransactionType: 705 if (type==kSecDbExclusiveTransactionType) 706 secdebug("db", "SecDbBeginTransaction kSecDbExclusiveTransactionType %p", dbconn); 707 query = CFSTR("BEGIN EXCLUSIVE"); 708 break; 709 case kSecDbNormalTransactionType: 710 secdebug("db", "SecDbBeginTransaction kSecDbNormalTransactionType %p", dbconn); 711 query = CFSTR("BEGIN"); 712 break; 713 default: 714 secdebug("db", "SecDbBeginTransaction invalid transaction type %lu", type); 715 ok = SecDbError(SQLITE_ERROR, error, CFSTR("invalid transaction type %d"), (int)type); 716 query = NULL; 717 break; 718 } 719 720 if (query != NULL && sqlite3_get_autocommit(dbconn->handle) != 0) { 721 ok = SecDbExec(dbconn, query, error); 722 } 723 if (ok) 724 dbconn->inTransaction = true; 725 726 return ok; 727 } 728 729 static bool SecDbEndTransaction(SecDbConnectionRef dbconn, bool commit, CFErrorRef *error) 730 { 731 __block bool ok = true; 732 __block bool commited = false; 733 734 dispatch_block_t notifyAndExec = ^{ 735 if (commit) { 736 //secdebug("db", "SecDbEndTransaction kSecDbTransactionWillCommit %p", dbconn); 737 SecDbNotifyPhase(dbconn, kSecDbTransactionWillCommit); 738 commited = ok = SecDbExec(dbconn, CFSTR("END"), error); 739 //secdebug("db", "SecDbEndTransaction kSecDbTransactionWillCommit %p (after notify)", dbconn); 740 } else { 741 ok = SecDbExec(dbconn, CFSTR("ROLLBACK"), error); 742 commited = false; 743 } 744 dbconn->inTransaction = false; 745 SecDbNotifyPhase(dbconn, commited ? kSecDbTransactionDidCommit : kSecDbTransactionDidRollback); 746 secdebug("db", "SecDbEndTransaction %s %p", commited ? "kSecDbTransactionDidCommit" : "kSecDbTransactionDidRollback", dbconn); 747 dbconn->source = kSecDbAPITransaction; 748 749 if (commit && dbconn->db->useRobotVacuum) { 750 SecDBManagementTasks(dbconn); 751 } 752 }; 753 754 SecDbPerformOnCommitQueue(dbconn, true, notifyAndExec); 755 756 return ok; 757 } 758 759 bool SecDbTransaction(SecDbConnectionRef dbconn, SecDbTransactionType type, 760 CFErrorRef *error, void (^transaction)(bool *commit)) 761 { 762 bool ok = true; 763 bool commit = true; 764 765 if (dbconn->inTransaction) { 766 transaction(&commit); 767 if (!commit) { 768 secinfo("#SecDB", "#SecDB nested transaction asked to not be committed"); 769 } 770 } else { 771 ok = SecDbBeginTransaction(dbconn, type, error); 772 if (ok) { 773 transaction(&commit); 774 ok = SecDbEndTransaction(dbconn, commit, error); 775 } 776 } 777 778 return ok && commit; 779 } 780 781 sqlite3 *SecDbHandle(SecDbConnectionRef dbconn) { 782 return dbconn->handle; 783 } 784 785 bool SecDbStep(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error, void (^row)(bool *stop)) { 786 for (;;) { 787 switch (_SecDbStep(dbconn, stmt, error)) { 788 case kSecDbErrorStep: 789 secdebug("db", "kSecDbErrorStep %@", error ? *error : NULL); 790 return false; 791 case kSecDbRowStep: 792 #if SECDB_DEBUGGING 793 secdebug("db", "kSecDbRowStep %@", error ? *error : NULL); 794 #endif 795 if (row) { 796 __block bool stop = false; 797 SecAutoreleaseInvokeWithPool(^{ 798 row(&stop); 799 }); 800 if (stop) 801 return true; 802 break; 803 } 804 SecDbError(SQLITE_ERROR, error, CFSTR("SecDbStep SQLITE_ROW returned without a row handler")); 805 return false; 806 case kSecDbDoneStep: 807 #if SECDB_DEBUGGING 808 secdebug("db", "kSecDbDoneStep %@", error ? *error : NULL); 809 #endif 810 return true; 811 } 812 } 813 } 814 815 bool SecDbCheckpoint(SecDbConnectionRef dbconn, CFErrorRef *error) 816 { 817 return SecDbConnectionCheckCode(dbconn, 818 sqlite3_wal_checkpoint_v2(dbconn->handle, NULL, SQLITE_CHECKPOINT_FULL, NULL, NULL), 819 error, 820 CFSTR("wal_checkpoint(FULL)")); 821 } 822 823 static sqlite3 *_SecDbOpenV2(const char *path, 824 int flags, 825 int useWAL, 826 int useRobotVacuum, 827 CFErrorRef *error) { 828 sqlite3 *handle = NULL; 829 int s3e = sqlite3_open_v2(path, &handle, flags, NULL); 830 if (s3e) { 831 if (handle) { 832 SecDbErrorWithDb(s3e, handle, error, CFSTR("open_v2 \"%s\" 0x%X"), path, flags); 833 sqlite3_close(handle); 834 handle = NULL; 835 } else { 836 SecDbError(s3e, error, CFSTR("open_v2 \"%s\" 0x%X"), path, flags); 837 } 838 } else if (SQLITE_OPEN_READWRITE == (flags & SQLITE_OPEN_READWRITE)) { 839 if (useRobotVacuum) { 840 #define SECDB_SQLITE_AUTO_VACUUM_INCREMENTAL 2 841 sqlite3_stmt *stmt = NULL; 842 int vacuumMode = -1; 843 844 /* 845 * Setting auto_vacuum = incremental on a database that is not empty requires 846 * a VACCUUM, so check if the vacuum mode is not INCREMENTAL, and if its not, 847 * set it to incremental and vacuum. 848 */ 849 850 s3e = sqlite3_prepare_v2(handle, "PRAGMA auto_vacuum", -1, &stmt, NULL); 851 if (s3e == 0) { 852 s3e = sqlite3_step(stmt); 853 if (s3e == SQLITE_ROW) { 854 vacuumMode = sqlite3_column_int(stmt, 0); 855 } 856 (void)sqlite3_finalize(stmt); 857 } 858 859 if (vacuumMode != SECDB_SQLITE_AUTO_VACUUM_INCREMENTAL) { 860 (void)sqlite3_exec(handle, "PRAGMA auto_vacuum = incremental", NULL, NULL, NULL); 861 (void)sqlite3_exec(handle, "VACUUM", NULL, NULL, NULL); 862 } 863 } 864 if (useWAL) { 865 (void)sqlite3_exec(handle, "PRAGMA journal_mode = WAL", NULL, NULL, NULL); 866 } 867 868 // Let SQLite handle timeouts. 869 sqlite3_busy_timeout(handle, 5*1000); 870 } 871 return handle; 872 } 873 874 static bool SecDbOpenV2(SecDbConnectionRef dbconn, const char *path, int flags, CFErrorRef *error) { 875 return (dbconn->handle = _SecDbOpenV2(path, flags, dbconn->db->useWAL, dbconn->db->useRobotVacuum, error)) != NULL; 876 } 877 878 // This construction lets tests not exit here 879 static void SecDbProductionCorruptionExitHandler(void) 880 { 881 exit(EXIT_FAILURE); 882 } 883 void (*SecDbCorruptionExitHandler)(void) = SecDbProductionCorruptionExitHandler; 884 885 void SecDbResetCorruptionExitHandler(void) 886 { 887 SecDbCorruptionExitHandler = SecDbProductionCorruptionExitHandler; 888 } 889 890 /* 891 There's not much to do in here because we should only ever be here when 892 SQLite tells us the DB is corrupt, or the DB is unrecoverable because of 893 some fatal logic problem. But we can't shoot it dead either due to client 894 connections. So, first we create a marker to tell ourselves things are bad, 895 then we'll die. When we come back up we'll notice the marker and remove the DB. 896 */ 897 static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn, int rc, CFErrorRef *error) 898 { 899 if (!dbconn->db->allowRepair) { 900 SecCFCreateErrorWithFormat(rc, kSecErrnoDomain, NULL, error, NULL, 901 CFSTR("SecDbHandleCorrupt not allowed to repair, handled error: [%d] %s"), rc, strerror(rc)); 902 dbconn->isCorrupted = false; 903 return false; 904 } 905 906 CFStringPerformWithCString(dbconn->db->db_path, ^(const char *db_path) { 907 char marker[PATH_MAX+1]; 908 snprintf(marker, sizeof(marker), "%s-iscorrupt", db_path); 909 struct stat info = {}; 910 if (0 == stat(marker, &info)) { 911 secerror("SecDbHandleCorrupt: Tried to write corruption marker %s but one already exists", marker); 912 } 913 914 FILE* file = fopen(marker, "w"); 915 if (file == NULL) { 916 secerror("SecDbHandleCorrupt: Unable (%{darwin.errno}d) to create corruption marker %{public}s", errno, marker); 917 } else { 918 fclose(file); 919 } 920 }); 921 922 secwarning("SecDbHandleCorrupt: killing self so that successor might cleanly delete corrupt db"); 923 924 // Call through function pointer so tests can replace it and call a SecKeychainDbReset instead 925 SecDbCorruptionExitHandler(); 926 return true; 927 } 928 929 static bool SecDbProcessCorruptionMarker(CFStringRef db_path) { 930 __block bool ok = true; 931 CFStringPerformWithCString(db_path, ^(const char *db_path) { 932 char marker[PATH_MAX+1]; 933 snprintf(marker, sizeof(marker), "%s-iscorrupt", db_path); 934 struct stat info = {}; 935 int result = stat(marker, &info); 936 if (result != 0 && errno == ENOENT) { 937 return; 938 } else if (result != 0) { 939 secerror("SecDbSecDbProcessCorruptionMarker: Unable to check for corruption marker: %{darwin.errno}d", errno); 940 return; 941 } 942 943 secwarning("SecDbSecDbProcessCorruptionMarker: found corruption marker %s", marker); 944 if (remove(marker)) { 945 secerror("SecDbSecDbProcessCorruptionMarker: Unable (%{darwin.errno}d) to delete corruption marker", errno); 946 ok = false; 947 } else if (remove(db_path) && errno != ENOENT) { // Not sure how we'd get ENOENT but it would suit us just fine 948 secerror("SecDbSecDbProcessCorruptionMarker: Unable (%{darwin.errno}d) to delete db %{public}s", errno, db_path); 949 ok = false; 950 } else { 951 secwarning("SecDbSecDbProcessCorruptionMarker: deleted corrupt db %{public}s", db_path); 952 } 953 }); 954 return ok; 955 } 956 957 void 958 SecDbSetCorruptionReset(SecDbRef db, void (^corruptionReset)(void)) 959 { 960 if (db->corruptionReset) { 961 Block_release(db->corruptionReset); 962 db->corruptionReset = NULL; 963 } 964 if (corruptionReset) { 965 db->corruptionReset = Block_copy(corruptionReset); 966 } 967 } 968 969 static bool SecDbLoggingEnabled(CFStringRef type) 970 { 971 CFTypeRef profile = NULL; 972 bool enabled = false; 973 974 if (csr_check(CSR_ALLOW_APPLE_INTERNAL) != 0) 975 return false; 976 977 profile = (CFNumberRef)CFPreferencesCopyValue(CFSTR("SQLProfile"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesAnyHost); 978 979 if (profile == NULL) 980 return false; 981 982 if (CFGetTypeID(profile) == CFBooleanGetTypeID()) { 983 enabled = CFBooleanGetValue((CFBooleanRef)profile); 984 } else if (CFGetTypeID(profile) == CFNumberGetTypeID()) { 985 int32_t num = 0; 986 CFNumberGetValue(profile, kCFNumberSInt32Type, &num); 987 enabled = !!num; 988 } 989 990 CFReleaseSafe(profile); 991 992 return enabled; 993 } 994 995 static unsigned 996 SecDbProfileMask(void) 997 { 998 static dispatch_once_t onceToken; 999 static unsigned profile_mask = 0; 1000 1001 // sudo defaults write /Library/Preferences/com.apple.security SQLProfile -bool true 1002 dispatch_once(&onceToken, ^{ 1003 if (SecDbLoggingEnabled(CFSTR("SQLProfile"))) 1004 profile_mask = SQLITE_TRACE_PROFILE; 1005 #if DEBUG 1006 profile_mask |= SQLITE_TRACE_STMT; 1007 #else 1008 if (SecDbLoggingEnabled(CFSTR("SQLTrace"))) 1009 profile_mask = SQLITE_TRACE_STMT; 1010 #endif 1011 if (SecDbLoggingEnabled(CFSTR("SQLRow"))) 1012 profile_mask = SQLITE_TRACE_ROW; 1013 secinfo("#SecDB", "sqlDb: sql trace mask: 0x%08x", profile_mask); 1014 }); 1015 return profile_mask; 1016 } 1017 1018 static int 1019 SecDbTraceV2(unsigned mask, void *ctx, void *p, void *x) { 1020 SecDbConnectionRef dbconn __unused = ctx; 1021 1022 #if SECDB_DEBUGGING 1023 const char *trace = "unknown"; 1024 char *tofree = NULL; 1025 1026 if (mask == SQLITE_TRACE_PROFILE) 1027 trace = sqlite3_sql(p); 1028 else if (mask == SQLITE_TRACE_STMT) { 1029 trace = sqlite3_sql(p); 1030 } else if (mask == SQLITE_TRACE_ROW) { 1031 trace = tofree = sqlite3_expanded_sql(p); 1032 } 1033 1034 secinfo("#SecDB", "#SecDB %{public}s", trace); 1035 1036 sqlite3_free(tofree); 1037 #endif 1038 1039 return 0; 1040 } 1041 1042 1043 static bool SecDbOpenHandle(SecDbConnectionRef dbconn, bool *created, CFErrorRef *error) 1044 { 1045 __block bool ok = true; 1046 1047 // This is pretty terrible because now what? We know we have a corrupt DB 1048 // and now we can't get rid of it. 1049 if (!SecDbProcessCorruptionMarker(dbconn->db->db_path)) { 1050 SecCFCreateErrorWithFormat(errno, kSecErrnoDomain, NULL, error, NULL, CFSTR("Unable to process corruption marker: %{darwin.errno}d"), errno); 1051 return false; 1052 } 1053 1054 CFStringPerformWithCString(dbconn->db->db_path, ^(const char *db_path) { 1055 #if TARGET_OS_IPHONE 1056 int flags = SQLITE_OPEN_FILEPROTECTION_NONE; 1057 #else 1058 int flags = 0; 1059 #endif 1060 flags |= (dbconn->db->readWrite) ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; 1061 ok = created && SecDbOpenV2(dbconn, db_path, flags, NULL); 1062 if (!ok) { 1063 ok = true; 1064 if (created) { 1065 char *tmp = dirname((char *)db_path); 1066 if (tmp) { 1067 mode_t omode = dbconn->db->mode; 1068 if (omode & S_IRUSR) { omode |= S_IXUSR; } // owner can read 1069 if (omode & S_IRGRP) { omode |= S_IXGRP; } // group can read 1070 if (omode & S_IROTH) { omode |= S_IXOTH; } // other can read 1071 int errnum = mkpath_np(tmp, omode); 1072 if (errnum != 0 && errnum != EEXIST) { 1073 SecCFCreateErrorWithFormat(errnum, kSecErrnoDomain, NULL, error, NULL, 1074 CFSTR("mkpath_np %s: [%d] %s"), tmp, errnum, strerror(errnum)); 1075 ok = false; 1076 } 1077 } 1078 } 1079 // if the enclosing directory is ok, try to create the database. 1080 // this forces us to open it read-write, so we'll need to be the owner here. 1081 flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; 1082 #if TARGET_OS_IPHONE 1083 flags |= SQLITE_OPEN_FILEPROTECTION_NONE; 1084 #endif 1085 ok = ok && SecDbOpenV2(dbconn, db_path, flags, error); 1086 if (ok) { 1087 chmod(db_path, dbconn->db->mode); // default: 0600 (S_IRUSR | S_IWUSR) 1088 if (created) 1089 *created = true; 1090 } 1091 } 1092 1093 if (ok) { 1094 unsigned mask = SecDbProfileMask(); 1095 if (mask) { 1096 (void)sqlite3_trace_v2(dbconn->handle, 1097 mask, 1098 SecDbTraceV2, 1099 dbconn); 1100 } 1101 } 1102 }); 1103 1104 return ok; 1105 } 1106 1107 static SecDbConnectionRef 1108 SecDbConnectionCreate(SecDbRef db, bool readOnly, CFErrorRef *error) 1109 { 1110 SecDbConnectionRef dbconn = NULL; 1111 1112 dbconn = CFTypeAllocate(SecDbConnection, struct __OpaqueSecDbConnection, kCFAllocatorDefault); 1113 require(dbconn != NULL, done); 1114 1115 dbconn->db = db; 1116 dbconn->readOnly = readOnly; 1117 dbconn->inTransaction = false; 1118 dbconn->source = kSecDbInvalidTransaction; 1119 dbconn->isCorrupted = false; 1120 dbconn->maybeCorruptedCode = 0; 1121 dbconn->hasIOFailure = false; 1122 dbconn->corruptionError = NULL; 1123 dbconn->handle = NULL; 1124 dbconn->changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 1125 1126 done: 1127 return dbconn; 1128 } 1129 1130 bool SecDbConnectionIsReadOnly(SecDbConnectionRef dbconn) { 1131 return dbconn->readOnly; 1132 } 1133 1134 static void SecDbConectionSetReadOnly(SecDbConnectionRef dbconn, bool readOnly) { 1135 dbconn->readOnly = readOnly; 1136 } 1137 1138 SecDbConnectionRef SecDbConnectionAcquire(SecDbRef db, bool readOnly, CFErrorRef *error) { 1139 SecDbConnectionRef dbconn = NULL; 1140 SecDbConnectionAcquireRefMigrationSafe(db, readOnly, &dbconn, error); 1141 return dbconn; 1142 } 1143 1144 static void SecDbConnectionConsumeResource(SecDbRef db, bool readOnly) { 1145 if (readOnly) { 1146 dispatch_semaphore_wait(db->readSemaphore, DISPATCH_TIME_FOREVER); 1147 } else { 1148 pthread_mutex_lock(&(db->writeMutex)); 1149 } 1150 } 1151 1152 static void SecDbConnectionMakeResourceAvailable(SecDbRef db, bool readOnly) { 1153 if (readOnly) { 1154 dispatch_semaphore_signal(db->readSemaphore); 1155 } else { 1156 pthread_mutex_unlock(&(db->writeMutex)); 1157 } 1158 } 1159 1160 bool SecDbConnectionAcquireRefMigrationSafe(SecDbRef db, bool readOnly, SecDbConnectionRef* dbconnRef, CFErrorRef *error) 1161 { 1162 CFRetain(db); 1163 #if SECDB_DEBUGGING 1164 secinfo("dbconn", "acquire %s connection", readOnly ? "ro" : "rw"); 1165 #endif 1166 SecDbConnectionConsumeResource(db, readOnly); 1167 1168 __block SecDbConnectionRef dbconn = NULL; 1169 __block bool ok = true; 1170 __block bool ranOpenedHandler = false; 1171 1172 bool (^assignDbConn)(SecDbConnectionRef) = ^bool(SecDbConnectionRef connection) { 1173 dbconn = connection; 1174 if (dbconnRef) { 1175 *dbconnRef = connection; 1176 } 1177 1178 return dbconn != NULL; 1179 }; 1180 1181 dispatch_sync(db->queue, ^{ 1182 if (!db->didFirstOpen) { 1183 bool didCreate = false; 1184 ok = assignDbConn(SecDbConnectionCreate(db, false, error)); 1185 CFErrorRef localError = NULL; 1186 if (ok && !SecDbOpenHandle(dbconn, &didCreate, &localError)) { 1187 secerror("Unable to create database: %@", localError); 1188 if (localError && CFEqual(CFErrorGetDomain(localError), kSecDbErrorDomain)) { 1189 int code = (int)CFErrorGetCode(localError); 1190 dbconn->isCorrupted = (SQLITE_CORRUPT == code) || (SQLITE_NOTADB == code); 1191 } 1192 // If the open failure isn't due to corruption, propagate the error. 1193 ok = dbconn->isCorrupted; 1194 if (!ok && error && *error == NULL) { 1195 *error = localError; 1196 localError = NULL; 1197 } 1198 } 1199 CFReleaseNull(localError); 1200 1201 if (ok) { 1202 db->didFirstOpen = ok = SecDbDidCreateFirstConnection(dbconn, didCreate, error); 1203 ranOpenedHandler = true; 1204 SecDbConectionSetReadOnly(dbconn, readOnly); // first connection always created "rw", so set to real value 1205 } else { 1206 CFReleaseNull(dbconn); 1207 } 1208 } else { 1209 /* Try to get one from the cache */ 1210 CFMutableArrayRef cache = readOnly ? db->idleReadConnections : db->idleWriteConnections; 1211 if (CFArrayGetCount(cache) && !dbconn) { 1212 if (assignDbConn((SecDbConnectionRef)CFArrayGetValueAtIndex(cache, 0))) { 1213 CFRetainSafe(dbconn); 1214 } 1215 CFArrayRemoveValueAtIndex(cache, 0); 1216 } 1217 } 1218 }); 1219 1220 if (ok && !dbconn) { 1221 /* Nothing found in cache, create a new connection */ 1222 bool created = false; 1223 if (assignDbConn(SecDbConnectionCreate(db, readOnly, error)) && !SecDbOpenHandle(dbconn, &created, error)) { 1224 CFReleaseNull(dbconn); 1225 } 1226 } 1227 1228 if (dbconn && !ranOpenedHandler && dbconn->db->opened) { 1229 dispatch_sync(db->queue, ^{ 1230 if (dbconn->db->callOpenedHandlerForNextConnection) { 1231 dbconn->db->callOpenedHandlerForNextConnection = false; 1232 if (!dbconn->db->opened(db, dbconn, false, &dbconn->db->callOpenedHandlerForNextConnection, error)) { 1233 if (!dbconn->isCorrupted || !SecDbHandleCorrupt(dbconn, 0, error)) { 1234 CFReleaseNull(dbconn); 1235 } 1236 } 1237 } 1238 }); 1239 } 1240 1241 if (dbconnRef) { 1242 *dbconnRef = dbconn; 1243 } 1244 1245 if (!dbconn) { 1246 // Caller doesn't get (to use) a connection so the backing synchronization primitive is available again 1247 SecDbConnectionMakeResourceAvailable(db, readOnly); 1248 CFRelease(db); 1249 } 1250 1251 return dbconn ? true : false; 1252 } 1253 1254 void SecDbConnectionRelease(SecDbConnectionRef dbconn) { 1255 if (!dbconn) { 1256 secerror("SecDbConnectionRelease called with NULL dbconn"); 1257 return; 1258 } 1259 SecDbRef db = dbconn->db; 1260 #if SECDB_DEBUGGING 1261 secinfo("dbconn", "release %@", dbconn); 1262 #endif 1263 1264 bool readOnly = SecDbConnectionIsReadOnly(dbconn); 1265 dispatch_sync(db->queue, ^{ 1266 if (dbconn->hasIOFailure) { 1267 // Something wrong on the file layer (e.g. revoked file descriptor for networked home) 1268 secwarning("SecDbConnectionRelease: IO failure reported in connection, throwing away currently idle caches"); 1269 // Any other checked-out connections are beyond our grasp. If they did not have IO failures they'll come back, 1270 // otherwise this branch gets taken more than once and gradually those connections die off 1271 CFArrayRemoveAllValues(db->idleWriteConnections); 1272 CFArrayRemoveAllValues(db->idleReadConnections); 1273 } else { 1274 CFMutableArrayRef cache = readOnly ? db->idleReadConnections : db->idleWriteConnections; 1275 CFIndex count = CFArrayGetCount(cache); 1276 if ((unsigned long)count < (readOnly ? kSecDbMaxReaders : kSecDbMaxWriters)) { 1277 CFArrayAppendValue(cache, dbconn); 1278 } else { 1279 secerror("dbconn: did not expect to run out of room in the %s cache when releasing connection", readOnly ? "ro" : "rw"); 1280 } 1281 } 1282 }); 1283 1284 // Signal after we have put the connection back in the pool of connections 1285 SecDbConnectionMakeResourceAvailable(db, readOnly); 1286 CFRelease(dbconn); 1287 CFRelease(db); 1288 } 1289 1290 void SecDbReleaseAllConnections(SecDbRef db) { 1291 // Force all connections to be removed (e.g. file descriptor no longer valid) 1292 if (!db) { 1293 secerror("called with NULL db"); 1294 return; 1295 } 1296 dispatch_sync(db->queue, ^{ 1297 CFArrayRemoveAllValues(db->idleReadConnections); 1298 CFArrayRemoveAllValues(db->idleWriteConnections); 1299 }); 1300 } 1301 1302 static void onQueueSecDbForceCloseForCache(CFMutableArrayRef cache) { 1303 CFArrayForEach(cache, ^(const void* ptr) { 1304 SecDbConnectionRef connection = (SecDbConnectionRef)ptr; 1305 1306 // this pointer is claimed to be nonretained 1307 connection->db = NULL; 1308 1309 if(connection->handle) { 1310 sqlite3_close(connection->handle); 1311 connection->handle = NULL; 1312 } 1313 }); 1314 CFArrayRemoveAllValues(cache); 1315 } 1316 1317 // Please make sure you want to do this. Any use of the outstanding connections to this DB will cause a crash. 1318 void SecDbForceClose(SecDbRef db) { 1319 dispatch_sync(db->queue, ^{ 1320 onQueueSecDbForceCloseForCache(db->idleReadConnections); 1321 onQueueSecDbForceCloseForCache(db->idleWriteConnections); 1322 }); 1323 } 1324 1325 bool SecDbPerformRead(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) { 1326 SecDbConnectionRef dbconn = SecDbConnectionAcquire(db, true, error); 1327 bool success = false; 1328 if (dbconn) { 1329 perform(dbconn); 1330 success = true; 1331 SecDbConnectionRelease(dbconn); 1332 } 1333 return success; 1334 } 1335 1336 bool SecDbPerformWrite(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) { 1337 if(!db) { 1338 SecError(errSecNotAvailable, error, CFSTR("failed to get a db handle")); 1339 return false; 1340 } 1341 SecDbConnectionRef dbconn = SecDbConnectionAcquire(db, false, error); 1342 bool success = false; 1343 if (dbconn) { 1344 perform(dbconn); 1345 success = true; 1346 SecDbConnectionRelease(dbconn); 1347 } 1348 return success; 1349 } 1350 1351 static CFStringRef 1352 SecDbConnectionCopyFormatDescription(CFTypeRef value, CFDictionaryRef formatOptions) 1353 { 1354 SecDbConnectionRef dbconn = (SecDbConnectionRef)value; 1355 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecDbConnection %s %s>"), 1356 dbconn->readOnly ? "ro" : "rw", dbconn->handle ? "open" : "closed"); 1357 } 1358 1359 static void 1360 SecDbConnectionDestroy(CFTypeRef value) 1361 { 1362 SecDbConnectionRef dbconn = (SecDbConnectionRef)value; 1363 if (dbconn->handle) { 1364 int s3e = sqlite3_close(dbconn->handle); 1365 if (s3e != SQLITE_OK) { 1366 secerror("failed to close database connection (%d) for %@: %s", s3e, dbconn->db->db_path, sqlite3_errmsg(dbconn->handle)); 1367 } 1368 os_assert(s3e == SQLITE_OK); // Crash now or jetsam later 1369 } 1370 dbconn->db = NULL; 1371 CFReleaseNull(dbconn->changes); 1372 CFReleaseNull(dbconn->corruptionError); 1373 1374 } 1375 1376 void SecDbPerformOnCommitQueue(SecDbConnectionRef dbconn, bool barrier, dispatch_block_t perform) { 1377 if (barrier) { 1378 dispatch_barrier_sync(dbconn->db->commitQueue, ^{ 1379 perform(); 1380 }); 1381 } else { 1382 dispatch_sync(dbconn->db->commitQueue, ^{ 1383 perform(); 1384 }); 1385 } 1386 } 1387 1388 // MARK: - 1389 // MARK: Bind helpers 1390 1391 // Logging binds is very spammy when debug logging is on (~90% of log lines), and isn't often useful. 1392 // Enable this in your local build if you actually want every single SQL variable bind logged for debugging. 1393 #define LOG_SECDB_BINDS 0 1394 1395 bool SecDbBindBlob(sqlite3_stmt *stmt, int param, const void *zData, size_t n, void(*xDel)(void*), CFErrorRef *error) { 1396 if (n > INT_MAX) { 1397 return SecDbErrorWithStmt(SQLITE_TOOBIG, stmt, error, 1398 CFSTR("bind_blob[%d]: blob bigger than INT_MAX"), param); 1399 } 1400 bool ok = SecDbErrorWithStmt(sqlite3_bind_blob(stmt, param, zData, (int)n, xDel), 1401 stmt, error, CFSTR("bind_blob[%d]"), param); 1402 #if LOG_SECDB_BINDS 1403 secinfo("bind", "bind_blob[%d]: %.*P: %@", param, (int)n, zData, error ? *error : NULL); 1404 #endif 1405 return ok; 1406 } 1407 1408 bool SecDbBindText(sqlite3_stmt *stmt, int param, const char *zData, size_t n, void(*xDel)(void*), CFErrorRef *error) { 1409 if (n > INT_MAX) { 1410 return SecDbErrorWithStmt(SQLITE_TOOBIG, stmt, error, 1411 CFSTR("bind_text[%d]: text bigger than INT_MAX"), param); 1412 } 1413 bool ok = SecDbErrorWithStmt(sqlite3_bind_text(stmt, param, zData, (int)n, xDel), stmt, error, 1414 CFSTR("bind_text[%d]"), param); 1415 #if LOG_SECDB_BINDS 1416 secinfo("bind", "bind_text[%d]: \"%s\" error: %@", param, zData, error ? *error : NULL); 1417 #endif 1418 return ok; 1419 } 1420 1421 bool SecDbBindDouble(sqlite3_stmt *stmt, int param, double value, CFErrorRef *error) { 1422 bool ok = SecDbErrorWithStmt(sqlite3_bind_double(stmt, param, value), stmt, error, 1423 CFSTR("bind_double[%d]"), param); 1424 #if LOG_SECDB_BINDS 1425 secinfo("bind", "bind_double[%d]: %f error: %@", param, value, error ? *error : NULL); 1426 #endif 1427 return ok; 1428 } 1429 1430 bool SecDbBindInt(sqlite3_stmt *stmt, int param, int value, CFErrorRef *error) { 1431 bool ok = SecDbErrorWithStmt(sqlite3_bind_int(stmt, param, value), stmt, error, 1432 CFSTR("bind_int[%d]"), param); 1433 #if LOG_SECDB_BINDS 1434 secinfo("bind", "bind_int[%d]: %d error: %@", param, value, error ? *error : NULL); 1435 #endif 1436 return ok; 1437 } 1438 1439 bool SecDbBindInt64(sqlite3_stmt *stmt, int param, sqlite3_int64 value, CFErrorRef *error) { 1440 bool ok = SecDbErrorWithStmt(sqlite3_bind_int64(stmt, param, value), stmt, error, 1441 CFSTR("bind_int64[%d]"), param); 1442 #if LOG_SECDB_BINDS 1443 secinfo("bind", "bind_int64[%d]: %lld error: %@", param, value, error ? *error : NULL); 1444 #endif 1445 return ok; 1446 } 1447 1448 1449 /* AUDIT[securityd](done): 1450 value (ok) is a caller provided, non NULL CFTypeRef. 1451 */ 1452 bool SecDbBindObject(sqlite3_stmt *stmt, int param, CFTypeRef value, CFErrorRef *error) { 1453 CFTypeID valueId; 1454 __block bool result = false; 1455 1456 /* TODO: Can we use SQLITE_STATIC below everwhere we currently use 1457 SQLITE_TRANSIENT since we finalize the statement before the value 1458 goes out of scope? */ 1459 if (!value || (valueId = CFGetTypeID(value)) == CFNullGetTypeID()) { 1460 /* Skip bindings for NULL values. sqlite3 will interpret unbound 1461 params as NULL which is exactly what we want. */ 1462 result = true; 1463 } else if (valueId == CFStringGetTypeID()) { 1464 CFStringPerformWithCStringAndLength(value, ^(const char *cstr, size_t clen) { 1465 result = SecDbBindText(stmt, param, cstr, clen, SQLITE_TRANSIENT, error); 1466 }); 1467 } else if (valueId == CFDataGetTypeID()) { 1468 CFIndex len = CFDataGetLength(value); 1469 if (len) { 1470 result = SecDbBindBlob(stmt, param, CFDataGetBytePtr(value), 1471 len, SQLITE_TRANSIENT, error); 1472 } else { 1473 result = SecDbBindText(stmt, param, "", 0, SQLITE_TRANSIENT, error); 1474 } 1475 } else if (valueId == CFDateGetTypeID()) { 1476 CFAbsoluteTime abs_time = CFDateGetAbsoluteTime(value); 1477 result = SecDbBindDouble(stmt, param, abs_time, error); 1478 } else if (valueId == CFBooleanGetTypeID()) { 1479 int bval = CFBooleanGetValue(value); 1480 result = SecDbBindInt(stmt, param, bval, error); 1481 } else if (valueId == CFNumberGetTypeID()) { 1482 Boolean convertOk; 1483 if (CFNumberIsFloatType(value)) { 1484 double nval; 1485 convertOk = CFNumberGetValue(value, kCFNumberDoubleType, &nval); 1486 result = SecDbBindDouble(stmt, param, nval, error); 1487 } else { 1488 sqlite_int64 nval64; 1489 convertOk = CFNumberGetValue(value, kCFNumberSInt64Type, &nval64); 1490 if (convertOk) { 1491 result = SecDbBindInt64(stmt, param, nval64, error); 1492 } 1493 } 1494 if (!convertOk) { 1495 result = SecDbError(SQLITE_INTERNAL, error, CFSTR("bind CFNumberGetValue failed for %@"), value); 1496 } 1497 } else { 1498 if (error) { 1499 CFStringRef valueDesc = CFCopyTypeIDDescription(valueId); 1500 SecDbError(SQLITE_MISMATCH, error, CFSTR("bind unsupported type %@"), valueDesc); 1501 CFReleaseSafe(valueDesc); 1502 } 1503 } 1504 1505 return result; 1506 } 1507 1508 // MARK: - 1509 // MARK: SecDbStatementRef 1510 1511 bool SecDbReset(sqlite3_stmt *stmt, CFErrorRef *error) { 1512 return SecDbErrorWithStmt(sqlite3_reset(stmt), stmt, error, CFSTR("reset")); 1513 } 1514 1515 bool SecDbClearBindings(sqlite3_stmt *stmt, CFErrorRef *error) { 1516 return SecDbErrorWithStmt(sqlite3_clear_bindings(stmt), stmt, error, CFSTR("clear bindings")); 1517 } 1518 1519 bool SecDbFinalize(sqlite3_stmt *stmt, CFErrorRef *error) { 1520 sqlite3 *handle = sqlite3_db_handle(stmt); 1521 int s3e = sqlite3_finalize(stmt); 1522 return s3e == SQLITE_OK ? true : SecDbErrorWithDb(s3e, handle, error, CFSTR("finalize: %p"), stmt); 1523 } 1524 1525 sqlite3_stmt *SecDbPrepareV2(SecDbConnectionRef dbconn, const char *sql, size_t sqlLen, const char **sqlTail, CFErrorRef *error) { 1526 sqlite3 *db = SecDbHandle(dbconn); 1527 if (sqlLen > INT_MAX) { 1528 SecDbErrorWithDb(SQLITE_TOOBIG, db, error, CFSTR("prepare_v2: sql bigger than INT_MAX")); 1529 return NULL; 1530 } 1531 int ntries = 0; 1532 for (;;) { 1533 sqlite3_stmt *stmt = NULL; 1534 int s3e = sqlite3_prepare_v2(db, sql, (int)sqlLen, &stmt, sqlTail); 1535 if (s3e == SQLITE_OK) 1536 return stmt; 1537 else if (!SecDbWaitIfNeeded(dbconn, s3e, NULL, CFSTR("preparev2"), ntries, error)) 1538 return NULL; 1539 ntries++; 1540 } 1541 } 1542 1543 static sqlite3_stmt *SecDbCopyStatementWithTailRange(SecDbConnectionRef dbconn, CFStringRef sql, CFRange *sqlTail, CFErrorRef *error) { 1544 __block sqlite3_stmt *stmt = NULL; 1545 if (sql) CFStringPerformWithCStringAndLength(sql, ^(const char *sqlStr, size_t sqlLen) { 1546 const char *tail = NULL; 1547 stmt = SecDbPrepareV2(dbconn, sqlStr, sqlLen, &tail, error); 1548 if (sqlTail && sqlStr < tail && tail < sqlStr + sqlLen) { 1549 sqlTail->location = tail - sqlStr; 1550 sqlTail->length = sqlLen - sqlTail->location; 1551 } 1552 }); 1553 1554 return stmt; 1555 } 1556 1557 sqlite3_stmt *SecDbCopyStmt(SecDbConnectionRef dbconn, CFStringRef sql, CFStringRef *tail, CFErrorRef *error) { 1558 // TODO: Add caching and cache lookup of statements 1559 CFRange sqlTail = {}; 1560 sqlite3_stmt *stmt = SecDbCopyStatementWithTailRange(dbconn, sql, &sqlTail, error); 1561 if (sqlTail.length > 0) { 1562 CFStringRef excess = CFStringCreateWithSubstring(CFGetAllocator(sql), sql, sqlTail); 1563 if (tail) { 1564 *tail = excess; 1565 } else { 1566 SecDbError(SQLITE_INTERNAL, error, 1567 CFSTR("prepare_v2: %@ unused sql: %@"), 1568 sql, excess); 1569 CFReleaseSafe(excess); 1570 SecDbFinalize(stmt, error); 1571 stmt = NULL; 1572 } 1573 } 1574 return stmt; 1575 } 1576 1577 /* 1578 TODO: Could do a hack here with a custom kCFAllocatorNULL allocator for a second CFRuntimeBase inside a SecDbStatement, 1579 TODO: Better yet make a full blow SecDbStatement instance whenever SecDbCopyStmt is called. Then, when the statement is released, in the Dispose method, we Reset and ClearBindings the sqlite3_stmt * and hand it back to the SecDb with the original CFStringRef for the sql (or hash thereof) as an argument. */ 1580 bool SecDbReleaseCachedStmt(SecDbConnectionRef dbconn, CFStringRef sql, sqlite3_stmt *stmt, CFErrorRef *error) { 1581 if (stmt) { 1582 return SecDbFinalize(stmt, error); 1583 } 1584 return true; 1585 } 1586 1587 bool SecDbPrepare(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error, void(^exec)(sqlite3_stmt *stmt)) { 1588 assert(sql != NULL); 1589 sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, NULL, error); 1590 if (!stmt) 1591 return false; 1592 1593 exec(stmt); 1594 return SecDbReleaseCachedStmt(dbconn, sql, stmt, error); 1595 } 1596 1597 bool SecDbWithSQL(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error, bool(^perform)(sqlite3_stmt *stmt)) { 1598 bool ok = true; 1599 CFRetain(sql); 1600 while (sql) { 1601 CFStringRef tail = NULL; 1602 if (ok) { 1603 sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, &tail, error); 1604 ok = stmt != NULL; 1605 if (stmt) { 1606 if (perform) { 1607 ok = perform(stmt); 1608 } else { 1609 // TODO: Use a different error scope here. 1610 ok = SecError(-50 /* errSecParam */, error, CFSTR("SecDbWithSQL perform block missing")); 1611 } 1612 ok &= SecDbReleaseCachedStmt(dbconn, sql, stmt, error); 1613 } 1614 } else { 1615 // TODO We already have an error here we really just want the left over sql in it's userData 1616 ok = SecDbError(SQLITE_ERROR, error, CFSTR("Error with unexecuted sql remaining %@"), sql); 1617 } 1618 CFRelease(sql); 1619 sql = tail; 1620 } 1621 return ok; 1622 } 1623 1624 /* SecDbForEach returns true if all SQLITE_ROW returns of sqlite3_step() return true from the row block. 1625 If the row block returns false and doesn't set an error (to indicate it has reached a limit), 1626 this entire function returns false. In that case no error will be set. */ 1627 bool SecDbForEach(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error, bool(^row)(int row_index)) { 1628 bool result = false; 1629 for (int row_ix = 0;;++row_ix) { 1630 if (SecDbConnectionIsReadOnly(dbconn) && !sqlite3_stmt_readonly(stmt)) { 1631 secerror("SecDbForEach: SecDbConnection is readonly but we're about to write: %s", sqlite3_sql(stmt)); 1632 } 1633 int s3e = sqlite3_step(stmt); 1634 if (s3e == SQLITE_ROW) { 1635 if (row) { 1636 if (!row(row_ix)) { 1637 break; 1638 } 1639 } else { 1640 // If we have no row block then getting SQLITE_ROW is an error 1641 SecDbError(s3e, error, 1642 CFSTR("step[%d]: %s returned SQLITE_ROW with NULL row block"), 1643 row_ix, sqlite3_sql(stmt)); 1644 } 1645 } else { 1646 if (s3e == SQLITE_DONE) { 1647 result = true; 1648 } else { 1649 SecDbConnectionCheckCode(dbconn, s3e, error, CFSTR("SecDbForEach step[%d]"), row_ix); 1650 } 1651 break; 1652 } 1653 } 1654 return result; 1655 } 1656 1657 void SecDbRecordChange(SecDbConnectionRef dbconn, CFTypeRef deleted, CFTypeRef inserted) { 1658 if (!dbconn->db->notifyPhase) return; 1659 CFTypeRef entry = SecDbEventCreateWithComponents(deleted, inserted); 1660 if (entry) { 1661 CFArrayAppendValue(dbconn->changes, entry); 1662 CFRelease(entry); 1663 1664 if (!dbconn->inTransaction) { 1665 secerror("db %@ changed outside txn", dbconn); 1666 // Only notify of DidCommit, since WillCommit code assumes 1667 // we are in a txn. 1668 SecDbOnNotify(dbconn, ^{ 1669 SecDbNotifyPhase(dbconn, kSecDbTransactionDidCommit); 1670 }); 1671 } 1672 } 1673 } 1674 1675 1676 CFGiblisFor(SecDbConnection) 1677 1678 // 1679 // SecDbEvent Creation and consumption 1680 // 1681 1682 static SecDbEventRef SecDbEventCreateInsert(CFTypeRef inserted) { 1683 return CFRetainSafe(inserted); 1684 } 1685 1686 static SecDbEventRef SecDbEventCreateDelete(CFTypeRef deleted) { 1687 return CFArrayCreate(kCFAllocatorDefault, &deleted, 1, &kCFTypeArrayCallBacks); 1688 } 1689 1690 static SecDbEventRef SecDbEventCreateUpdate(CFTypeRef deleted, CFTypeRef inserted) { 1691 const void *values[2] = { deleted, inserted }; 1692 return CFArrayCreate(kCFAllocatorDefault, values, 2, &kCFTypeArrayCallBacks); 1693 } 1694 1695 SecDbEventRef SecDbEventCreateWithComponents(CFTypeRef deleted, CFTypeRef inserted) { 1696 if (deleted && inserted) 1697 return SecDbEventCreateUpdate(deleted, inserted); 1698 else if (deleted) 1699 return SecDbEventCreateDelete(deleted); 1700 else if (inserted) 1701 return SecDbEventCreateInsert(inserted); 1702 else 1703 return NULL; 1704 } 1705 1706 void SecDbEventTranslateComponents(SecDbEventRef item, CFTypeRef* deleted, CFTypeRef* inserted) { 1707 if(CFGetTypeID(item) == CFArrayGetTypeID()) { 1708 // One item: deletion. Two: update. 1709 CFIndex arraySize = CFArrayGetCount(item); 1710 if(arraySize == 1) { 1711 if(deleted) { *deleted = CFArrayGetValueAtIndex(item, 0); } 1712 if(inserted) { *inserted = NULL; } 1713 } else if(arraySize == 2) { 1714 if(deleted) { *deleted = CFArrayGetValueAtIndex(item, 0); } 1715 if(inserted) { *inserted = CFArrayGetValueAtIndex(item, 1); } 1716 } else { 1717 if(deleted) { *deleted = NULL; } 1718 if(inserted) { *inserted = NULL; } 1719 } 1720 } else { 1721 if(deleted) { *deleted = NULL; } 1722 if(inserted) { *inserted = item; } 1723 } 1724 1725 } 1726 1727 bool SecDbEventGetComponents(SecDbEventRef event, CFTypeRef *deleted, CFTypeRef *inserted, CFErrorRef *error) { 1728 if (isArray(event)) { 1729 CFArrayRef array = event; 1730 switch (CFArrayGetCount(array)) { 1731 case 2: 1732 *deleted = CFArrayGetValueAtIndex(array, 0); 1733 *inserted = CFArrayGetValueAtIndex(array, 1); 1734 break; 1735 case 1: 1736 *deleted = CFArrayGetValueAtIndex(array, 0); 1737 *inserted = NULL; 1738 break; 1739 default: 1740 SecError(errSecParam, error, NULL, CFSTR("invalid entry in changes array: %@"), array); 1741 break; 1742 } 1743 } else { 1744 *deleted = NULL; 1745 *inserted = event; 1746 } 1747 return true; 1748 }