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