1// 2// SecDb.c 3// utilities 4// 5// Created by Michael Brouwer on 11/12/12. 6// Copyright (c) 2012-2013 Apple Inc. All rights reserved. 7// 8 9#include "SecDb.h" 10#include "debugging.h" 11 12#include <sqlite3.h> 13#include <sqlite3_private.h> 14#include <CoreFoundation/CoreFoundation.h> 15#include <libgen.h> 16#include <sys/stat.h> 17#include <AssertMacros.h> 18#include "SecCFWrappers.h" 19#include "SecCFError.h" 20#include "SecIOFormat.h" 21#include <stdio.h> 22#include "Security/SecBase.h" 23 24#define LOGE(ARG,...) secerror(ARG, ## __VA_ARGS__) 25#define LOGV(ARG,...) secdebug("secdb", ARG, ## __VA_ARGS__) 26#define LOGD(ARG,...) secdebug("secdb", ARG, ## __VA_ARGS__) 27 28#define HAVE_UNLOCK_NOTIFY 0 29#define USE_BUSY_HANDLER 1 30 31struct __OpaqueSecDbStatement { 32 CFRuntimeBase _base; 33 34 SecDbConnectionRef dbconn; 35 sqlite3_stmt *stmt; 36}; 37 38struct __OpaqueSecDbConnection { 39 CFRuntimeBase _base; 40 41 //CFMutableDictionaryRef statements; 42 43 SecDbRef db; // NONRETAINED, since db or block retains us 44 bool readOnly; 45 bool inTransaction; 46 bool isCorrupted; 47 sqlite3 *handle; 48}; 49 50struct __OpaqueSecDb { 51 CFRuntimeBase _base; 52 53 CFStringRef db_path; 54 dispatch_queue_t queue; 55 CFMutableArrayRef connections; 56 dispatch_semaphore_t write_semaphore; 57 dispatch_semaphore_t read_semaphore; 58 bool didFirstOpen; 59 bool (^opened)(SecDbConnectionRef dbconn, bool did_create, CFErrorRef *error); 60}; 61 62// MARK: Error domains and error helper functions 63 64CFStringRef kSecDbErrorDomain = CFSTR("com.apple.utilities.sqlite3"); 65 66bool SecDbError(int sql_code, CFErrorRef *error, CFStringRef format, ...) { 67 if (sql_code == SQLITE_OK) return true; 68 if (error) { 69 va_list args; 70 CFIndex code = sql_code; 71 CFErrorRef previousError = *error; 72 73 *error = NULL; 74 va_start(args, format); 75 SecCFCreateErrorWithFormatAndArguments(code, kSecDbErrorDomain, previousError, error, NULL, format, args); 76 va_end(args); 77 } 78 return false; 79} 80 81bool SecDbErrorWithDb(int sql_code, sqlite3 *db, CFErrorRef *error, CFStringRef format, ...) { 82 if (sql_code == SQLITE_OK) return true; 83 if (error) { 84 va_list args; 85 va_start(args, format); 86 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args); 87 va_end(args); 88 89 int extended_code = sqlite3_extended_errcode(db); 90 if (sql_code == extended_code) 91 SecDbError(sql_code, error, CFSTR("%@: [%d] %s"), message, sql_code, sqlite3_errmsg(db)); 92 else 93 SecDbError(sql_code, error, CFSTR("%@: [%d->%d] %s"), message, sql_code, extended_code, sqlite3_errmsg(db)); 94 CFReleaseSafe(message); 95 } 96 return false; 97} 98 99bool SecDbErrorWithStmt(int sql_code, sqlite3_stmt *stmt, CFErrorRef *error, CFStringRef format, ...) { 100 if (sql_code == SQLITE_OK) return true; 101 if (error) { 102 va_list args; 103 va_start(args, format); 104 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args); 105 va_end(args); 106 107 sqlite3 *db = sqlite3_db_handle(stmt); 108 const char *sql = sqlite3_sql(stmt); 109 int extended_code = sqlite3_extended_errcode(db); 110 if (sql_code == extended_code) 111 SecDbError(sql_code, error, CFSTR("%@: [%d] %s sql: %s"), message, sql_code, sqlite3_errmsg(db), sql); 112 else 113 SecDbError(sql_code, error, CFSTR("%@: [%d->%d] %s sql: %s"), message, sql_code, extended_code, sqlite3_errmsg(db), sql); 114 CFReleaseSafe(message); 115 } 116 return false; 117} 118 119 120// MARK: - 121// MARK: Static helper functions 122 123static bool SecDbOpenHandle(SecDbConnectionRef dbconn, bool *created, CFErrorRef *error); 124static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn, int rc, CFErrorRef *error); 125 126#pragma mark - 127#pragma mark SecDbRef 128 129static CFStringRef 130SecDbCopyDescription(CFTypeRef value) 131{ 132 SecDbRef db = (SecDbRef)value; 133 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecDb path:%@ connections: %@>"), db->db_path, db->connections); 134} 135 136 137static void 138SecDbDestroy(CFTypeRef value) 139{ 140 SecDbRef db = (SecDbRef)value; 141 CFReleaseSafe(db->connections); 142 CFReleaseSafe(db->db_path); 143 dispatch_release(db->queue); 144 dispatch_release(db->read_semaphore); 145 dispatch_release(db->write_semaphore); 146} 147 148CFGiblisFor(SecDb) 149 150SecDbRef 151SecDbCreate(CFStringRef dbName, 152 bool (^opened)(SecDbConnectionRef dbconn, bool did_create, CFErrorRef *error)) 153{ 154 SecDbRef db = NULL; 155 156 db = CFTypeAllocate(SecDb, struct __OpaqueSecDb, kCFAllocatorDefault); 157 require(db != NULL, done); 158 159 CFStringPerformWithCString(dbName, ^(const char *dbNameStr) { 160 db->queue = dispatch_queue_create(dbNameStr, DISPATCH_QUEUE_SERIAL); 161 }); 162 db->read_semaphore = dispatch_semaphore_create(kSecDbMaxReaders); 163 db->write_semaphore = dispatch_semaphore_create(kSecDbMaxWriters); 164 db->connections = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 165 db->opened = opened; 166 if (getenv("__OSINSTALL_ENVIRONMENT") != NULL) { 167 // TODO: Move this code out of this layer 168 LOGV("sqlDb: running from installer"); 169 db->db_path = CFSTR("file::memory:?cache=shared"); 170 } else { 171 db->db_path = CFStringCreateCopy(kCFAllocatorDefault, dbName); 172 } 173done: 174 return db; 175} 176 177CFIndex 178SecDbIdleConnectionCount(SecDbRef db) { 179 __block CFIndex count = 0; 180 dispatch_sync(db->queue, ^{ 181 count = CFArrayGetCount(db->connections); 182 }); 183 return count; 184} 185 186 187#pragma mark - 188#pragma mark SecDbConnectionRef 189 190static bool SecDbCheckCorrupted(SecDbConnectionRef dbconn) 191{ 192 __block bool isCorrupted = true; 193 __block CFErrorRef error = NULL; 194 SecDbPrepare(dbconn, CFSTR("PRAGMA integrity_check"), &error, ^(sqlite3_stmt *stmt) { 195 SecDbStep(dbconn, stmt, &error, ^(bool *stop) { 196 const char * result = (const char*)sqlite3_column_text(stmt, 0); 197 if (result && strncasecmp(result, "ok", 3) == 0) { 198 isCorrupted = false; 199 } 200 }); 201 }); 202 if (error) { 203 LOGV("sqlDb: warning error %@ when running integrity check", error); 204 CFRelease(error); 205 } 206 return isCorrupted; 207} 208 209static bool SecDbDidCreateFirstConnection(SecDbConnectionRef dbconn, bool didCreate, CFErrorRef *error) 210{ 211 LOGD("sqlDb: starting maintenance"); 212 bool ok = true; 213 214 if (!didCreate && !dbconn->isCorrupted) { 215 dbconn->isCorrupted = SecDbCheckCorrupted(dbconn); 216 if (dbconn->isCorrupted) 217 secerror("integrity check=fail"); 218 else 219 LOGD("sqlDb: integrity check=pass"); 220 } 221 222 if (!dbconn->isCorrupted && dbconn->db->opened) { 223 CFErrorRef localError = NULL; 224 225 ok = dbconn->db->opened(dbconn, didCreate, &localError); 226 227 if (!ok) 228 secerror("opened block failed: %@", localError); 229 230 if (!dbconn->isCorrupted && error && *error == NULL) { 231 *error = localError; 232 localError = NULL; 233 } else { 234 secerror("opened block failed: error is released and lost"); 235 CFReleaseNull(localError); 236 } 237 } 238 239 if (dbconn->isCorrupted) { 240 ok = SecDbHandleCorrupt(dbconn, 0, error); 241 } 242 243 LOGD("sqlDb: finished maintenance"); 244 return ok; 245} 246 247void SecDbCorrupt(SecDbConnectionRef dbconn) 248{ 249 dbconn->isCorrupted = true; 250} 251 252 253static uint8_t knownDbPathIndex(SecDbConnectionRef dbconn) 254{ 255 256 if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/keychain-2.db"))) 257 return 1; 258 if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/ocspcache.sqlite3"))) 259 return 2; 260 if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/TrustStore.sqlite3"))) 261 return 3; 262 if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/caissuercache.sqlite3"))) 263 return 4; 264 265 /* Unknown DB path */ 266 return 0; 267} 268 269 270// Return true if there was no error, returns false otherwise and set *error to an appropriate CFErrorRef. 271static bool SecDbConnectionCheckCode(SecDbConnectionRef dbconn, int code, CFErrorRef *error, CFStringRef desc, ...) { 272 if (code == SQLITE_OK || code == SQLITE_DONE) 273 return true; 274 275 if (error) { 276 va_list args; 277 va_start(args, desc); 278 CFStringRef msg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, desc, args); 279 va_end(args); 280 SecDbErrorWithDb(code, dbconn->handle, error, msg); 281 CFRelease(msg); 282 } 283 284 /* If it's already corrupted, don't try to recover */ 285 if (dbconn->isCorrupted) { 286 CFStringRef reason = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("SQL DB %@ is corrupted already. Not trying to recover"), dbconn->db->db_path); 287 secerror("%@",reason); 288 __security_simulatecrash(reason, __sec_exception_code_TwiceCorruptDb(knownDbPathIndex(dbconn))); 289 CFReleaseSafe(reason); 290 return false; 291 } 292 293 dbconn->isCorrupted = (SQLITE_CORRUPT == code) || (SQLITE_NOTADB == code) || (SQLITE_IOERR == code) || (SQLITE_CANTOPEN == code); 294 if (dbconn->isCorrupted) { 295 /* Run integrity check and only make dbconn->isCorrupted true and 296 run the corruption handler if the integrity check conclusively fails. */ 297 dbconn->isCorrupted = SecDbCheckCorrupted(dbconn); 298 if (dbconn->isCorrupted) { 299 secerror("operation returned code: %d integrity check=fail", code); 300 SecDbHandleCorrupt(dbconn, code, error); 301 } else { 302 secerror("operation returned code: %d: integrity check=pass", code); 303 } 304 } 305 306 return false; 307} 308 309#if HAVE_UNLOCK_NOTIFY 310 311static void SecDbUnlockNotify(void **apArg, int nArg) { 312 int i; 313 for(i=0; i<nArg; i++) { 314 dispatch_semaphore_t dsema = (dispatch_semaphore_t)apArg[i]; 315 dispatch_semaphore_signal(dsema); 316 } 317} 318 319static bool SecDbWaitForUnlockNotify(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error) { 320 int rc; 321 dispatch_semaphore_t dsema = dispatch_semaphore_create(0); 322 rc = sqlite3_unlock_notify(dbconn->handle, SecDbUnlockNotify, dsema); 323 assert(rc == SQLITE_LOCKED || rc == SQLITE_OK); 324 if (rc == SQLITE_OK) { 325 dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER); 326 } 327 dispatch_release(dsema); 328 return (rc == SQLITE_OK 329 ? true 330 : (stmt 331 ? SecDbErrorWithStmt(rc, stmt, error, CFSTR("sqlite3_unlock_notify")) 332 : SecDbErrorWithDb(rc, dbconn->handle, error, CFSTR("sqlite3_unlock_notify")))); 333} 334 335#endif 336 337#if USE_BUSY_HANDLER 338 339// Return 0 to stop retrying. 340static int SecDbHandleBusy(void *ctx, int retryCount) { 341 SecDbConnectionRef dbconn __unused = ctx; 342 struct timespec sleeptime = { .tv_sec = 0, .tv_nsec = 10000 }; 343 while (retryCount--) { 344 // Double sleeptime until we hit one second then add one 345 // second more every time we sleep. 346 if (sleeptime.tv_sec) { 347 sleeptime.tv_sec++; 348 } else { 349 sleeptime.tv_nsec *= 2; 350 if (sleeptime.tv_nsec > NSEC_PER_SEC) { 351 sleeptime.tv_nsec = 0; 352 sleeptime.tv_sec++; 353 } 354 } 355 } 356 struct timespec unslept = {}; 357 nanosleep(&sleeptime, &unslept); 358 359 return 1; 360} 361 362static bool SecDbBusyHandler(SecDbConnectionRef dbconn, CFErrorRef *error) { 363 return SecDbErrorWithDb(sqlite3_busy_handler(dbconn->handle, SecDbHandleBusy, dbconn), dbconn->handle, error, CFSTR("busy_handler")); 364} 365 366#endif // USE_BUSY_HANDLER 367 368// Return true causes the operation to be tried again. 369static bool SecDbWaitIfNeeded(SecDbConnectionRef dbconn, int s3e, sqlite3_stmt *stmt, CFStringRef desc, struct timespec *sleeptime, CFErrorRef *error) { 370#if HAVE_UNLOCK_NOTIFY 371 if (s3e == SQLITE_LOCKED) { // Optionally check for extended code being SQLITE_LOCKED_SHAREDCACHE 372 return SecDbWaitForUnlockNotify(dbconn, stmt, error)) 373 } 374#endif 375 376#if !USE_BUSY_HANDLER 377 if (s3e == SQLITE_LOCKED || s3e == SQLITE_BUSY) { 378 LOGV("sqlDb: %s", sqlite3_errmsg(dbconn->handle)); 379 while (s3e == SQLITE_LOCKED || s3e == SQLITE_BUSY) { 380 struct timespec unslept = {}; 381 nanosleep(sleeptime, &unslept); 382 s3e = SQLITE_OK; 383 if (stmt) 384 s3e = sqlite3_reset(stmt); 385 386 // Double sleeptime until we hit one second the add one 387 // second more every time we sleep. 388 if (sleeptime->tv_sec) { 389 sleeptime->tv_sec++; 390 } else { 391 sleeptime->tv_nsec *= 2; 392 if (sleeptime->tv_nsec > NSEC_PER_SEC) { 393 sleeptime->tv_nsec = 0; 394 sleeptime->tv_sec++; 395 } 396 } 397 } 398 if (s3e) 399 return SecDbErrorWithStmt(s3e, stmt, error, CFSTR("reset")); 400 } else 401#endif // !USE_BUSY_HANDLER 402 { 403 return SecDbConnectionCheckCode(dbconn, s3e, error, desc); 404 } 405 return true; 406} 407 408enum SecDbStepResult { 409 kSecDbErrorStep = 0, 410 kSecDbRowStep = 1, 411 kSecDbDoneStep = 2, 412}; 413typedef enum SecDbStepResult SecDbStepResult; 414 415static SecDbStepResult _SecDbStep(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error) { 416 assert(stmt != NULL); 417 int s3e; 418 struct timespec sleeptime = { .tv_sec = 0, .tv_nsec = 10000 }; 419 for (;;) { 420 s3e = sqlite3_step(stmt); 421 if (s3e == SQLITE_ROW) 422 return kSecDbRowStep; 423 else if (s3e == SQLITE_DONE) 424 return kSecDbDoneStep; 425 else if (!SecDbWaitIfNeeded(dbconn, s3e, stmt, CFSTR("step"), &sleeptime, error)) 426 return kSecDbErrorStep; 427 }; 428} 429 430bool 431SecDbExec(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error) 432{ 433 bool ok = true; 434 CFRetain(sql); 435 while (sql) { 436 CFStringRef tail = NULL; 437 if (ok) { 438 sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, &tail, error); 439 ok = stmt != NULL; 440 if (stmt) { 441 SecDbStepResult sr; 442 while ((sr = _SecDbStep(dbconn, stmt, error)) == kSecDbRowStep); 443 if (sr == kSecDbErrorStep) 444 ok = false; 445 ok &= SecDbReleaseCachedStmt(dbconn, sql, stmt, error); 446 } 447 } else { 448 // TODO We already have an error here we really just want the left over sql in it's userData 449 ok = SecDbError(SQLITE_ERROR, error, CFSTR("Error with unexecuted sql remaining %@"), sql); 450 } 451 CFRelease(sql); 452 sql = tail; 453 } 454 return ok; 455} 456 457static bool SecDbBeginTransaction(SecDbConnectionRef dbconn, SecDbTransactionType type, CFErrorRef *error) 458{ 459 bool ok = true; 460 CFStringRef query; 461 switch (type) { 462 case kSecDbImmediateTransactionType: 463 query = CFSTR("BEGIN IMMEDATE"); 464 break; 465 case kSecDbExclusiveTransactionType: 466 query = CFSTR("BEGIN EXCLUSIVE"); 467 break; 468 case kSecDbNormalTransactionType: 469 query = CFSTR("BEGIN"); 470 break; 471 default: 472 ok = SecDbError(SQLITE_ERROR, error, CFSTR("invalid transaction type %" PRIu32), type); 473 query = NULL; 474 break; 475 } 476 477 if (query != NULL && sqlite3_get_autocommit(dbconn->handle) != 0) { 478 ok = SecDbExec(dbconn, query, error); 479 } 480 481 return ok; 482} 483 484static bool SecDbEndTransaction(SecDbConnectionRef dbconn, bool commit, CFErrorRef *error) 485{ 486 if (commit) { 487 return SecDbExec(dbconn, CFSTR("END"), error); 488 } else { 489 return SecDbExec(dbconn, CFSTR("ROLLBACK"), error); 490 } 491} 492 493bool SecDbTransaction(SecDbConnectionRef dbconn, SecDbTransactionType type, 494 CFErrorRef *error, void (^transaction)(bool *commit)) 495{ 496 bool ok = true; 497 bool commit = true; 498 499 if (dbconn->inTransaction) { 500 transaction(&commit); 501 if (!commit) { 502 LOGV("sqlDb: nested transaction asked to not be committed"); 503 } 504 } else { 505 ok = SecDbBeginTransaction(dbconn, type, error); 506 if (ok) { 507 dbconn->inTransaction = true; 508 transaction(&commit); 509 dbconn->inTransaction = false; 510 ok = SecDbEndTransaction(dbconn, commit, error); 511 } 512 } 513 514done: 515 return ok && commit; 516} 517 518sqlite3 *SecDbHandle(SecDbConnectionRef dbconn) { 519 return dbconn->handle; 520} 521 522bool SecDbStep(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error, void (^row)(bool *stop)) { 523 for (;;) { 524 switch (_SecDbStep(dbconn, stmt, error)) { 525 case kSecDbErrorStep: 526 return false; 527 case kSecDbRowStep: 528 if (row) { 529 bool stop = false; 530 row(&stop); 531 if (stop) 532 return true; 533 break; 534 } 535 SecDbError(SQLITE_ERROR, error, CFSTR("SecDbStep SQLITE_ROW returned without a row handler")); 536 return false; 537 case kSecDbDoneStep: 538 return true; 539 } 540 } 541} 542 543bool SecDbCheckpoint(SecDbConnectionRef dbconn, CFErrorRef *error) 544{ 545 return SecDbConnectionCheckCode(dbconn, sqlite3_wal_checkpoint(dbconn->handle, NULL), error, CFSTR("wal_checkpoint")); 546} 547 548static bool SecDbFileControl(SecDbConnectionRef dbconn, int op, void *arg, CFErrorRef *error) { 549 return SecDbConnectionCheckCode(dbconn, sqlite3_file_control(dbconn->handle, NULL, op, arg), error, CFSTR("file_control")); 550} 551 552static sqlite3 *_SecDbOpenV2(const char *path, int flags, CFErrorRef *error) { 553#if HAVE_UNLOCK_NOTIFY 554 flags |= SQLITE_OPEN_SHAREDCACHE; 555#endif 556 sqlite3 *handle = NULL; 557 int s3e = sqlite3_open_v2(path, &handle, flags, NULL); 558 if (s3e) { 559 if (handle) { 560 SecDbErrorWithDb(s3e, handle, error, CFSTR("open_v2 \"%s\" 0x%X"), path, flags); 561 sqlite3_close(handle); 562 handle = NULL; 563 } else { 564 SecDbError(s3e, error, CFSTR("open_v2 \"%s\" 0x%X"), path, flags); 565 } 566 } 567 return handle; 568} 569 570static bool SecDbOpenV2(SecDbConnectionRef dbconn, const char *path, int flags, CFErrorRef *error) { 571 return (dbconn->handle = _SecDbOpenV2(path, flags, error)) != NULL; 572} 573 574static bool SecDbTruncate(SecDbConnectionRef dbconn, CFErrorRef *error) 575{ 576 int flags = SQLITE_TRUNCATE_JOURNALMODE_WAL | SQLITE_TRUNCATE_AUTOVACUUM_FULL; 577 __block bool ok = SecDbFileControl(dbconn, SQLITE_TRUNCATE_DATABASE, &flags, error); 578 if (!ok) { 579 sqlite3_close(dbconn->handle); 580 dbconn->handle = NULL; 581 CFStringPerformWithCString(dbconn->db->db_path, ^(const char *path) { 582 if (error) 583 CFReleaseNull(*error); 584 if (SecCheckErrno(unlink(path), error, CFSTR("unlink %s"), path)) { 585 ok = SecDbOpenHandle(dbconn, NULL, error); 586 } 587 }); 588 if (!ok) { 589 secerror("Failed to delete db handle: %@", error ? *error : NULL); 590 abort(); 591 } 592 } 593 594 return ok; 595} 596 597static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn, int rc, CFErrorRef *error) 598{ 599 CFStringRef reason = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("SQL DB %@ is corrupted, trying to recover (rc=%d)"), dbconn->db->db_path, rc); 600 __security_simulatecrash(reason, __sec_exception_code_CorruptDb(knownDbPathIndex(dbconn), rc)); 601 CFReleaseSafe(reason); 602 603 // Backup current db. 604 __block bool didRename = false; 605 CFStringPerformWithCString(dbconn->db->db_path, ^(const char *db_path) { 606 sqlite3 *corrupt_db = NULL; 607 char buf[PATH_MAX+1]; 608 snprintf(buf, sizeof(buf), "%s-corrupt", db_path); 609 if (dbconn->handle && (corrupt_db = _SecDbOpenV2(buf, SQLITE_OPEN_READWRITE, error))) { 610 int on = 1; 611 didRename = SecDbErrorWithDb(sqlite3_file_control(corrupt_db, NULL, SQLITE_FCNTL_PERSIST_WAL, &on), corrupt_db, error, CFSTR("persist wal")); 612 didRename &= SecDbErrorWithDb(sqlite3_file_control(corrupt_db, NULL, SQLITE_REPLACE_DATABASE, (void *)dbconn->handle), corrupt_db, error, CFSTR("replace database")); 613 sqlite3_close(corrupt_db); 614 } 615 if (!didRename) { 616 if (dbconn->handle) 617 secerror("Tried to rename corrupt database at path %@, but we failed: %@, trying explicit rename", dbconn->db->db_path, error ? *error : NULL); 618 if (error) 619 CFReleaseNull(*error); 620 621 didRename = SecCheckErrno(rename(db_path, buf), error, CFSTR("rename %s %s"), db_path, buf) && 622 (!dbconn->handle || SecDbError(sqlite3_close(dbconn->handle), error, CFSTR("close"))) && 623 SecDbOpenHandle(dbconn, NULL, error); 624 } 625 if (didRename) { 626 secerror("Database at path %@ is corrupt. Copied it to %s for further investigation.", dbconn->db->db_path, buf); 627 } else { 628 seccritical("Tried to copy corrupt database at path %@, but we failed: %@", dbconn->db->db_path, error ? *error : NULL); 629 } 630 }); 631 632 bool ok = (didRename && 633 (dbconn->handle || SecDbOpenHandle(dbconn, NULL, error)) && 634 SecDbTruncate(dbconn, error)); 635 636 // Mark the db as not corrupted, even if something failed. 637 // Always note we are no longer in the corruption handler 638 dbconn->isCorrupted = false; 639 640 // Invoke our callers opened callback, since we just created a new database 641 if (ok && dbconn->db->opened) 642 ok = dbconn->db->opened(dbconn, true, error); 643 644 return ok; 645} 646 647static bool SecDbProfileEnabled(void) 648{ 649#if 0 650 static dispatch_once_t onceToken; 651 static bool profile_enabled = false; 652 653#if DEBUG 654 //sudo defaults write /Library/Preferences/com.apple.security.auth profile -bool true 655 dispatch_once(&onceToken, ^{ 656 CFTypeRef profile = (CFNumberRef)CFPreferencesCopyValue(CFSTR("profile"), CFSTR(SECURITY_AUTH_NAME), kCFPreferencesAnyUser, kCFPreferencesCurrentHost); 657 658 if (profile && CFGetTypeID(profile) == CFBooleanGetTypeID()) { 659 profile_enabled = CFBooleanGetValue((CFBooleanRef)profile); 660 } 661 662 LOGV("sqlDb: sql profile: %s", profile_enabled ? "enabled" : "disabled"); 663 664 CFReleaseSafe(profile); 665 }); 666#endif 667 668 return profile_enabled; 669#else 670#if DEBUG 671 return true; 672#else 673 return false; 674#endif 675#endif 676} 677 678#if 0 679static void SecDbProfile(void *context __unused, const char *sql, sqlite3_uint64 ns) { 680 LOGV("==\nsqlDb: %s\nTime: %llu ms\n", sql, ns >> 20); 681} 682#else 683static void SecDbProfile(void *context, const char *sql, sqlite3_uint64 ns) { 684 sqlite3 *s3h = context; 685 int code = sqlite3_extended_errcode(s3h); 686 if (code == SQLITE_OK || code == SQLITE_DONE) { 687 secdebug("profile", "==\nsqlDb: %s\nTime: %llu ms\n", sql, ns >> 20); 688 } else { 689 secdebug("profile", "==error[%d]: %s==\nsqlDb: %s\nTime: %llu ms \n", code, sqlite3_errmsg(s3h), sql, ns >> 20); 690 } 691} 692#endif 693 694static bool SecDbTraceEnabled(void) 695{ 696#if DEBUG 697 return true; 698#else 699 return false; 700#endif 701} 702 703static void SecDbTrace(void *ctx, const char *trace) { 704 SecDbConnectionRef dbconn __unused = ctx; 705 static dispatch_queue_t queue; 706 static dispatch_once_t once; 707 dispatch_once(&once, ^{ 708 queue = dispatch_queue_create("trace_queue", DISPATCH_QUEUE_SERIAL); 709 }); 710 dispatch_sync(queue, ^{ 711 __security_debug(CFSTR("trace"), "", "", 0, CFSTR("%s"), trace); 712 }); 713} 714 715static bool SecDbOpenHandle(SecDbConnectionRef dbconn, bool *created, CFErrorRef *error) 716{ 717 __block bool ok = true; 718 CFStringPerformWithCString(dbconn->db->db_path, ^(const char *db_path) { 719 ok = created && SecDbOpenV2(dbconn, db_path, SQLITE_OPEN_READWRITE, NULL); 720 if (!ok) { 721 ok = true; 722 if (created) { 723 char *tmp = dirname((char *)db_path); 724 if (tmp) { 725 int errnum = mkpath_np(tmp, 0700); 726 if (errnum != 0 && errnum != EEXIST) { 727 SecCFCreateErrorWithFormat(errnum, kSecErrnoDomain, NULL, error, NULL, 728 CFSTR("mkpath_np %s: [%d] %s"), tmp, errnum, strerror(errnum)); 729 ok = false; 730 } 731 } 732 } 733 ok = ok && SecDbOpenV2(dbconn, db_path, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, error); 734 if (ok) { 735 chmod(db_path, S_IRUSR | S_IWUSR); 736 if (created) 737 *created = true; 738 } 739 } 740 741 if (ok && SecDbProfileEnabled()) { 742 sqlite3_profile(dbconn->handle, SecDbProfile, dbconn->handle); 743 } 744 if (ok && SecDbTraceEnabled()) { 745 sqlite3_trace(dbconn->handle, SecDbTrace, dbconn); 746 } 747#if USE_BUSY_HANDLER 748 ok = ok && SecDbBusyHandler(dbconn, error); 749#endif 750 }); 751 752done: 753 return ok; 754} 755 756static SecDbConnectionRef 757SecDbConnectionCreate(SecDbRef db, bool readOnly, CFErrorRef *error) 758{ 759 SecDbConnectionRef dbconn = NULL; 760 761 dbconn = CFTypeAllocate(SecDbConnection, struct __OpaqueSecDbConnection, kCFAllocatorDefault); 762 require(dbconn != NULL, done); 763 764 dbconn->db = db; 765 dbconn->readOnly = readOnly; 766 767done: 768 return dbconn; 769} 770 771static bool SecDbConnectionIsReadOnly(SecDbConnectionRef dbconn) { 772 return dbconn->readOnly; 773} 774 775static void SecDbConectionSetReadOnly(SecDbConnectionRef dbconn, bool readOnly) { 776 dbconn->readOnly = readOnly; 777} 778 779/* Read only connections go to the end of the queue, writeable connections 780 go to the start of the queue. */ 781SecDbConnectionRef SecDbConnectionAquire(SecDbRef db, bool readOnly, CFErrorRef *error) { 782 CFRetain(db); 783 secdebug("dbconn", "aquire %s connection", readOnly ? "ro" : "rw"); 784 dispatch_semaphore_wait(readOnly ? db->read_semaphore : db->write_semaphore, DISPATCH_TIME_FOREVER); 785 __block SecDbConnectionRef dbconn = NULL; 786 __block bool ok = true; 787 dispatch_sync(db->queue, ^{ 788 if (!db->didFirstOpen) { 789 bool didCreate = false; 790 ok = dbconn = SecDbConnectionCreate(db, false, error); 791 CFErrorRef localError = NULL; 792 if (ok && !SecDbOpenHandle(dbconn, &didCreate, &localError)) { 793 secerror("Unable to create database: %@", localError); 794 if (localError && CFEqual(CFErrorGetDomain(localError), kSecDbErrorDomain)) { 795 int code = (int)CFErrorGetCode(localError); 796 dbconn->isCorrupted = (SQLITE_CORRUPT == code) || (SQLITE_NOTADB == code) || (SQLITE_IOERR == code) || (SQLITE_CANTOPEN == code); 797 } 798 // If the open failure isn't due to corruption, propagte the error. 799 ok = dbconn->isCorrupted; 800 if (!ok && error && *error == NULL) { 801 *error = localError; 802 localError = NULL; 803 } 804 } 805 CFReleaseNull(localError); 806 807 if (ok) 808 db->didFirstOpen = ok = SecDbDidCreateFirstConnection(dbconn, didCreate, error); 809 if (!ok) 810 CFReleaseNull(dbconn); 811 } else { 812 /* Try to get one from the cache */ 813 CFIndex count = CFArrayGetCount(db->connections); 814 while (count && !dbconn) { 815 CFIndex ix = readOnly ? count - 1 : 0; 816 dbconn = (SecDbConnectionRef)CFArrayGetValueAtIndex(db->connections, ix); 817 if (dbconn) 818 CFRetain(dbconn); 819 else 820 secerror("got NULL dbconn at index: %" PRIdCFIndex " skipping", ix); 821 CFArrayRemoveValueAtIndex(db->connections, ix); 822 } 823 } 824 }); 825 826 if (dbconn) { 827 /* Make sure the connection we found has the right access */ 828 if (SecDbConnectionIsReadOnly(dbconn) != readOnly) { 829 SecDbConectionSetReadOnly(dbconn, readOnly); 830 } 831 } else if (ok) { 832 /* Nothing found in cache, create a new connection */ 833 bool created = false; 834 dbconn = SecDbConnectionCreate(db, readOnly, error); 835 if (dbconn && !SecDbOpenHandle(dbconn, &created, error)) { 836 CFReleaseNull(dbconn); 837 } 838 } 839 840 if (!dbconn) { 841 // If aquire fails we need to signal the semaphore again. 842 dispatch_semaphore_signal(readOnly ? db->read_semaphore : db->write_semaphore); 843 CFRelease(db); 844 } 845 846 return dbconn; 847} 848 849void SecDbConnectionRelease(SecDbConnectionRef dbconn) { 850 if (!dbconn) { 851 secerror("called with NULL dbconn"); 852 return; 853 } 854 SecDbRef db = dbconn->db; 855 secdebug("dbconn", "release %@", dbconn); 856 dispatch_sync(db->queue, ^{ 857 CFIndex count = CFArrayGetCount(db->connections); 858 // Add back possible writable dbconn to the pool. 859 bool readOnly = SecDbConnectionIsReadOnly(dbconn); 860 CFArrayInsertValueAtIndex(db->connections, readOnly ? count : 0, dbconn); 861 // Remove the last (probably read-only) dbconn from the pool. 862 if (count >= kSecDbMaxIdleHandles) { 863 CFArrayRemoveValueAtIndex(db->connections, count); 864 } 865 // Signal after we have put the connection back in the pool of connections 866 dispatch_semaphore_signal(readOnly ? db->read_semaphore : db->write_semaphore); 867 CFRelease(dbconn); 868 CFRelease(db); 869 }); 870} 871 872bool SecDbPerformRead(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) { 873 SecDbConnectionRef dbconn = SecDbConnectionAquire(db, true, error); 874 bool success = false; 875 if (dbconn) { 876 perform(dbconn); 877 success = true; 878 SecDbConnectionRelease(dbconn); 879 } 880 return success; 881} 882 883bool SecDbPerformWrite(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) { 884 SecDbConnectionRef dbconn = SecDbConnectionAquire(db, false, error); 885 bool success = false; 886 if (dbconn) { 887 perform(dbconn); 888 success = true; 889 SecDbConnectionRelease(dbconn); 890 } 891 return success; 892} 893 894static CFStringRef 895SecDbConnectionCopyDescription(CFTypeRef value) 896{ 897 SecDbConnectionRef dbconn = (SecDbConnectionRef)value; 898 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecDbConnection %s %s>"), 899 dbconn->readOnly ? "ro" : "rw", dbconn->handle ? "open" : "closed"); 900} 901 902static void 903SecDbConnectionDestroy(CFTypeRef value) 904{ 905 SecDbConnectionRef dbconn = (SecDbConnectionRef)value; 906 if (dbconn->handle) { 907 sqlite3_close(dbconn->handle); 908 } 909 dbconn->db = NULL; 910} 911 912 913// MARK: - 914// MARK: Bind helpers 915 916#if 0 917bool SecDbBindNull(sqlite3_stmt *stmt, int param, CFErrorRef *error) { 918 bool ok = SecDbErrorWithStmt(sqlite3_bind_null(stmt, param), 919 stmt, error, CFSTR("bind_null[%d]"), param); 920 secdebug("bind", "bind_null[%d]: %@", param, error ? *error : NULL); 921 return ok; 922} 923#endif 924 925bool SecDbBindBlob(sqlite3_stmt *stmt, int param, const void *zData, size_t n, void(*xDel)(void*), CFErrorRef *error) { 926 if (n > INT_MAX) { 927 return SecDbErrorWithStmt(SQLITE_TOOBIG, stmt, error, 928 CFSTR("bind_blob[%d]: blob bigger than INT_MAX"), param); 929 } 930 bool ok = SecDbErrorWithStmt(sqlite3_bind_blob(stmt, param, zData, (int)n, xDel), 931 stmt, error, CFSTR("bind_blob[%d]"), param); 932 secdebug("bind", "bind_blob[%d]: %.*s: %@", param, (int)n, zData, error ? *error : NULL); 933 return ok; 934} 935 936bool SecDbBindText(sqlite3_stmt *stmt, int param, const char *zData, size_t n, void(*xDel)(void*), CFErrorRef *error) { 937 if (n > INT_MAX) { 938 return SecDbErrorWithStmt(SQLITE_TOOBIG, stmt, error, 939 CFSTR("bind_text[%d]: text bigger than INT_MAX"), param); 940 } 941 bool ok = SecDbErrorWithStmt(sqlite3_bind_text(stmt, param, zData, (int)n, xDel), stmt, error, 942 CFSTR("bind_text[%d]"), param); 943 secdebug("bind", "bind_text[%d]: \"%s\": %@", param, zData, error ? *error : NULL); 944 return ok; 945} 946 947bool SecDbBindDouble(sqlite3_stmt *stmt, int param, double value, CFErrorRef *error) { 948 bool ok = SecDbErrorWithStmt(sqlite3_bind_double(stmt, param, value), stmt, error, 949 CFSTR("bind_double[%d]"), param); 950 secdebug("bind", "bind_double[%d]: %f: %@", param, value, error ? *error : NULL); 951 return ok; 952} 953 954bool SecDbBindInt(sqlite3_stmt *stmt, int param, int value, CFErrorRef *error) { 955 bool ok = SecDbErrorWithStmt(sqlite3_bind_int(stmt, param, value), stmt, error, 956 CFSTR("bind_int[%d]"), param); 957 secdebug("bind", "bind_int[%d]: %d: %@", param, value, error ? *error : NULL); 958 return ok; 959} 960 961bool SecDbBindInt64(sqlite3_stmt *stmt, int param, sqlite3_int64 value, CFErrorRef *error) { 962 bool ok = SecDbErrorWithStmt(sqlite3_bind_int64(stmt, param, value), stmt, error, 963 CFSTR("bind_int64[%d]"), param); 964 secdebug("bind", "bind_int64[%d]: %lld: %@", param, value, error ? *error : NULL); 965 return ok; 966} 967 968 969/* AUDIT[securityd](done): 970 value (ok) is a caller provided, non NULL CFTypeRef. 971 */ 972bool SecDbBindObject(sqlite3_stmt *stmt, int param, CFTypeRef value, CFErrorRef *error) { 973 CFTypeID valueId; 974 __block bool result = false; 975 976 /* TODO: Can we use SQLITE_STATIC below everwhere we currently use 977 SQLITE_TRANSIENT since we finalize the statement before the value 978 goes out of scope? */ 979 if (!value || (valueId = CFGetTypeID(value)) == CFNullGetTypeID()) { 980 /* Skip bindings for NULL values. sqlite3 will interpret unbound 981 params as NULL which is exactly what we want. */ 982#if 1 983 result = true; 984#else 985 result = SecDbBindNull(stmt, param, error); 986#endif 987 } else if (valueId == CFStringGetTypeID()) { 988 CFStringPerformWithCStringAndLength(value, ^(const char *cstr, size_t clen) { 989 result = SecDbBindText(stmt, param, cstr, clen, SQLITE_TRANSIENT, error); 990 }); 991 } else if (valueId == CFDataGetTypeID()) { 992 CFIndex len = CFDataGetLength(value); 993 if (len) { 994 result = SecDbBindBlob(stmt, param, CFDataGetBytePtr(value), 995 len, SQLITE_TRANSIENT, error); 996 } else { 997 result = SecDbBindText(stmt, param, "", 0, SQLITE_TRANSIENT, error); 998 } 999 } else if (valueId == CFDateGetTypeID()) { 1000 CFAbsoluteTime abs_time = CFDateGetAbsoluteTime(value); 1001 result = SecDbBindDouble(stmt, param, abs_time, error); 1002 } else if (valueId == CFBooleanGetTypeID()) { 1003 int bval = CFBooleanGetValue(value); 1004 result = SecDbBindInt(stmt, param, bval, error); 1005 } else if (valueId == CFNumberGetTypeID()) { 1006 Boolean convertOk; 1007 if (CFNumberIsFloatType(value)) { 1008 double nval; 1009 convertOk = CFNumberGetValue(value, kCFNumberDoubleType, &nval); 1010 result = SecDbBindDouble(stmt, param, nval, error); 1011 } else { 1012 int nval; 1013 convertOk = CFNumberGetValue(value, kCFNumberSInt32Type, &nval); 1014 if (convertOk) { 1015 result = SecDbBindInt(stmt, param, nval, error); 1016 } else { 1017 sqlite_int64 nval64; 1018 convertOk = CFNumberGetValue(value, kCFNumberSInt64Type, &nval64); 1019 if (convertOk) 1020 result = SecDbBindInt64(stmt, param, nval64, error); 1021 } 1022 } 1023 if (!convertOk) { 1024 result = SecDbError(SQLITE_INTERNAL, error, CFSTR("bind CFNumberGetValue failed for %@"), value); 1025 } 1026 } else { 1027 if (error) { 1028 CFStringRef valueDesc = CFCopyTypeIDDescription(valueId); 1029 SecDbError(SQLITE_MISMATCH, error, CFSTR("bind unsupported type %@"), valueDesc); 1030 CFReleaseSafe(valueDesc); 1031 } 1032 } 1033 1034 return result; 1035} 1036 1037// MARK: - 1038// MARK: SecDbStatementRef 1039 1040bool SecDbReset(sqlite3_stmt *stmt, CFErrorRef *error) { 1041 return SecDbErrorWithStmt(sqlite3_reset(stmt), stmt, error, CFSTR("reset")); 1042} 1043 1044bool SecDbClearBindings(sqlite3_stmt *stmt, CFErrorRef *error) { 1045 return SecDbErrorWithStmt(sqlite3_clear_bindings(stmt), stmt, error, CFSTR("clear bindings")); 1046} 1047 1048bool SecDbFinalize(sqlite3_stmt *stmt, CFErrorRef *error) { 1049 int s3e = sqlite3_finalize(stmt); 1050 return s3e == SQLITE_OK ? true : SecDbErrorWithDb(s3e, sqlite3_db_handle(stmt), error, CFSTR("finalize: %p"), stmt); 1051} 1052 1053sqlite3_stmt *SecDbPrepareV2(SecDbConnectionRef dbconn, const char *sql, size_t sqlLen, const char **sqlTail, CFErrorRef *error) { 1054 sqlite3 *db = SecDbHandle(dbconn); 1055 if (sqlLen > INT_MAX) { 1056 SecDbErrorWithDb(SQLITE_TOOBIG, db, error, CFSTR("prepare_v2: sql bigger than INT_MAX")); 1057 return NULL; 1058 } 1059 struct timespec sleeptime = { .tv_sec = 0, .tv_nsec = 10000 }; 1060 for (;;) { 1061 sqlite3_stmt *stmt = NULL; 1062 int s3e = sqlite3_prepare_v2(db, sql, (int)sqlLen, &stmt, sqlTail); 1063 if (s3e == SQLITE_OK) 1064 return stmt; 1065 else if (!SecDbWaitIfNeeded(dbconn, s3e, NULL, CFSTR("preparev2"), &sleeptime, error)) 1066 return NULL; 1067 } 1068} 1069 1070static sqlite3_stmt *SecDbCopyStatementWithTailRange(SecDbConnectionRef dbconn, CFStringRef sql, CFRange *sqlTail, CFErrorRef *error) { 1071 __block sqlite3_stmt *stmt = NULL; 1072 if (sql) CFStringPerformWithCStringAndLength(sql, ^(const char *sqlStr, size_t sqlLen) { 1073 const char *tail = NULL; 1074 stmt = SecDbPrepareV2(dbconn, sqlStr, sqlLen, &tail, error); 1075 if (sqlTail && sqlStr < tail && tail < sqlStr + sqlLen) { 1076 sqlTail->location = tail - sqlStr; 1077 sqlTail->length = sqlLen - sqlTail->location; 1078 } 1079 }); 1080 1081 return stmt; 1082} 1083 1084sqlite3_stmt *SecDbCopyStmt(SecDbConnectionRef dbconn, CFStringRef sql, CFStringRef *tail, CFErrorRef *error) { 1085 // TODO: Add caching and cache lookup of statements 1086 CFRange sqlTail = {}; 1087 sqlite3_stmt *stmt = SecDbCopyStatementWithTailRange(dbconn, sql, &sqlTail, error); 1088 if (sqlTail.length > 0) { 1089 CFStringRef excess = CFStringCreateWithSubstring(CFGetAllocator(sql), sql, sqlTail); 1090 if (tail) { 1091 *tail = excess; 1092 } else { 1093 SecDbError(SQLITE_INTERNAL, error, 1094 CFSTR("prepare_v2: %@ unused sql: %@"), 1095 sql, excess); 1096 CFReleaseSafe(excess); 1097 SecDbFinalize(stmt, error); 1098 stmt = NULL; 1099 } 1100 } 1101 return stmt; 1102} 1103 1104/* 1105 TODO: Could do a hack here with a custom kCFAllocatorNULL allocator for a second CFRuntimeBase inside a SecDbStatement, 1106 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. */ 1107bool SecDbReleaseCachedStmt(SecDbConnectionRef dbconn, CFStringRef sql, sqlite3_stmt *stmt, CFErrorRef *error) { 1108 if (stmt) { 1109 return SecDbReset(stmt, error) && SecDbClearBindings(stmt, error) && SecDbFinalize(stmt, error); 1110 } 1111 return true; 1112} 1113 1114bool SecDbPrepare(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error, void(^exec)(sqlite3_stmt *stmt)) { 1115 assert(sql != NULL); 1116 sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, NULL, error); 1117 if (!stmt) 1118 return false; 1119 1120 exec(stmt); 1121 return SecDbReleaseCachedStmt(dbconn, sql, stmt, error); 1122} 1123 1124bool SecDbWithSQL(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error, bool(^perform)(sqlite3_stmt *stmt)) { 1125 bool ok = true; 1126 CFRetain(sql); 1127 while (sql) { 1128 CFStringRef tail = NULL; 1129 if (ok) { 1130 sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, &tail, error); 1131 ok = stmt != NULL; 1132 if (stmt) { 1133 if (perform) { 1134 ok = perform(stmt); 1135 } else { 1136 // TODO: Use a different error scope here. 1137 ok = SecError(-50 /* errSecParam */, error, CFSTR("SecDbWithSQL perform block missing")); 1138 } 1139 ok &= SecDbReleaseCachedStmt(dbconn, sql, stmt, error); 1140 } 1141 } else { 1142 // TODO We already have an error here we really just want the left over sql in it's userData 1143 ok = SecDbError(SQLITE_ERROR, error, CFSTR("Error with unexecuted sql remaining %@"), sql); 1144 } 1145 CFRelease(sql); 1146 sql = tail; 1147 } 1148 return ok; 1149} 1150 1151#if 1 1152/* SecDbForEach returns true if all SQLITE_ROW returns of sqlite3_step() return true from the row block. 1153 If the row block returns false and doesn't set an error (to indicate it has reached a limit), 1154 this entire function returns false. In that case no error will be set. */ 1155bool SecDbForEach(sqlite3_stmt *stmt, CFErrorRef *error, bool(^row)(int row_index)) { 1156 bool result = false; 1157 for (int row_ix = 0;;++row_ix) { 1158 int s3e = sqlite3_step(stmt); 1159 if (s3e == SQLITE_ROW) { 1160 if (row) { 1161 if (!row(row_ix)) { 1162 break; 1163 } 1164 } else { 1165 // If we have no row block then getting SQLITE_ROW is an error 1166 SecDbError(s3e, error, 1167 CFSTR("step[%d]: %s returned SQLITE_ROW with NULL row block"), 1168 row_ix, sqlite3_sql(stmt)); 1169 } 1170 } else { 1171 if (s3e == SQLITE_DONE) { 1172 result = true; 1173 } else { 1174 SecDbErrorWithStmt(s3e, stmt, error, CFSTR("step[%d]"), row_ix); 1175 } 1176 break; 1177 } 1178 } 1179 return result; 1180} 1181#else 1182bool SecDbForEach(sqlite3_stmt *stmt, CFErrorRef *error, bool(^row)(int row_index)) { 1183 int row_ix = 0; 1184 for (;;) { 1185 switch (_SecDbStep(dbconn, stmt, error)) { 1186 case kSecDbErrorStep: 1187 return false; 1188 case kSecDbRowStep: 1189 if (row) { 1190 if (row(row_ix++)) 1191 break; 1192 } else { 1193 SecDbError(SQLITE_ERROR, error, CFSTR("SecDbStep SQLITE_ROW returned without a row handler")); 1194 } 1195 return false; 1196 case kSecDbDoneStep: 1197 return true; 1198 } 1199 } 1200} 1201#endif 1202 1203CFGiblisFor(SecDbConnection) 1204