2 * Copyright (c) 2012-2017 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include "debugging.h"
29 #include <sqlite3_private.h>
30 #include <CoreFoundation/CoreFoundation.h>
34 #include <AssertMacros.h>
35 #include "SecCFWrappers.h"
36 #include "SecCFError.h"
37 #include "SecIOFormat.h"
39 #include "Security/SecBase.h"
40 #include "SecAutorelease.h"
41 #include <os/assumes.h>
42 #include <xpc/private.h> // xpc_transaction_exit_clean()
46 // Architecturally inverted files
47 // These are in SecureObjectSync but utilities depends on them
48 // <rdar://problem/20802079> Fix layer violation (SOSDigestVector, SOSManifest, SecDB.c)
50 #include "keychain/SecureObjectSync/SOSDigestVector.h"
51 #include "keychain/SecureObjectSync/SOSManifest.h"
53 #define SECDB_DEBUGGING 0
55 struct __OpaqueSecDbStatement
{
58 SecDbConnectionRef dbconn
;
62 struct __OpaqueSecDbConnection
{
65 //CFMutableDictionaryRef statements;
67 SecDbRef db
; // NONRETAINED, since db or block retains us
70 SecDbTransactionSource source
;
72 int maybeCorruptedCode
;
74 CFErrorRef corruptionError
;
76 // Pending deletions and additions for the current transaction
77 // Entires are either:
78 // 1) a CFArrayRef of 1 element representing a deletion,
79 // 2) a CFArrayRef of 2 elements representing the element 0 having been replaced with element 1
80 // 3) a CFTypeRef that is not a CFArrayRef, representing an add of the element in question.
81 CFMutableArrayRef changes
;
84 struct __OpaqueSecDb
{
88 dispatch_queue_t queue
;
89 dispatch_queue_t commitQueue
;
90 CFMutableArrayRef connections
;
91 dispatch_semaphore_t write_semaphore
;
92 dispatch_semaphore_t read_semaphore
;
94 bool (^opened
)(SecDbRef db
, SecDbConnectionRef dbconn
, bool didCreate
, bool *callMeAgainForNextConnection
, CFErrorRef
*error
);
95 bool callOpenedHandlerForNextConnection
;
96 CFMutableArrayRef notifyPhase
; /* array of SecDBNotifyBlock */
97 mode_t mode
; /* database file permissions */
98 bool readWrite
; /* open database read-write */
99 bool allowRepair
; /* allow database repair */
100 bool useWAL
; /* use WAL mode */
101 bool useRobotVacuum
; /* use if SecDB should manage vacuum behind your back */
102 uint8_t maxIdleHandles
;
103 void (^corruptionReset
)(void);
106 // MARK: Error domains and error helper functions
108 CFStringRef kSecDbErrorDomain
= CFSTR("com.apple.utilities.sqlite3");
110 bool SecDbError(int sql_code
, CFErrorRef
*error
, CFStringRef format
, ...) {
111 if (sql_code
== SQLITE_OK
) return true;
115 CFIndex code
= sql_code
;
116 CFErrorRef previousError
= *error
;
119 va_start(args
, format
);
120 SecCFCreateErrorWithFormatAndArguments(code
, kSecDbErrorDomain
, previousError
, error
, NULL
, format
, args
);
126 bool SecDbErrorWithDb(int sql_code
, sqlite3
*db
, CFErrorRef
*error
, CFStringRef format
, ...) {
127 if (sql_code
== SQLITE_OK
) return true;
130 va_start(args
, format
);
131 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
133 CFStringRef errno_code
= NULL
;
135 if (sql_code
== SQLITE_CANTOPEN
) {
136 int errno_number
= sqlite3_system_errno(db
);
137 errno_code
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%d"), errno_number
);
139 errno_code
= CFRetain(CFSTR(""));
142 int extended_code
= sqlite3_extended_errcode(db
);
143 if (sql_code
== extended_code
)
144 SecDbError(sql_code
, error
, CFSTR("%@: [%d]%@ %s"), message
, sql_code
, errno_code
, sqlite3_errmsg(db
));
146 SecDbError(sql_code
, error
, CFSTR("%@: [%d->%d]%@ %s"), message
, sql_code
, extended_code
, errno_code
, sqlite3_errmsg(db
));
147 CFReleaseSafe(message
);
148 CFReleaseSafe(errno_code
);
153 bool SecDbErrorWithStmt(int sql_code
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, CFStringRef format
, ...) {
154 if (sql_code
== SQLITE_OK
) return true;
157 va_start(args
, format
);
158 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
161 sqlite3
*db
= sqlite3_db_handle(stmt
);
162 const char *sql
= sqlite3_sql(stmt
);
163 int extended_code
= sqlite3_extended_errcode(db
);
164 if (sql_code
== extended_code
)
165 SecDbError(sql_code
, error
, CFSTR("%@: [%d] %s sql: %s"), message
, sql_code
, sqlite3_errmsg(db
), sql
);
167 SecDbError(sql_code
, error
, CFSTR("%@: [%d->%d] %s sql: %s"), message
, sql_code
, extended_code
, sqlite3_errmsg(db
), sql
);
168 CFReleaseSafe(message
);
173 // A callback for the sqlite3_log() interface.
174 static void sqlite3Log(void *pArg
, int iErrCode
, const char *zMsg
){
175 secdebug("sqlite3", "(%d) %s", iErrCode
, zMsg
);
178 void _SecDbServerSetup(void)
180 static dispatch_once_t onceToken
;
181 dispatch_once(&onceToken
, ^{
182 int rx
= sqlite3_config(SQLITE_CONFIG_LOG
, sqlite3Log
, NULL
);
183 if (SQLITE_OK
!= rx
) {
184 secwarning("Could not set up sqlite global error logging to syslog: %d", rx
);
191 // MARK: Static helper functions
193 static bool SecDbOpenHandle(SecDbConnectionRef dbconn
, bool *created
, CFErrorRef
*error
);
194 static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn
, int rc
, CFErrorRef
*error
);
197 #pragma mark SecDbRef
200 SecDbCopyFormatDescription(CFTypeRef value
, CFDictionaryRef formatOptions
)
202 SecDbRef db
= (SecDbRef
)value
;
203 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<SecDb path:%@ connections: %@>"), db
->db_path
, db
->connections
);
208 SecDbDestroy(CFTypeRef value
)
210 SecDbRef db
= (SecDbRef
)value
;
211 CFReleaseNull(db
->connections
);
212 CFReleaseNull(db
->db_path
);
214 dispatch_release(db
->queue
);
217 if (db
->commitQueue
) {
218 dispatch_release(db
->commitQueue
);
219 db
->commitQueue
= NULL
;
221 if (db
->read_semaphore
) {
222 dispatch_release(db
->read_semaphore
);
223 db
->read_semaphore
= NULL
;
225 if (db
->write_semaphore
) {
226 dispatch_release(db
->write_semaphore
);
227 db
->write_semaphore
= NULL
;
230 Block_release(db
->opened
);
233 CFReleaseNull(db
->notifyPhase
);
239 SecDbCreate(CFStringRef dbName
, mode_t mode
, bool readWrite
, bool allowRepair
, bool useWAL
, bool useRobotVacuum
, uint8_t maxIdleHandles
,
240 bool (^opened
)(SecDbRef db
, SecDbConnectionRef dbconn
, bool didCreate
, bool *callMeAgainForNextConnection
, CFErrorRef
*error
))
244 db
= CFTypeAllocate(SecDb
, struct __OpaqueSecDb
, kCFAllocatorDefault
);
245 require(db
!= NULL
, done
);
247 CFStringPerformWithCString(dbName
, ^(const char *dbNameStr
) {
248 db
->queue
= dispatch_queue_create(dbNameStr
, DISPATCH_QUEUE_SERIAL
);
250 CFStringRef commitQueueStr
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@-commit"), dbName
);
251 CFStringPerformWithCString(commitQueueStr
, ^(const char *cqNameStr
) {
252 db
->commitQueue
= dispatch_queue_create(cqNameStr
, DISPATCH_QUEUE_CONCURRENT
);
254 CFReleaseNull(commitQueueStr
);
255 db
->read_semaphore
= dispatch_semaphore_create(kSecDbMaxReaders
);
256 db
->write_semaphore
= dispatch_semaphore_create(kSecDbMaxWriters
);
257 db
->connections
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
258 db
->opened
= opened
? Block_copy(opened
) : NULL
;
259 if (getenv("__OSINSTALL_ENVIRONMENT") != NULL
) {
260 // TODO: Move this code out of this layer
261 secinfo("#SecDB", "SecDB: running from installer");
263 db
->db_path
= CFSTR("file::memory:?cache=shared");
265 db
->db_path
= CFStringCreateCopy(kCFAllocatorDefault
, dbName
);
268 db
->readWrite
= readWrite
;
269 db
->allowRepair
= allowRepair
;
271 db
->useRobotVacuum
= useRobotVacuum
;
272 db
->maxIdleHandles
= maxIdleHandles
;
273 db
->corruptionReset
= NULL
;
280 SecDbIdleConnectionCount(SecDbRef db
) {
281 __block CFIndex count
= 0;
282 dispatch_sync(db
->queue
, ^{
283 count
= CFArrayGetCount(db
->connections
);
288 void SecDbAddNotifyPhaseBlock(SecDbRef db
, SecDBNotifyBlock notifyPhase
)
290 // SecDbNotifyPhase seems to mostly be called on the db's commitQueue, and not the db's queue. Therefore, protect the array with that queue.
291 dispatch_sync(db
->commitQueue
, ^{
292 SecDBNotifyBlock block
= Block_copy(notifyPhase
); /* Force the block off the stack */
293 if (db
->notifyPhase
== NULL
) {
294 db
->notifyPhase
= CFArrayCreateMutableForCFTypes(NULL
);
296 CFArrayAppendValue(db
->notifyPhase
, block
);
297 Block_release(block
);
301 static void SecDbNotifyPhase(SecDbConnectionRef dbconn
, SecDbTransactionPhase phase
) {
302 if (CFArrayGetCount(dbconn
->changes
)) {
303 CFArrayRef changes
= dbconn
->changes
;
304 dbconn
->changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
305 if (dbconn
->db
->notifyPhase
) {
306 CFArrayForEach(dbconn
->db
->notifyPhase
, ^(const void *value
) {
307 SecDBNotifyBlock notifyBlock
= (SecDBNotifyBlock
)value
;
308 notifyBlock(dbconn
, phase
, dbconn
->source
, changes
);
311 CFReleaseSafe(changes
);
315 static void SecDbOnNotify(SecDbConnectionRef dbconn
, void (^perform
)(void)) {
319 CFStringRef
SecDbGetPath(SecDbRef db
) {
328 #pragma mark SecDbConnectionRef
330 static bool SecDbCheckCorrupted(SecDbConnectionRef dbconn
)
332 __block
bool checkDidRun
= false;
333 __block
bool isCorrupted
= false;
334 __block CFErrorRef error
= NULL
;
335 SecDbPrepare(dbconn
, CFSTR("PRAGMA integrity_check"), &error
, ^(sqlite3_stmt
*stmt
) {
336 SecDbStep(dbconn
, stmt
, &error
, ^(bool *stop
) {
337 const char * result
= (const char*)sqlite3_column_text(stmt
, 0);
338 if (!result
|| strncasecmp(result
, "ok", 3) != 0) {
340 secerror("SecDBCheckCorrupted integrity_check returned %s", (result
) ? result
: "NULL");
346 // An error occurred in SecDbPrepare before we could run the block.
348 CFIndex code
= CFErrorGetCode(error
);
349 if (SQLITE_CORRUPT
== code
|| SQLITE_NOTADB
== code
) {
352 secinfo("#SecDB", "#SecDB warning error %{public}@ when running integrity check", error
);
354 // We don't have an error ref if SecDbPrepare has called SecDbConnectionCheckCode,
355 // which then called SecDbHandleCorrupt. That code path is only entered when the
356 // original error was SQLITE_CORRUPT or SQLITE_NOTADB. On other errors, the
357 // CFErrorRef is not cleared and we can just check the code above.
359 secinfo("#SecDB", "#SecDB warning: failed to run integrity check due to corruption");
364 secerror("SecDBCheckCorrupted ran integrity_check, and that didn't return ok");
366 secerror("SecDBCheckCorrupted failed to run integrity check");
369 CFReleaseNull(error
);
374 static bool SecDbDidCreateFirstConnection(SecDbConnectionRef dbconn
, bool didCreate
, CFErrorRef
*error
)
376 secinfo("#SecDB", "#SecDB starting maintenance");
379 // Historical note: this used to check for integrity but that became too slow and caused panics at boot.
380 // Now, just react to SQLite errors when doing an operation. If file on disk is borked it'll tell us right away.
382 if (!dbconn
->isCorrupted
&& dbconn
->db
->opened
) {
383 CFErrorRef localError
= NULL
;
385 dbconn
->db
->callOpenedHandlerForNextConnection
= false;
386 ok
= dbconn
->db
->opened(dbconn
->db
, dbconn
, didCreate
, &dbconn
->db
->callOpenedHandlerForNextConnection
, &localError
);
389 secerror("opened block failed: %@", localError
);
391 if (!dbconn
->isCorrupted
&& error
&& *error
== NULL
) {
396 secerror("opened block failed: error (%@) is being released and lost", localError
);
397 CFReleaseNull(localError
);
401 if (dbconn
->isCorrupted
) {
402 ok
= SecDbHandleCorrupt(dbconn
, 0, error
);
405 secinfo("#SecDB", "#SecDB starting maintenance");
409 void SecDbCorrupt(SecDbConnectionRef dbconn
, CFErrorRef error
)
411 if (__security_simulatecrash_enabled()) {
412 os_log_fault(secLogObjForScope("SecEmergency"), "SecDBCorrupt: %@", error
);
414 dbconn
->isCorrupted
= true;
415 CFRetainAssign(dbconn
->corruptionError
, error
);
419 static uint8_t knownDbPathIndex(SecDbConnectionRef dbconn
)
422 if(CFEqual(dbconn
->db
->db_path
, CFSTR("/Library/Keychains/keychain-2.db")))
424 if(CFEqual(dbconn
->db
->db_path
, CFSTR("/Library/Keychains/ocspcache.sqlite3")))
426 if(CFEqual(dbconn
->db
->db_path
, CFSTR("/Library/Keychains/TrustStore.sqlite3")))
428 if(CFEqual(dbconn
->db
->db_path
, CFSTR("/Library/Keychains/caissuercache.sqlite3")))
431 /* Unknown DB path */
435 static bool SecDbConnectionCheckCode(SecDbConnectionRef dbconn
, int code
, CFErrorRef
*error
, CFStringRef desc
, ...)
436 CF_FORMAT_FUNCTION(4, 5);
438 // Return true if there was no error, returns false otherwise and set *error to an appropriate CFErrorRef.
439 static bool SecDbConnectionCheckCode(SecDbConnectionRef dbconn
, int code
, CFErrorRef
*error
, CFStringRef desc
, ...) {
440 if (code
== SQLITE_OK
|| code
== SQLITE_DONE
)
445 va_start(args
, desc
);
446 CFStringRef msg
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, desc
, args
);
448 SecDbErrorWithDb(code
, dbconn
->handle
, error
, CFSTR("%@"), msg
);
452 dbconn
->hasIOFailure
|= (SQLITE_IOERR
== code
);
454 /* If it's already corrupted, don't try to recover */
455 if (dbconn
->isCorrupted
) {
456 CFStringRef reason
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
457 CFSTR("SQL DB %@ is corrupted already. Corruption error was: %d (previously %d)"),
458 dbconn
->db
->db_path
, code
, dbconn
->maybeCorruptedCode
);
459 secerror("%@",reason
);
460 __security_simulatecrash(reason
, __sec_exception_code_TwiceCorruptDb(knownDbPathIndex(dbconn
)));
461 CFReleaseSafe(reason
);
462 // We can't fall through to the checking case because it eventually calls SecDbConnectionCheckCode again.
463 // However, this is the second time we're seeing corruption so let's take the ultimate measure.
464 if ((SQLITE_CORRUPT
== code
) || (SQLITE_NOTADB
== code
)) {
465 secerror("SecDbConnectionCheckCode detected corruption twice: going to handle corrupt DB");
466 (void)SecDbHandleCorrupt(dbconn
, code
, error
);
471 // NOTADB means file is garbage, so it's functionally equivalent to corruption
472 dbconn
->isCorrupted
= (SQLITE_CORRUPT
== code
) || (SQLITE_NOTADB
== code
);
473 if (dbconn
->isCorrupted
) {
474 /* Run integrity check and only make dbconn->isCorrupted true and
475 run the corruption handler if the integrity check conclusively fails. */
476 dbconn
->maybeCorruptedCode
= code
;
477 dbconn
->isCorrupted
= SecDbCheckCorrupted(dbconn
);
478 if (dbconn
->isCorrupted
) {
479 secerror("operation returned code: %d integrity check=fail", code
);
480 (void)SecDbHandleCorrupt(dbconn
, code
, error
);
482 secerror("operation returned code: %d: integrity check=pass", code
);
489 #define BUSY_TIMEOUT_MS (5 * 60 * 1000) /* 5 minutes */
491 static int sleepBackoff
[] = { 10, 20, 50, 100, 250 };
492 static int sumBackoff
[] = { 10, 30, 80, 180, 430 };
493 static int NumberOfSleepBackoff
= sizeof(sleepBackoff
)/sizeof(sleepBackoff
[0]);
495 // Use these as silly hacks to encode the SQLite return code in the backtrace, for hang debugging purposes
496 static void __attribute__((noinline
)) SecDbLockSleep(int ms
) {
500 static void __attribute__((noinline
)) SecDbBusySleep(int ms
) {
504 // Return true causes the operation to be tried again.
505 // Note that we set sqlite3_busy_timeout on the connection, so anytime you're in here, it's likely due to SQLITE_LOCKED.
506 static bool SecDbWaitIfNeeded(SecDbConnectionRef dbconn
, int s3e
, sqlite3_stmt
*stmt
, CFStringRef desc
, int nTries
, CFErrorRef
*error
) {
507 if (((0xFF & s3e
) == SQLITE_BUSY
) || ((0xFF & s3e
) == SQLITE_LOCKED
)) {
508 int totaltimeout
, timeout
;
510 _Static_assert(sizeof(sumBackoff
) == sizeof(sleepBackoff
), "matching arrays not matching");
511 _Static_assert(sizeof(sumBackoff
[0]) == sizeof(sleepBackoff
[0]), "matching arrays not matching");
513 if (nTries
< NumberOfSleepBackoff
) {
514 timeout
= sleepBackoff
[nTries
];
515 totaltimeout
= sumBackoff
[nTries
];
517 timeout
= sleepBackoff
[NumberOfSleepBackoff
- 1];
518 totaltimeout
= sumBackoff
[NumberOfSleepBackoff
- 1] + (timeout
* (nTries
- NumberOfSleepBackoff
));
520 if (totaltimeout
< BUSY_TIMEOUT_MS
) {
521 secinfo("#SecDB", "sqlite busy/locked: %d ntries: %d totaltimeout: %d", s3e
, nTries
, totaltimeout
);
522 if(((0xFF & s3e
) == SQLITE_LOCKED
)) {
523 SecDbLockSleep(timeout
);
525 SecDbBusySleep(timeout
);
529 secinfo("#SecDB", "sqlite busy/locked: too long: %d ms, giving up", totaltimeout
);
533 return SecDbConnectionCheckCode(dbconn
, s3e
, error
, CFSTR("%@"), desc
);
536 enum SecDbStepResult
{
541 typedef enum SecDbStepResult SecDbStepResult
;
543 static SecDbStepResult
_SecDbStep(SecDbConnectionRef dbconn
, sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
544 assert(stmt
!= NULL
);
548 if (SecDbConnectionIsReadOnly(dbconn
) && !sqlite3_stmt_readonly(stmt
)) {
549 secerror("_SecDbStep: SecDbConnection is readonly but we're about to write: %s", sqlite3_sql(stmt
));
551 s3e
= sqlite3_step(stmt
);
552 if (s3e
== SQLITE_ROW
) {
553 return kSecDbRowStep
;
554 } else if (s3e
== SQLITE_DONE
) {
556 ** ^[SQLITE_DONE] means that the statement has finished executing
557 ** successfully. sqlite3_step() should not be called again on this virtual
558 ** machine without first calling [] to reset the virtual
559 ** machine back to its initial state.
562 return kSecDbDoneStep
;
563 } else if (!SecDbWaitIfNeeded(dbconn
, s3e
, stmt
, CFSTR("step"), ntries
, error
)) {
564 return kSecDbErrorStep
;
571 SecDbExec(SecDbConnectionRef dbconn
, CFStringRef sql
, CFErrorRef
*error
)
576 CFStringRef tail
= NULL
;
578 sqlite3_stmt
*stmt
= SecDbCopyStmt(dbconn
, sql
, &tail
, error
);
582 while ((sr
= _SecDbStep(dbconn
, stmt
, error
)) == kSecDbRowStep
);
583 if (sr
== kSecDbErrorStep
)
585 ok
&= SecDbReleaseCachedStmt(dbconn
, sql
, stmt
, error
);
588 // TODO We already have an error here we really just want the left over sql in it's userData
589 ok
= SecDbError(SQLITE_ERROR
, error
, CFSTR("Error with unexecuted sql remaining %@"), sql
);
597 static int SecDBGetInteger(SecDbConnectionRef dbconn
, CFStringRef sql
)
599 __block
int number
= -1;
600 __block CFErrorRef error
= NULL
;
602 (void)SecDbWithSQL(dbconn
, sql
, &error
, ^bool(sqlite3_stmt
*sqlStmt
) {
603 (void)SecDbStep(dbconn
, sqlStmt
, &error
, ^(bool *stop
) {
604 number
= sqlite3_column_int(sqlStmt
, 0);
609 CFReleaseNull(error
);
614 void SecDBManagementTasks(SecDbConnectionRef dbconn
)
616 int64_t page_count
= SecDBGetInteger(dbconn
, CFSTR("pragma page_count"));
617 if (page_count
<= 0) {
620 int64_t free_count
= SecDBGetInteger(dbconn
, CFSTR("pragma freelist_count"));
621 if (free_count
< 0) {
625 int64_t max_free
= 8192;
627 int64_t pages_in_use
= page_count
- free_count
;
628 double loadFactor
= ((double)pages_in_use
/(double)page_count
);
629 if (0.85 < loadFactor
&& free_count
< max_free
) {
632 int64_t pages_to_free
= (int64_t)(0.2 * free_count
);
633 if (0.4 > loadFactor
) {
634 pages_to_free
= free_count
;
637 char *formatString
= NULL
;
638 asprintf(&formatString
, "pragma incremental_vacuum(%d)", (int)pages_to_free
);
640 char *sqlerror
= NULL
;
641 int rc
= sqlite3_exec(dbconn
->handle
, formatString
, NULL
, NULL
, &sqlerror
);
643 secerror("incremental_vacuum failed with: (%d) %{public}s", rc
, sqlerror
);
645 sqlite3_free(sqlerror
);
652 static bool SecDbBeginTransaction(SecDbConnectionRef dbconn
, SecDbTransactionType type
, CFErrorRef
*error
)
657 case kSecDbImmediateTransactionType
:
658 secdebug("db", "SecDbBeginTransaction SecDbBeginTransaction %p", dbconn
);
659 query
= CFSTR("BEGIN IMMEDIATE");
661 case kSecDbExclusiveRemoteSOSTransactionType
:
662 secdebug("db", "SecDbBeginTransaction kSecDbExclusiveRemoteSOSTransactionType %p", dbconn
);
663 dbconn
->source
= kSecDbSOSTransaction
;
664 query
= CFSTR("BEGIN EXCLUSIVE");
666 case kSecDbExclusiveRemoteCKKSTransactionType
:
667 secdebug("db", "SecDbBeginTransaction kSecDbExclusiveRemoteCKKSTransactionType %p", dbconn
);
668 dbconn
->source
= kSecDbCKKSTransaction
;
669 query
= CFSTR("BEGIN EXCLUSIVE");
671 case kSecDbExclusiveTransactionType
:
672 if (type
==kSecDbExclusiveTransactionType
)
673 secdebug("db", "SecDbBeginTransaction kSecDbExclusiveTransactionType %p", dbconn
);
674 query
= CFSTR("BEGIN EXCLUSIVE");
676 case kSecDbNormalTransactionType
:
677 secdebug("db", "SecDbBeginTransaction kSecDbNormalTransactionType %p", dbconn
);
678 query
= CFSTR("BEGIN");
681 secdebug("db", "SecDbBeginTransaction invalid transaction type %lu", type
);
682 ok
= SecDbError(SQLITE_ERROR
, error
, CFSTR("invalid transaction type %d"), (int)type
);
687 if (query
!= NULL
&& sqlite3_get_autocommit(dbconn
->handle
) != 0) {
688 ok
= SecDbExec(dbconn
, query
, error
);
691 dbconn
->inTransaction
= true;
696 static bool SecDbEndTransaction(SecDbConnectionRef dbconn
, bool commit
, CFErrorRef
*error
)
698 __block
bool ok
= true;
699 __block
bool commited
= false;
701 dispatch_block_t notifyAndExec
= ^{
703 //secdebug("db", "SecDbEndTransaction kSecDbTransactionWillCommit %p", dbconn);
704 SecDbNotifyPhase(dbconn
, kSecDbTransactionWillCommit
);
705 commited
= ok
= SecDbExec(dbconn
, CFSTR("END"), error
);
706 //secdebug("db", "SecDbEndTransaction kSecDbTransactionWillCommit %p (after notify)", dbconn);
708 ok
= SecDbExec(dbconn
, CFSTR("ROLLBACK"), error
);
711 dbconn
->inTransaction
= false;
712 SecDbNotifyPhase(dbconn
, commited
? kSecDbTransactionDidCommit
: kSecDbTransactionDidRollback
);
713 secdebug("db", "SecDbEndTransaction %s %p", commited
? "kSecDbTransactionDidCommit" : "kSecDbTransactionDidRollback", dbconn
);
714 dbconn
->source
= kSecDbAPITransaction
;
716 if (commit
&& dbconn
->db
->useRobotVacuum
) {
717 SecDBManagementTasks(dbconn
);
721 SecDbPerformOnCommitQueue(dbconn
, true, notifyAndExec
);
726 bool SecDbTransaction(SecDbConnectionRef dbconn
, SecDbTransactionType type
,
727 CFErrorRef
*error
, void (^transaction
)(bool *commit
))
732 if (dbconn
->inTransaction
) {
733 transaction(&commit
);
735 secinfo("#SecDB", "#SecDB nested transaction asked to not be committed");
738 ok
= SecDbBeginTransaction(dbconn
, type
, error
);
740 transaction(&commit
);
741 ok
= SecDbEndTransaction(dbconn
, commit
, error
);
748 sqlite3
*SecDbHandle(SecDbConnectionRef dbconn
) {
749 return dbconn
->handle
;
752 bool SecDbStep(SecDbConnectionRef dbconn
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, void (^row
)(bool *stop
)) {
754 switch (_SecDbStep(dbconn
, stmt
, error
)) {
755 case kSecDbErrorStep
:
756 secdebug("db", "kSecDbErrorStep %@", error
? *error
: NULL
);
760 secdebug("db", "kSecDbRowStep %@", error
? *error
: NULL
);
763 __block
bool stop
= false;
764 SecAutoreleaseInvokeWithPool(^{
771 SecDbError(SQLITE_ERROR
, error
, CFSTR("SecDbStep SQLITE_ROW returned without a row handler"));
775 secdebug("db", "kSecDbDoneStep %@", error
? *error
: NULL
);
782 bool SecDbCheckpoint(SecDbConnectionRef dbconn
, CFErrorRef
*error
)
784 return SecDbConnectionCheckCode(dbconn
, sqlite3_wal_checkpoint(dbconn
->handle
, NULL
), error
, CFSTR("wal_checkpoint"));
787 static bool SecDbFileControl(SecDbConnectionRef dbconn
, int op
, void *arg
, CFErrorRef
*error
) {
788 return SecDbConnectionCheckCode(dbconn
, sqlite3_file_control(dbconn
->handle
, NULL
, op
, arg
), error
, CFSTR("file_control"));
791 static sqlite3
*_SecDbOpenV2(const char *path
,
796 sqlite3
*handle
= NULL
;
797 int s3e
= sqlite3_open_v2(path
, &handle
, flags
, NULL
);
800 SecDbErrorWithDb(s3e
, handle
, error
, CFSTR("open_v2 \"%s\" 0x%X"), path
, flags
);
801 sqlite3_close(handle
);
804 SecDbError(s3e
, error
, CFSTR("open_v2 \"%s\" 0x%X"), path
, flags
);
806 } else if (SQLITE_OPEN_READWRITE
== (flags
& SQLITE_OPEN_READWRITE
)) {
807 if (useRobotVacuum
) {
808 #define SECDB_SQLITE_AUTO_VACUUM_INCREMENTAL 2
809 sqlite3_stmt
*stmt
= NULL
;
813 * Setting auto_vacuum = incremental on a database that is not empty requires
814 * a VACCUUM, so check if the vacuum mode is not INCREMENTAL, and if its not,
815 * set it to incremental and vacuum.
818 s3e
= sqlite3_prepare_v2(handle
, "PRAGMA auto_vacuum", -1, &stmt
, NULL
);
820 s3e
= sqlite3_step(stmt
);
821 if (s3e
== SQLITE_ROW
) {
822 vacuumMode
= sqlite3_column_int(stmt
, 0);
824 (void)sqlite3_finalize(stmt
);
827 if (vacuumMode
!= SECDB_SQLITE_AUTO_VACUUM_INCREMENTAL
) {
828 (void)sqlite3_exec(handle
, "PRAGMA auto_vacuum = incremental", NULL
, NULL
, NULL
);
829 (void)sqlite3_exec(handle
, "VACUUM", NULL
, NULL
, NULL
);
833 (void)sqlite3_exec(handle
, "PRAGMA journal_mode = WAL", NULL
, NULL
, NULL
);
836 // Let SQLite handle timeouts.
837 sqlite3_busy_timeout(handle
, 5*1000);
842 static bool SecDbOpenV2(SecDbConnectionRef dbconn
, const char *path
, int flags
, CFErrorRef
*error
) {
843 return (dbconn
->handle
= _SecDbOpenV2(path
, flags
, dbconn
->db
->useWAL
, dbconn
->db
->useRobotVacuum
, error
)) != NULL
;
846 // This construction lets tests not exit here
847 static void SecDbProductionCorruptionExitHandler(void)
851 void (*SecDbCorruptionExitHandler
)(void) = SecDbProductionCorruptionExitHandler
;
853 void SecDbResetCorruptionExitHandler(void)
855 SecDbCorruptionExitHandler
= SecDbProductionCorruptionExitHandler
;
859 There's not much to do in here because we should only ever be here when
860 SQLite tells us the DB is corrupt, or the DB is unrecoverable because of
861 some fatal logic problem. But we can't shoot it dead either due to client
862 connections. So, first we create a marker to tell ourselves things are bad,
863 then we'll die. When we come back up we'll notice the marker and remove the DB.
865 static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn
, int rc
, CFErrorRef
*error
)
867 if (!dbconn
->db
->allowRepair
) {
868 SecCFCreateErrorWithFormat(rc
, kSecErrnoDomain
, NULL
, error
, NULL
,
869 CFSTR("SecDbHandleCorrupt not allowed to repair, handled error: [%d] %s"), rc
, strerror(rc
));
870 dbconn
->isCorrupted
= false;
874 CFStringPerformWithCString(dbconn
->db
->db_path
, ^(const char *db_path
) {
875 char marker
[PATH_MAX
+1];
876 snprintf(marker
, sizeof(marker
), "%s-iscorrupt", db_path
);
877 struct stat info
= {};
878 if (0 == stat(marker
, &info
)) {
879 secerror("SecDbHandleCorrupt: Tried to write corruption marker %s but one already exists", marker
);
882 FILE* file
= fopen(marker
, "w");
884 secerror("SecDbHandleCorrupt: Unable (%{darwin.errno}d) to create corruption marker %{public}s", errno
, marker
);
890 secwarning("SecDbHandleCorrupt: killing self so that successor might cleanly delete corrupt db");
892 // Call through function pointer so tests can replace it and call a SecKeychainDbReset instead
893 SecDbCorruptionExitHandler();
897 static bool SecDbProcessCorruptionMarker(CFStringRef db_path
) {
898 __block
bool ok
= true;
899 CFStringPerformWithCString(db_path
, ^(const char *db_path
) {
900 char marker
[PATH_MAX
+1];
901 snprintf(marker
, sizeof(marker
), "%s-iscorrupt", db_path
);
902 struct stat info
= {};
903 int result
= stat(marker
, &info
);
904 if (result
!= 0 && errno
== ENOENT
) {
906 } else if (result
!= 0) {
907 secerror("SecDbSecDbProcessCorruptionMarker: Unable to check for corruption marker: %{darwin.errno}d", errno
);
911 secwarning("SecDbSecDbProcessCorruptionMarker: found corruption marker %s", marker
);
912 if (remove(marker
)) {
913 secerror("SecDbSecDbProcessCorruptionMarker: Unable (%{darwin.errno}d) to delete corruption marker", errno
);
915 } else if (remove(db_path
) && errno
!= ENOENT
) { // Not sure how we'd get ENOENT but it would suit us just fine
916 secerror("SecDbSecDbProcessCorruptionMarker: Unable (%{darwin.errno}d) to delete db %{public}s", errno
, db_path
);
919 secwarning("SecDbSecDbProcessCorruptionMarker: deleted corrupt db %{public}s", db_path
);
926 SecDbSetCorruptionReset(SecDbRef db
, void (^corruptionReset
)(void))
928 if (db
->corruptionReset
) {
929 Block_release(db
->corruptionReset
);
930 db
->corruptionReset
= NULL
;
932 if (corruptionReset
) {
933 db
->corruptionReset
= Block_copy(corruptionReset
);
937 static bool SecDbLoggingEnabled(CFStringRef type
)
939 CFTypeRef profile
= NULL
;
940 bool enabled
= false;
942 if (csr_check(CSR_ALLOW_APPLE_INTERNAL
) != 0)
945 profile
= (CFNumberRef
)CFPreferencesCopyValue(CFSTR("SQLProfile"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesAnyHost
);
950 if (CFGetTypeID(profile
) == CFBooleanGetTypeID()) {
951 enabled
= CFBooleanGetValue((CFBooleanRef
)profile
);
952 } else if (CFGetTypeID(profile
) == CFNumberGetTypeID()) {
954 CFNumberGetValue(profile
, kCFNumberSInt32Type
, &num
);
958 CFReleaseSafe(profile
);
964 SecDbProfileMask(void)
966 static dispatch_once_t onceToken
;
967 static unsigned profile_mask
= 0;
969 // sudo defaults write /Library/Preferences/com.apple.security SQLProfile -bool true
970 dispatch_once(&onceToken
, ^{
971 if (SecDbLoggingEnabled(CFSTR("SQLProfile")))
972 profile_mask
= SQLITE_TRACE_PROFILE
;
974 profile_mask
|= SQLITE_TRACE_STMT
;
976 if (SecDbLoggingEnabled(CFSTR("SQLTrace")))
977 profile_mask
= SQLITE_TRACE_STMT
;
979 if (SecDbLoggingEnabled(CFSTR("SQLRow")))
980 profile_mask
= SQLITE_TRACE_ROW
;
981 secinfo("#SecDB", "sqlDb: sql trace mask: 0x%08x", profile_mask
);
987 SecDbTraceV2(unsigned mask
, void *ctx
, void *p
, void *x
) {
988 SecDbConnectionRef dbconn __unused
= ctx
;
989 const char *trace
= "unknown";
991 if (mask
== SQLITE_TRACE_PROFILE
)
992 trace
= sqlite3_sql(p
);
993 else if (mask
== SQLITE_TRACE_STMT
) {
994 trace
= sqlite3_sql(p
);
995 } else if (mask
== SQLITE_TRACE_ROW
) {
996 trace
= sqlite3_expanded_sql(p
);
1000 secinfo("#SecDB", "#SecDB %{public}s", trace
);
1006 static bool SecDbOpenHandle(SecDbConnectionRef dbconn
, bool *created
, CFErrorRef
*error
)
1008 __block
bool ok
= true;
1010 // This is pretty terrible because now what? We know we have a corrupt DB
1011 // and now we can't get rid of it.
1012 if (!SecDbProcessCorruptionMarker(dbconn
->db
->db_path
)) {
1013 SecCFCreateErrorWithFormat(errno
, kSecErrnoDomain
, NULL
, error
, NULL
, CFSTR("Unable to process corruption marker: %{darwin.errno}d"), errno
);
1017 CFStringPerformWithCString(dbconn
->db
->db_path
, ^(const char *db_path
) {
1018 int flags
= (dbconn
->db
->readWrite
) ? SQLITE_OPEN_READWRITE
: SQLITE_OPEN_READONLY
;
1019 ok
= created
&& SecDbOpenV2(dbconn
, db_path
, flags
, NULL
);
1023 char *tmp
= dirname((char *)db_path
);
1025 mode_t omode
= dbconn
->db
->mode
;
1026 if (omode
& S_IRUSR
) { omode
|= S_IXUSR
; } // owner can read
1027 if (omode
& S_IRGRP
) { omode
|= S_IXGRP
; } // group can read
1028 if (omode
& S_IROTH
) { omode
|= S_IXOTH
; } // other can read
1029 int errnum
= mkpath_np(tmp
, omode
);
1030 if (errnum
!= 0 && errnum
!= EEXIST
) {
1031 SecCFCreateErrorWithFormat(errnum
, kSecErrnoDomain
, NULL
, error
, NULL
,
1032 CFSTR("mkpath_np %s: [%d] %s"), tmp
, errnum
, strerror(errnum
));
1037 // if the enclosing directory is ok, try to create the database.
1038 // this forces us to open it read-write, so we'll need to be the owner here.
1039 ok
= ok
&& SecDbOpenV2(dbconn
, db_path
, SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE
, error
);
1041 chmod(db_path
, dbconn
->db
->mode
); // default: 0600 (S_IRUSR | S_IWUSR)
1048 unsigned mask
= SecDbProfileMask();
1050 (void)sqlite3_trace_v2(dbconn
->handle
,
1061 static SecDbConnectionRef
1062 SecDbConnectionCreate(SecDbRef db
, bool readOnly
, CFErrorRef
*error
)
1064 SecDbConnectionRef dbconn
= NULL
;
1066 dbconn
= CFTypeAllocate(SecDbConnection
, struct __OpaqueSecDbConnection
, kCFAllocatorDefault
);
1067 require(dbconn
!= NULL
, done
);
1070 dbconn
->readOnly
= readOnly
;
1071 dbconn
->inTransaction
= false;
1072 dbconn
->source
= kSecDbInvalidTransaction
;
1073 dbconn
->isCorrupted
= false;
1074 dbconn
->maybeCorruptedCode
= 0;
1075 dbconn
->hasIOFailure
= false;
1076 dbconn
->corruptionError
= NULL
;
1077 dbconn
->handle
= NULL
;
1078 dbconn
->changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1084 bool SecDbConnectionIsReadOnly(SecDbConnectionRef dbconn
) {
1085 return dbconn
->readOnly
;
1088 static void SecDbConectionSetReadOnly(SecDbConnectionRef dbconn
, bool readOnly
) {
1089 dbconn
->readOnly
= readOnly
;
1092 /* Read only connections go to the end of the queue, writeable connections
1093 go to the start of the queue. */
1094 SecDbConnectionRef
SecDbConnectionAcquire(SecDbRef db
, bool readOnly
, CFErrorRef
*error
) {
1095 SecDbConnectionRef dbconn
= NULL
;
1096 SecDbConnectionAcquireRefMigrationSafe(db
, readOnly
, &dbconn
, error
);
1100 bool SecDbConnectionAcquireRefMigrationSafe(SecDbRef db
, bool readOnly
, SecDbConnectionRef
* dbconnRef
, CFErrorRef
*error
)
1104 secinfo("dbconn", "acquire %s connection", readOnly
? "ro" : "rw");
1106 dispatch_semaphore_wait(readOnly
? db
->read_semaphore
: db
->write_semaphore
, DISPATCH_TIME_FOREVER
);
1107 __block SecDbConnectionRef dbconn
= NULL
;
1108 __block
bool ok
= true;
1109 __block
bool ranOpenedHandler
= false;
1111 bool (^assignDbConn
)(SecDbConnectionRef
) = ^bool(SecDbConnectionRef connection
) {
1112 dbconn
= connection
;
1114 *dbconnRef
= connection
;
1117 return dbconn
!= NULL
;
1120 dispatch_sync(db
->queue
, ^{
1121 if (!db
->didFirstOpen
) {
1122 bool didCreate
= false;
1123 ok
= assignDbConn(SecDbConnectionCreate(db
, false, error
));
1124 CFErrorRef localError
= NULL
;
1125 if (ok
&& !SecDbOpenHandle(dbconn
, &didCreate
, &localError
)) {
1126 secerror("Unable to create database: %@", localError
);
1127 if (localError
&& CFEqual(CFErrorGetDomain(localError
), kSecDbErrorDomain
)) {
1128 int code
= (int)CFErrorGetCode(localError
);
1129 dbconn
->isCorrupted
= (SQLITE_CORRUPT
== code
) || (SQLITE_NOTADB
== code
);
1131 // If the open failure isn't due to corruption, propagate the error.
1132 ok
= dbconn
->isCorrupted
;
1133 if (!ok
&& error
&& *error
== NULL
) {
1134 *error
= localError
;
1138 CFReleaseNull(localError
);
1141 db
->didFirstOpen
= ok
= SecDbDidCreateFirstConnection(dbconn
, didCreate
, error
);
1142 ranOpenedHandler
= true;
1145 CFReleaseNull(dbconn
);
1147 /* Try to get one from the cache */
1148 CFIndex count
= CFArrayGetCount(db
->connections
);
1149 while (count
&& !dbconn
) {
1150 CFIndex ix
= readOnly
? count
- 1 : 0;
1151 if (assignDbConn((SecDbConnectionRef
)CFArrayGetValueAtIndex(db
->connections
, ix
)))
1152 CFRetainSafe(dbconn
);
1154 secerror("got NULL dbconn at index: %" PRIdCFIndex
" skipping", ix
);
1155 CFArrayRemoveValueAtIndex(db
->connections
, ix
);
1161 /* Make sure the connection we found has the right access */
1162 if (SecDbConnectionIsReadOnly(dbconn
) != readOnly
) {
1163 SecDbConectionSetReadOnly(dbconn
, readOnly
);
1166 /* Nothing found in cache, create a new connection */
1167 bool created
= false;
1168 if (assignDbConn(SecDbConnectionCreate(db
, readOnly
, error
)) && !SecDbOpenHandle(dbconn
, &created
, error
)) {
1169 CFReleaseNull(dbconn
);
1174 if (dbconn
&& !ranOpenedHandler
&& dbconn
->db
->opened
) {
1175 dispatch_sync(db
->queue
, ^{
1176 if (dbconn
->db
->callOpenedHandlerForNextConnection
) {
1177 dbconn
->db
->callOpenedHandlerForNextConnection
= false;
1178 if (!dbconn
->db
->opened(db
, dbconn
, false, &dbconn
->db
->callOpenedHandlerForNextConnection
, error
)) {
1179 if (!dbconn
->isCorrupted
|| !SecDbHandleCorrupt(dbconn
, 0, error
)) {
1180 CFReleaseNull(dbconn
);
1188 *dbconnRef
= dbconn
;
1192 // If acquire fails we need to signal the semaphore again.
1193 dispatch_semaphore_signal(readOnly
? db
->read_semaphore
: db
->write_semaphore
);
1197 return dbconn
? true : false;
1200 void SecDbConnectionRelease(SecDbConnectionRef dbconn
) {
1202 secerror("called with NULL dbconn");
1205 SecDbRef db
= dbconn
->db
;
1207 secinfo("dbconn", "release %@", dbconn
);
1209 dispatch_sync(db
->queue
, ^{
1210 bool readOnly
= SecDbConnectionIsReadOnly(dbconn
);
1211 if (dbconn
->hasIOFailure
) {
1212 // Something wrong on the file layer (e.g. revoked file descriptor for networked home)
1213 // so we don't trust our existing connections anymore.
1214 CFArrayRemoveAllValues(db
->connections
);
1216 CFIndex count
= CFArrayGetCount(db
->connections
);
1217 // Add back possible writable dbconn to the pool.
1218 CFArrayInsertValueAtIndex(db
->connections
, readOnly
? count
: 0, dbconn
);
1219 // Remove the last (probably read-only) dbconn from the pool.
1220 if (count
>= db
->maxIdleHandles
) {
1221 CFArrayRemoveValueAtIndex(db
->connections
, count
);
1224 // Signal after we have put the connection back in the pool of connections
1225 dispatch_semaphore_signal(readOnly
? db
->read_semaphore
: db
->write_semaphore
);
1231 void SecDbReleaseAllConnections(SecDbRef db
) {
1232 // Force all connections to be removed (e.g. file descriptor no longer valid)
1234 secerror("called with NULL db");
1237 dispatch_sync(db
->queue
, ^{
1238 CFArrayRemoveAllValues(db
->connections
);
1239 dispatch_semaphore_signal(db
->write_semaphore
);
1240 dispatch_semaphore_signal(db
->read_semaphore
);
1244 // Please make sure you want to do this. Any use of the outstanding connections to this DB will cause a crash.
1245 void SecDbForceClose(SecDbRef db
) {
1246 dispatch_sync(db
->queue
, ^{
1247 CFArrayForEach(db
->connections
, ^(const void* p
) {
1248 SecDbConnectionRef connection
= (SecDbConnectionRef
)p
;
1250 // this pointer is claimed to be nonretained
1251 connection
->db
= NULL
;
1253 if(connection
->handle
) {
1254 sqlite3_close(connection
->handle
);
1255 connection
->handle
= NULL
;
1258 CFArrayRemoveAllValues(db
->connections
);
1259 dispatch_semaphore_signal(db
->write_semaphore
);
1260 dispatch_semaphore_signal(db
->read_semaphore
);
1264 bool SecDbPerformRead(SecDbRef db
, CFErrorRef
*error
, void (^perform
)(SecDbConnectionRef dbconn
)) {
1265 SecDbConnectionRef dbconn
= SecDbConnectionAcquire(db
, true, error
);
1266 bool success
= false;
1270 SecDbConnectionRelease(dbconn
);
1275 bool SecDbPerformWrite(SecDbRef db
, CFErrorRef
*error
, void (^perform
)(SecDbConnectionRef dbconn
)) {
1277 SecError(errSecNotAvailable
, error
, CFSTR("failed to get a db handle"));
1280 SecDbConnectionRef dbconn
= SecDbConnectionAcquire(db
, false, error
);
1281 bool success
= false;
1285 SecDbConnectionRelease(dbconn
);
1291 SecDbConnectionCopyFormatDescription(CFTypeRef value
, CFDictionaryRef formatOptions
)
1293 SecDbConnectionRef dbconn
= (SecDbConnectionRef
)value
;
1294 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<SecDbConnection %s %s>"),
1295 dbconn
->readOnly
? "ro" : "rw", dbconn
->handle
? "open" : "closed");
1299 SecDbConnectionDestroy(CFTypeRef value
)
1301 SecDbConnectionRef dbconn
= (SecDbConnectionRef
)value
;
1302 if (dbconn
->handle
) {
1303 int s3e
= sqlite3_close(dbconn
->handle
);
1304 if (s3e
!= SQLITE_OK
) {
1305 secerror("failed to close database connection (%d) for %@: %s", s3e
, dbconn
->db
->db_path
, sqlite3_errmsg(dbconn
->handle
));
1307 os_assert(s3e
== SQLITE_OK
); // Crash now or jetsam later
1310 CFReleaseNull(dbconn
->changes
);
1311 CFReleaseNull(dbconn
->corruptionError
);
1315 void SecDbPerformOnCommitQueue(SecDbConnectionRef dbconn
, bool barrier
, dispatch_block_t perform
) {
1317 dispatch_barrier_sync(dbconn
->db
->commitQueue
, ^{
1321 dispatch_sync(dbconn
->db
->commitQueue
, ^{
1328 // MARK: Bind helpers
1330 // Logging binds is very spammy when debug logging is on (~90% of log lines), and isn't often useful.
1331 // Enable this in your local build if you actually want every single SQL variable bind logged for debugging.
1332 #define LOG_SECDB_BINDS 0
1334 bool SecDbBindBlob(sqlite3_stmt
*stmt
, int param
, const void *zData
, size_t n
, void(*xDel
)(void*), CFErrorRef
*error
) {
1336 return SecDbErrorWithStmt(SQLITE_TOOBIG
, stmt
, error
,
1337 CFSTR("bind_blob[%d]: blob bigger than INT_MAX"), param
);
1339 bool ok
= SecDbErrorWithStmt(sqlite3_bind_blob(stmt
, param
, zData
, (int)n
, xDel
),
1340 stmt
, error
, CFSTR("bind_blob[%d]"), param
);
1342 secinfo("bind", "bind_blob[%d]: %.*P: %@", param
, (int)n
, zData
, error
? *error
: NULL
);
1347 bool SecDbBindText(sqlite3_stmt
*stmt
, int param
, const char *zData
, size_t n
, void(*xDel
)(void*), CFErrorRef
*error
) {
1349 return SecDbErrorWithStmt(SQLITE_TOOBIG
, stmt
, error
,
1350 CFSTR("bind_text[%d]: text bigger than INT_MAX"), param
);
1352 bool ok
= SecDbErrorWithStmt(sqlite3_bind_text(stmt
, param
, zData
, (int)n
, xDel
), stmt
, error
,
1353 CFSTR("bind_text[%d]"), param
);
1355 secinfo("bind", "bind_text[%d]: \"%s\" error: %@", param
, zData
, error
? *error
: NULL
);
1360 bool SecDbBindDouble(sqlite3_stmt
*stmt
, int param
, double value
, CFErrorRef
*error
) {
1361 bool ok
= SecDbErrorWithStmt(sqlite3_bind_double(stmt
, param
, value
), stmt
, error
,
1362 CFSTR("bind_double[%d]"), param
);
1364 secinfo("bind", "bind_double[%d]: %f error: %@", param
, value
, error
? *error
: NULL
);
1369 bool SecDbBindInt(sqlite3_stmt
*stmt
, int param
, int value
, CFErrorRef
*error
) {
1370 bool ok
= SecDbErrorWithStmt(sqlite3_bind_int(stmt
, param
, value
), stmt
, error
,
1371 CFSTR("bind_int[%d]"), param
);
1373 secinfo("bind", "bind_int[%d]: %d error: %@", param
, value
, error
? *error
: NULL
);
1378 bool SecDbBindInt64(sqlite3_stmt
*stmt
, int param
, sqlite3_int64 value
, CFErrorRef
*error
) {
1379 bool ok
= SecDbErrorWithStmt(sqlite3_bind_int64(stmt
, param
, value
), stmt
, error
,
1380 CFSTR("bind_int64[%d]"), param
);
1382 secinfo("bind", "bind_int64[%d]: %lld error: %@", param
, value
, error
? *error
: NULL
);
1388 /* AUDIT[securityd](done):
1389 value (ok) is a caller provided, non NULL CFTypeRef.
1391 bool SecDbBindObject(sqlite3_stmt
*stmt
, int param
, CFTypeRef value
, CFErrorRef
*error
) {
1393 __block
bool result
= false;
1395 /* TODO: Can we use SQLITE_STATIC below everwhere we currently use
1396 SQLITE_TRANSIENT since we finalize the statement before the value
1397 goes out of scope? */
1398 if (!value
|| (valueId
= CFGetTypeID(value
)) == CFNullGetTypeID()) {
1399 /* Skip bindings for NULL values. sqlite3 will interpret unbound
1400 params as NULL which is exactly what we want. */
1402 } else if (valueId
== CFStringGetTypeID()) {
1403 CFStringPerformWithCStringAndLength(value
, ^(const char *cstr
, size_t clen
) {
1404 result
= SecDbBindText(stmt
, param
, cstr
, clen
, SQLITE_TRANSIENT
, error
);
1406 } else if (valueId
== CFDataGetTypeID()) {
1407 CFIndex len
= CFDataGetLength(value
);
1409 result
= SecDbBindBlob(stmt
, param
, CFDataGetBytePtr(value
),
1410 len
, SQLITE_TRANSIENT
, error
);
1412 result
= SecDbBindText(stmt
, param
, "", 0, SQLITE_TRANSIENT
, error
);
1414 } else if (valueId
== CFDateGetTypeID()) {
1415 CFAbsoluteTime abs_time
= CFDateGetAbsoluteTime(value
);
1416 result
= SecDbBindDouble(stmt
, param
, abs_time
, error
);
1417 } else if (valueId
== CFBooleanGetTypeID()) {
1418 int bval
= CFBooleanGetValue(value
);
1419 result
= SecDbBindInt(stmt
, param
, bval
, error
);
1420 } else if (valueId
== CFNumberGetTypeID()) {
1422 if (CFNumberIsFloatType(value
)) {
1424 convertOk
= CFNumberGetValue(value
, kCFNumberDoubleType
, &nval
);
1425 result
= SecDbBindDouble(stmt
, param
, nval
, error
);
1427 sqlite_int64 nval64
;
1428 convertOk
= CFNumberGetValue(value
, kCFNumberSInt64Type
, &nval64
);
1430 result
= SecDbBindInt64(stmt
, param
, nval64
, error
);
1434 result
= SecDbError(SQLITE_INTERNAL
, error
, CFSTR("bind CFNumberGetValue failed for %@"), value
);
1438 CFStringRef valueDesc
= CFCopyTypeIDDescription(valueId
);
1439 SecDbError(SQLITE_MISMATCH
, error
, CFSTR("bind unsupported type %@"), valueDesc
);
1440 CFReleaseSafe(valueDesc
);
1448 // MARK: SecDbStatementRef
1450 bool SecDbReset(sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
1451 return SecDbErrorWithStmt(sqlite3_reset(stmt
), stmt
, error
, CFSTR("reset"));
1454 bool SecDbClearBindings(sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
1455 return SecDbErrorWithStmt(sqlite3_clear_bindings(stmt
), stmt
, error
, CFSTR("clear bindings"));
1458 bool SecDbFinalize(sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
1459 sqlite3
*handle
= sqlite3_db_handle(stmt
);
1460 int s3e
= sqlite3_finalize(stmt
);
1461 return s3e
== SQLITE_OK
? true : SecDbErrorWithDb(s3e
, handle
, error
, CFSTR("finalize: %p"), stmt
);
1464 sqlite3_stmt
*SecDbPrepareV2(SecDbConnectionRef dbconn
, const char *sql
, size_t sqlLen
, const char **sqlTail
, CFErrorRef
*error
) {
1465 sqlite3
*db
= SecDbHandle(dbconn
);
1466 if (sqlLen
> INT_MAX
) {
1467 SecDbErrorWithDb(SQLITE_TOOBIG
, db
, error
, CFSTR("prepare_v2: sql bigger than INT_MAX"));
1472 sqlite3_stmt
*stmt
= NULL
;
1473 int s3e
= sqlite3_prepare_v2(db
, sql
, (int)sqlLen
, &stmt
, sqlTail
);
1474 if (s3e
== SQLITE_OK
)
1476 else if (!SecDbWaitIfNeeded(dbconn
, s3e
, NULL
, CFSTR("preparev2"), ntries
, error
))
1482 static sqlite3_stmt
*SecDbCopyStatementWithTailRange(SecDbConnectionRef dbconn
, CFStringRef sql
, CFRange
*sqlTail
, CFErrorRef
*error
) {
1483 __block sqlite3_stmt
*stmt
= NULL
;
1484 if (sql
) CFStringPerformWithCStringAndLength(sql
, ^(const char *sqlStr
, size_t sqlLen
) {
1485 const char *tail
= NULL
;
1486 stmt
= SecDbPrepareV2(dbconn
, sqlStr
, sqlLen
, &tail
, error
);
1487 if (sqlTail
&& sqlStr
< tail
&& tail
< sqlStr
+ sqlLen
) {
1488 sqlTail
->location
= tail
- sqlStr
;
1489 sqlTail
->length
= sqlLen
- sqlTail
->location
;
1496 sqlite3_stmt
*SecDbCopyStmt(SecDbConnectionRef dbconn
, CFStringRef sql
, CFStringRef
*tail
, CFErrorRef
*error
) {
1497 // TODO: Add caching and cache lookup of statements
1498 CFRange sqlTail
= {};
1499 sqlite3_stmt
*stmt
= SecDbCopyStatementWithTailRange(dbconn
, sql
, &sqlTail
, error
);
1500 if (sqlTail
.length
> 0) {
1501 CFStringRef excess
= CFStringCreateWithSubstring(CFGetAllocator(sql
), sql
, sqlTail
);
1505 SecDbError(SQLITE_INTERNAL
, error
,
1506 CFSTR("prepare_v2: %@ unused sql: %@"),
1508 CFReleaseSafe(excess
);
1509 SecDbFinalize(stmt
, error
);
1517 TODO: Could do a hack here with a custom kCFAllocatorNULL allocator for a second CFRuntimeBase inside a SecDbStatement,
1518 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. */
1519 bool SecDbReleaseCachedStmt(SecDbConnectionRef dbconn
, CFStringRef sql
, sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
1521 return SecDbFinalize(stmt
, error
);
1526 bool SecDbPrepare(SecDbConnectionRef dbconn
, CFStringRef sql
, CFErrorRef
*error
, void(^exec
)(sqlite3_stmt
*stmt
)) {
1527 assert(sql
!= NULL
);
1528 sqlite3_stmt
*stmt
= SecDbCopyStmt(dbconn
, sql
, NULL
, error
);
1533 return SecDbReleaseCachedStmt(dbconn
, sql
, stmt
, error
);
1536 bool SecDbWithSQL(SecDbConnectionRef dbconn
, CFStringRef sql
, CFErrorRef
*error
, bool(^perform
)(sqlite3_stmt
*stmt
)) {
1540 CFStringRef tail
= NULL
;
1542 sqlite3_stmt
*stmt
= SecDbCopyStmt(dbconn
, sql
, &tail
, error
);
1548 // TODO: Use a different error scope here.
1549 ok
= SecError(-50 /* errSecParam */, error
, CFSTR("SecDbWithSQL perform block missing"));
1551 ok
&= SecDbReleaseCachedStmt(dbconn
, sql
, stmt
, error
);
1554 // TODO We already have an error here we really just want the left over sql in it's userData
1555 ok
= SecDbError(SQLITE_ERROR
, error
, CFSTR("Error with unexecuted sql remaining %@"), sql
);
1563 /* SecDbForEach returns true if all SQLITE_ROW returns of sqlite3_step() return true from the row block.
1564 If the row block returns false and doesn't set an error (to indicate it has reached a limit),
1565 this entire function returns false. In that case no error will be set. */
1566 bool SecDbForEach(SecDbConnectionRef dbconn
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^row
)(int row_index
)) {
1567 bool result
= false;
1568 for (int row_ix
= 0;;++row_ix
) {
1569 if (SecDbConnectionIsReadOnly(dbconn
) && !sqlite3_stmt_readonly(stmt
)) {
1570 secerror("SecDbForEach: SecDbConnection is readonly but we're about to write: %s", sqlite3_sql(stmt
));
1572 int s3e
= sqlite3_step(stmt
);
1573 if (s3e
== SQLITE_ROW
) {
1579 // If we have no row block then getting SQLITE_ROW is an error
1580 SecDbError(s3e
, error
,
1581 CFSTR("step[%d]: %s returned SQLITE_ROW with NULL row block"),
1582 row_ix
, sqlite3_sql(stmt
));
1585 if (s3e
== SQLITE_DONE
) {
1588 SecDbConnectionCheckCode(dbconn
, s3e
, error
, CFSTR("SecDbForEach step[%d]"), row_ix
);
1596 void SecDbRecordChange(SecDbConnectionRef dbconn
, CFTypeRef deleted
, CFTypeRef inserted
) {
1597 if (!dbconn
->db
->notifyPhase
) return;
1598 CFTypeRef entry
= SecDbEventCreateWithComponents(deleted
, inserted
);
1600 CFArrayAppendValue(dbconn
->changes
, entry
);
1603 if (!dbconn
->inTransaction
) {
1604 secerror("db %@ changed outside txn", dbconn
);
1605 // Only notify of DidCommit, since WillCommit code assumes
1607 SecDbOnNotify(dbconn
, ^{
1608 SecDbNotifyPhase(dbconn
, kSecDbTransactionDidCommit
);
1615 CFGiblisFor(SecDbConnection
)
1618 // SecDbEvent Creation and consumption
1621 static SecDbEventRef
SecDbEventCreateInsert(CFTypeRef inserted
) {
1622 return CFRetainSafe(inserted
);
1625 static SecDbEventRef
SecDbEventCreateDelete(CFTypeRef deleted
) {
1626 return CFArrayCreate(kCFAllocatorDefault
, &deleted
, 1, &kCFTypeArrayCallBacks
);
1629 static SecDbEventRef
SecDbEventCreateUpdate(CFTypeRef deleted
, CFTypeRef inserted
) {
1630 const void *values
[2] = { deleted
, inserted
};
1631 return CFArrayCreate(kCFAllocatorDefault
, values
, 2, &kCFTypeArrayCallBacks
);
1634 SecDbEventRef
SecDbEventCreateWithComponents(CFTypeRef deleted
, CFTypeRef inserted
) {
1635 if (deleted
&& inserted
)
1636 return SecDbEventCreateUpdate(deleted
, inserted
);
1638 return SecDbEventCreateDelete(deleted
);
1640 return SecDbEventCreateInsert(inserted
);
1645 void SecDbEventTranslateComponents(SecDbEventRef item
, CFTypeRef
* deleted
, CFTypeRef
* inserted
) {
1646 if(CFGetTypeID(item
) == CFArrayGetTypeID()) {
1647 // One item: deletion. Two: update.
1648 CFIndex arraySize
= CFArrayGetCount(item
);
1649 if(arraySize
== 1) {
1650 if(deleted
) { *deleted
= CFArrayGetValueAtIndex(item
, 0); }
1651 if(inserted
) { *inserted
= NULL
; }
1652 } else if(arraySize
== 2) {
1653 if(deleted
) { *deleted
= CFArrayGetValueAtIndex(item
, 0); }
1654 if(inserted
) { *inserted
= CFArrayGetValueAtIndex(item
, 1); }
1656 if(deleted
) { *deleted
= NULL
; }
1657 if(inserted
) { *inserted
= NULL
; }
1660 if(deleted
) { *deleted
= NULL
; }
1661 if(inserted
) { *inserted
= item
; }
1666 bool SecDbEventGetComponents(SecDbEventRef event
, CFTypeRef
*deleted
, CFTypeRef
*inserted
, CFErrorRef
*error
) {
1667 if (isArray(event
)) {
1668 CFArrayRef array
= event
;
1669 switch (CFArrayGetCount(array
)) {
1671 *deleted
= CFArrayGetValueAtIndex(array
, 0);
1672 *inserted
= CFArrayGetValueAtIndex(array
, 1);
1675 *deleted
= CFArrayGetValueAtIndex(array
, 0);
1679 SecError(errSecParam
, error
, NULL
, CFSTR("invalid entry in changes array: %@"), array
);