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"
44 // Architecturally inverted files
45 // These are in SecureObjectSync but utilities depends on them
46 // <rdar://problem/20802079> Fix layer violation (SOSDigestVector, SOSManifest, SecDB.c)
48 #include <Security/SecureObjectSync/SOSDigestVector.h>
49 #include <Security/SecureObjectSync/SOSManifest.h>
51 struct __OpaqueSecDbStatement
{
54 SecDbConnectionRef dbconn
;
58 struct __OpaqueSecDbConnection
{
61 //CFMutableDictionaryRef statements;
63 SecDbRef db
; // NONRETAINED, since db or block retains us
66 SecDbTransactionSource source
;
68 int maybeCorruptedCode
;
70 CFErrorRef corruptionError
;
72 // Pending deletions and additions for the current transaction
73 // Entires are either:
74 // 1) a CFArrayRef of 1 element representing a deletion,
75 // 2) a CFArrayRef of 2 elements representing the element 0 having been replaced with element 1
76 // 3) a CFTypeRef that is not a CFArrayRef, representing an add of the element in question.
77 CFMutableArrayRef changes
;
80 struct __OpaqueSecDb
{
84 dispatch_queue_t queue
;
85 dispatch_queue_t commitQueue
;
86 CFMutableArrayRef connections
;
87 dispatch_semaphore_t write_semaphore
;
88 dispatch_semaphore_t read_semaphore
;
90 bool (^opened
)(SecDbRef db
, SecDbConnectionRef dbconn
, bool didCreate
, bool *callMeAgainForNextConnection
, CFErrorRef
*error
);
91 bool callOpenedHandlerForNextConnection
;
92 CFMutableArrayRef notifyPhase
; /* array of SecDBNotifyBlock */
93 mode_t mode
; /* database file permissions */
94 bool readWrite
; /* open database read-write */
95 bool allowRepair
; /* allow database repair */
96 bool useWAL
; /* use WAL mode */
97 bool useRobotVacuum
; /* use if SecDB should manage vacuum behind your back */
98 uint8_t maxIdleHandles
;
99 void (^corruptionReset
)(void);
102 // MARK: Error domains and error helper functions
104 CFStringRef kSecDbErrorDomain
= CFSTR("com.apple.utilities.sqlite3");
106 bool SecDbError(int sql_code
, CFErrorRef
*error
, CFStringRef format
, ...) {
107 if (sql_code
== SQLITE_OK
) return true;
111 CFIndex code
= sql_code
;
112 CFErrorRef previousError
= *error
;
115 va_start(args
, format
);
116 SecCFCreateErrorWithFormatAndArguments(code
, kSecDbErrorDomain
, previousError
, error
, NULL
, format
, args
);
122 bool SecDbErrorWithDb(int sql_code
, sqlite3
*db
, CFErrorRef
*error
, CFStringRef format
, ...) {
123 if (sql_code
== SQLITE_OK
) return true;
126 va_start(args
, format
);
127 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
129 CFStringRef errno_code
= NULL
;
131 if (sql_code
== SQLITE_CANTOPEN
) {
132 int errno_number
= sqlite3_system_errno(db
);
133 errno_code
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%d"), errno_number
);
135 errno_code
= CFRetain(CFSTR(""));
138 int extended_code
= sqlite3_extended_errcode(db
);
139 if (sql_code
== extended_code
)
140 SecDbError(sql_code
, error
, CFSTR("%@: [%d]%@ %s"), message
, sql_code
, errno_code
, sqlite3_errmsg(db
));
142 SecDbError(sql_code
, error
, CFSTR("%@: [%d->%d]%@ %s"), message
, sql_code
, extended_code
, errno_code
, sqlite3_errmsg(db
));
143 CFReleaseSafe(message
);
144 CFReleaseSafe(errno_code
);
149 bool SecDbErrorWithStmt(int sql_code
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, CFStringRef format
, ...) {
150 if (sql_code
== SQLITE_OK
) return true;
153 va_start(args
, format
);
154 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
157 sqlite3
*db
= sqlite3_db_handle(stmt
);
158 const char *sql
= sqlite3_sql(stmt
);
159 int extended_code
= sqlite3_extended_errcode(db
);
160 if (sql_code
== extended_code
)
161 SecDbError(sql_code
, error
, CFSTR("%@: [%d] %s sql: %s"), message
, sql_code
, sqlite3_errmsg(db
), sql
);
163 SecDbError(sql_code
, error
, CFSTR("%@: [%d->%d] %s sql: %s"), message
, sql_code
, extended_code
, sqlite3_errmsg(db
), sql
);
164 CFReleaseSafe(message
);
169 // A callback for the sqlite3_log() interface.
170 static void sqlite3Log(void *pArg
, int iErrCode
, const char *zMsg
){
171 secdebug("sqlite3", "(%d) %s", iErrCode
, zMsg
);
174 void _SecDbServerSetup(void)
176 static dispatch_once_t onceToken
;
177 dispatch_once(&onceToken
, ^{
178 int rx
= sqlite3_config(SQLITE_CONFIG_LOG
, sqlite3Log
, NULL
);
179 if (SQLITE_OK
!= rx
) {
180 secwarning("Could not set up sqlite global error logging to syslog: %d", rx
);
187 // MARK: Static helper functions
189 static bool SecDbOpenHandle(SecDbConnectionRef dbconn
, bool *created
, CFErrorRef
*error
);
190 static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn
, int rc
, CFErrorRef
*error
);
193 #pragma mark SecDbRef
196 SecDbCopyFormatDescription(CFTypeRef value
, CFDictionaryRef formatOptions
)
198 SecDbRef db
= (SecDbRef
)value
;
199 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<SecDb path:%@ connections: %@>"), db
->db_path
, db
->connections
);
204 SecDbDestroy(CFTypeRef value
)
206 SecDbRef db
= (SecDbRef
)value
;
207 CFReleaseNull(db
->connections
);
208 CFReleaseNull(db
->db_path
);
210 dispatch_release(db
->queue
);
213 if (db
->commitQueue
) {
214 dispatch_release(db
->commitQueue
);
215 db
->commitQueue
= NULL
;
217 if (db
->read_semaphore
) {
218 dispatch_release(db
->read_semaphore
);
219 db
->read_semaphore
= NULL
;
221 if (db
->write_semaphore
) {
222 dispatch_release(db
->write_semaphore
);
223 db
->write_semaphore
= NULL
;
226 Block_release(db
->opened
);
229 CFReleaseNull(db
->notifyPhase
);
235 SecDbCreate(CFStringRef dbName
, mode_t mode
, bool readWrite
, bool allowRepair
, bool useWAL
, bool useRobotVacuum
, uint8_t maxIdleHandles
,
236 bool (^opened
)(SecDbRef db
, SecDbConnectionRef dbconn
, bool didCreate
, bool *callMeAgainForNextConnection
, CFErrorRef
*error
))
240 db
= CFTypeAllocate(SecDb
, struct __OpaqueSecDb
, kCFAllocatorDefault
);
241 require(db
!= NULL
, done
);
243 CFStringPerformWithCString(dbName
, ^(const char *dbNameStr
) {
244 db
->queue
= dispatch_queue_create(dbNameStr
, DISPATCH_QUEUE_SERIAL
);
246 CFStringRef commitQueueStr
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@-commit"), dbName
);
247 CFStringPerformWithCString(commitQueueStr
, ^(const char *cqNameStr
) {
248 db
->commitQueue
= dispatch_queue_create(cqNameStr
, DISPATCH_QUEUE_CONCURRENT
);
250 CFReleaseNull(commitQueueStr
);
251 db
->read_semaphore
= dispatch_semaphore_create(kSecDbMaxReaders
);
252 db
->write_semaphore
= dispatch_semaphore_create(kSecDbMaxWriters
);
253 db
->connections
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
254 db
->opened
= opened
? Block_copy(opened
) : NULL
;
255 if (getenv("__OSINSTALL_ENVIRONMENT") != NULL
) {
256 // TODO: Move this code out of this layer
257 secinfo("#SecDB", "SecDB: running from installer");
259 db
->db_path
= CFSTR("file::memory:?cache=shared");
261 db
->db_path
= CFStringCreateCopy(kCFAllocatorDefault
, dbName
);
264 db
->readWrite
= readWrite
;
265 db
->allowRepair
= allowRepair
;
267 db
->useRobotVacuum
= useRobotVacuum
;
268 db
->maxIdleHandles
= maxIdleHandles
;
269 db
->corruptionReset
= NULL
;
276 SecDbIdleConnectionCount(SecDbRef db
) {
277 __block CFIndex count
= 0;
278 dispatch_sync(db
->queue
, ^{
279 count
= CFArrayGetCount(db
->connections
);
284 void SecDbAddNotifyPhaseBlock(SecDbRef db
, SecDBNotifyBlock notifyPhase
)
286 // SecDbNotifyPhase seems to mostly be called on the db's commitQueue, and not the db's queue. Therefore, protect the array with that queue.
287 dispatch_sync(db
->commitQueue
, ^{
288 SecDBNotifyBlock block
= Block_copy(notifyPhase
); /* Force the block off the stack */
289 if (db
->notifyPhase
== NULL
) {
290 db
->notifyPhase
= CFArrayCreateMutableForCFTypes(NULL
);
292 CFArrayAppendValue(db
->notifyPhase
, block
);
293 Block_release(block
);
297 static void SecDbNotifyPhase(SecDbConnectionRef dbconn
, SecDbTransactionPhase phase
) {
298 if (CFArrayGetCount(dbconn
->changes
)) {
299 CFArrayRef changes
= dbconn
->changes
;
300 dbconn
->changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
301 if (dbconn
->db
->notifyPhase
) {
302 CFArrayForEach(dbconn
->db
->notifyPhase
, ^(const void *value
) {
303 SecDBNotifyBlock notifyBlock
= (SecDBNotifyBlock
)value
;
304 notifyBlock(dbconn
, phase
, dbconn
->source
, changes
);
307 CFReleaseSafe(changes
);
311 static void SecDbOnNotify(SecDbConnectionRef dbconn
, void (^perform
)(void)) {
315 CFStringRef
SecDbGetPath(SecDbRef db
) {
324 #pragma mark SecDbConnectionRef
326 static bool SecDbCheckCorrupted(SecDbConnectionRef dbconn
)
328 __block
bool checkDidRun
= false;
329 __block
bool isCorrupted
= false;
330 __block CFErrorRef error
= NULL
;
331 SecDbPrepare(dbconn
, CFSTR("PRAGMA integrity_check"), &error
, ^(sqlite3_stmt
*stmt
) {
332 SecDbStep(dbconn
, stmt
, &error
, ^(bool *stop
) {
333 const char * result
= (const char*)sqlite3_column_text(stmt
, 0);
334 if (!result
|| strncasecmp(result
, "ok", 3) != 0) {
336 secerror("SecDBCheckCorrupted integrity_check returned %s", (result
) ? result
: "NULL");
342 // An error occurred in SecDbPrepare before we could run the block.
344 CFIndex code
= CFErrorGetCode(error
);
345 if (SQLITE_CORRUPT
== code
|| SQLITE_NOTADB
== code
) {
348 secinfo("#SecDB", "#SecDB warning error %{public}@ when running integrity check", error
);
350 // We don't have an error ref if SecDbPrepare has called SecDbConnectionCheckCode,
351 // which then called SecDbHandleCorrupt. That code path is only entered when the
352 // original error was SQLITE_CORRUPT or SQLITE_NOTADB. On other errors, the
353 // CFErrorRef is not cleared and we can just check the code above.
355 secinfo("#SecDB", "#SecDB warning: failed to run integrity check due to corruption");
360 secerror("SecDBCheckCorrupted ran integrity_check, and that didn't return ok");
362 secerror("SecDBCheckCorrupted failed to run integrity check");
365 CFReleaseNull(error
);
370 static bool SecDbDidCreateFirstConnection(SecDbConnectionRef dbconn
, bool didCreate
, CFErrorRef
*error
)
372 secinfo("#SecDB", "#SecDB starting maintenance");
375 // Historical note: this used to check for integrity but that became too slow and caused panics at boot.
376 // Now, just react to SQLite errors when doing an operation. If file on disk is borked it'll tell us right away.
378 if (!dbconn
->isCorrupted
&& dbconn
->db
->opened
) {
379 CFErrorRef localError
= NULL
;
381 dbconn
->db
->callOpenedHandlerForNextConnection
= false;
382 ok
= dbconn
->db
->opened(dbconn
->db
, dbconn
, didCreate
, &dbconn
->db
->callOpenedHandlerForNextConnection
, &localError
);
385 secerror("opened block failed: %@", localError
);
387 if (!dbconn
->isCorrupted
&& error
&& *error
== NULL
) {
392 secerror("opened block failed: error (%@) is being released and lost", localError
);
393 CFReleaseNull(localError
);
397 if (dbconn
->isCorrupted
) {
398 ok
= SecDbHandleCorrupt(dbconn
, 0, error
);
401 secinfo("#SecDB", "#SecDB starting maintenance");
405 void SecDbCorrupt(SecDbConnectionRef dbconn
, CFErrorRef error
)
407 os_log_fault(secLogObjForScope("SecEmergency"), "SecDBCorrupt: %@", error
);
408 dbconn
->isCorrupted
= true;
409 CFRetainAssign(dbconn
->corruptionError
, error
);
413 static uint8_t knownDbPathIndex(SecDbConnectionRef dbconn
)
416 if(CFEqual(dbconn
->db
->db_path
, CFSTR("/Library/Keychains/keychain-2.db")))
418 if(CFEqual(dbconn
->db
->db_path
, CFSTR("/Library/Keychains/ocspcache.sqlite3")))
420 if(CFEqual(dbconn
->db
->db_path
, CFSTR("/Library/Keychains/TrustStore.sqlite3")))
422 if(CFEqual(dbconn
->db
->db_path
, CFSTR("/Library/Keychains/caissuercache.sqlite3")))
425 /* Unknown DB path */
429 static bool SecDbConnectionCheckCode(SecDbConnectionRef dbconn
, int code
, CFErrorRef
*error
, CFStringRef desc
, ...)
430 CF_FORMAT_FUNCTION(4, 5);
434 // Return true if there was no error, returns false otherwise and set *error to an appropriate CFErrorRef.
435 static bool SecDbConnectionCheckCode(SecDbConnectionRef dbconn
, int code
, CFErrorRef
*error
, CFStringRef desc
, ...) {
436 if (code
== SQLITE_OK
|| code
== SQLITE_DONE
)
441 va_start(args
, desc
);
442 CFStringRef msg
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, desc
, args
);
444 SecDbErrorWithDb(code
, dbconn
->handle
, error
, CFSTR("%@"), msg
);
448 dbconn
->hasIOFailure
|= (SQLITE_IOERR
== code
);
450 /* If it's already corrupted, don't try to recover */
451 if (dbconn
->isCorrupted
) {
452 CFStringRef reason
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
453 CFSTR("SQL DB %@ is corrupted already. Corruption error was: %d (previously %d)"),
454 dbconn
->db
->db_path
, code
, dbconn
->maybeCorruptedCode
);
455 secerror("%@",reason
);
456 __security_simulatecrash(reason
, __sec_exception_code_TwiceCorruptDb(knownDbPathIndex(dbconn
)));
457 CFReleaseSafe(reason
);
458 // We can't fall through to the checking case because it eventually calls SecDbConnectionCheckCode again.
459 // However, this is the second time we're seeing corruption so let's take the ultimate measure.
460 if ((SQLITE_CORRUPT
== code
) || (SQLITE_NOTADB
== code
)) {
461 secerror("SecDbConnectionCheckCode detected corruption twice: going to handle corrupt DB");
462 (void)SecDbHandleCorrupt(dbconn
, code
, error
);
467 // NOTADB means file is garbage, so it's functionally equivalent to corruption
468 dbconn
->isCorrupted
= (SQLITE_CORRUPT
== code
) || (SQLITE_NOTADB
== code
);
469 if (dbconn
->isCorrupted
) {
470 /* Run integrity check and only make dbconn->isCorrupted true and
471 run the corruption handler if the integrity check conclusively fails. */
472 dbconn
->maybeCorruptedCode
= code
;
473 dbconn
->isCorrupted
= SecDbCheckCorrupted(dbconn
);
474 if (dbconn
->isCorrupted
) {
475 secerror("operation returned code: %d integrity check=fail", code
);
476 (void)SecDbHandleCorrupt(dbconn
, code
, error
);
478 secerror("operation returned code: %d: integrity check=pass", code
);
485 #define BUSY_TIMEOUT_MS (5 * 60 * 1000) /* 5 minutes */
487 static int sleepBackoff
[] = { 10, 20, 50, 100, 250 };
488 static int sumBackoff
[] = { 10, 30, 80, 180, 430 };
489 static int NumberOfSleepBackoff
= sizeof(sleepBackoff
)/sizeof(sleepBackoff
[0]);
491 // Use these as silly hacks to encode the SQLite return code in the backtrace, for hang debugging purposes
492 static void __attribute__((noinline
)) SecDbLockSleep(int ms
) {
496 static void __attribute__((noinline
)) SecDbBusySleep(int ms
) {
500 // Return true causes the operation to be tried again.
501 // Note that we set sqlite3_busy_timeout on the connection, so anytime you're in here, it's likely due to SQLITE_LOCKED.
502 static bool SecDbWaitIfNeeded(SecDbConnectionRef dbconn
, int s3e
, sqlite3_stmt
*stmt
, CFStringRef desc
, int nTries
, CFErrorRef
*error
) {
503 if (((0xFF & s3e
) == SQLITE_BUSY
) || ((0xFF & s3e
) == SQLITE_LOCKED
)) {
504 int totaltimeout
, timeout
;
506 _Static_assert(sizeof(sumBackoff
) == sizeof(sleepBackoff
), "matching arrays not matching");
507 _Static_assert(sizeof(sumBackoff
[0]) == sizeof(sleepBackoff
[0]), "matching arrays not matching");
509 if (nTries
< NumberOfSleepBackoff
) {
510 timeout
= sleepBackoff
[nTries
];
511 totaltimeout
= sumBackoff
[nTries
];
513 timeout
= sleepBackoff
[NumberOfSleepBackoff
- 1];
514 totaltimeout
= sumBackoff
[NumberOfSleepBackoff
- 1] + (timeout
* (nTries
- NumberOfSleepBackoff
));
516 if (totaltimeout
< BUSY_TIMEOUT_MS
) {
517 secinfo("#SecDB", "sqlite busy/locked: %d ntries: %d totaltimeout: %d", s3e
, nTries
, totaltimeout
);
518 if(((0xFF & s3e
) == SQLITE_LOCKED
)) {
519 SecDbLockSleep(timeout
);
521 SecDbBusySleep(timeout
);
525 secinfo("#SecDB", "sqlite busy/locked: too long: %d ms, giving up", totaltimeout
);
529 return SecDbConnectionCheckCode(dbconn
, s3e
, error
, CFSTR("%@"), desc
);
532 enum SecDbStepResult
{
537 typedef enum SecDbStepResult SecDbStepResult
;
539 static SecDbStepResult
_SecDbStep(SecDbConnectionRef dbconn
, sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
540 assert(stmt
!= NULL
);
544 s3e
= sqlite3_step(stmt
);
545 if (s3e
== SQLITE_ROW
) {
546 return kSecDbRowStep
;
547 } else if (s3e
== SQLITE_DONE
) {
549 ** ^[SQLITE_DONE] means that the statement has finished executing
550 ** successfully. sqlite3_step() should not be called again on this virtual
551 ** machine without first calling [] to reset the virtual
552 ** machine back to its initial state.
555 return kSecDbDoneStep
;
556 } else if (!SecDbWaitIfNeeded(dbconn
, s3e
, stmt
, CFSTR("step"), ntries
, error
)) {
557 return kSecDbErrorStep
;
564 SecDbExec(SecDbConnectionRef dbconn
, CFStringRef sql
, CFErrorRef
*error
)
569 CFStringRef tail
= NULL
;
571 sqlite3_stmt
*stmt
= SecDbCopyStmt(dbconn
, sql
, &tail
, error
);
575 while ((sr
= _SecDbStep(dbconn
, stmt
, error
)) == kSecDbRowStep
);
576 if (sr
== kSecDbErrorStep
)
578 ok
&= SecDbReleaseCachedStmt(dbconn
, sql
, stmt
, error
);
581 // TODO We already have an error here we really just want the left over sql in it's userData
582 ok
= SecDbError(SQLITE_ERROR
, error
, CFSTR("Error with unexecuted sql remaining %@"), sql
);
590 static int SecDBGetInteger(SecDbConnectionRef dbconn
, CFStringRef sql
)
592 __block
int number
= -1;
593 __block CFErrorRef error
= NULL
;
595 (void)SecDbWithSQL(dbconn
, sql
, &error
, ^bool(sqlite3_stmt
*sqlStmt
) {
596 (void)SecDbStep(dbconn
, sqlStmt
, &error
, ^(bool *stop
) {
597 number
= sqlite3_column_int(sqlStmt
, 0);
602 CFReleaseNull(error
);
607 void SecDBManagementTasks(SecDbConnectionRef dbconn
)
609 int64_t page_count
= SecDBGetInteger(dbconn
, CFSTR("pragma page_count"));
610 if (page_count
<= 0) {
613 int64_t free_count
= SecDBGetInteger(dbconn
, CFSTR("pragma freelist_count"));
614 if (free_count
< 0) {
618 int64_t max_free
= 8192;
620 int64_t pages_in_use
= page_count
- free_count
;
621 double loadFactor
= ((double)pages_in_use
/(double)page_count
);
622 if (0.85 < loadFactor
&& free_count
< max_free
) {
625 int64_t pages_to_free
= (int64_t)(0.2 * free_count
);
626 if (0.4 > loadFactor
) {
627 pages_to_free
= free_count
;
630 char *formatString
= NULL
;
631 asprintf(&formatString
, "pragma incremental_vacuum(%d)", (int)pages_to_free
);
633 char *sqlerror
= NULL
;
634 int rc
= sqlite3_exec(dbconn
->handle
, formatString
, NULL
, NULL
, &sqlerror
);
636 secerror("incremental_vacuum failed with: (%d) %{public}s", rc
, sqlerror
);
638 sqlite3_free(sqlerror
);
645 static bool SecDbBeginTransaction(SecDbConnectionRef dbconn
, SecDbTransactionType type
, CFErrorRef
*error
)
650 case kSecDbImmediateTransactionType
:
651 secdebug("db", "SecDbBeginTransaction SecDbBeginTransaction %p", dbconn
);
652 query
= CFSTR("BEGIN IMMEDIATE");
654 case kSecDbExclusiveRemoteSOSTransactionType
:
655 secdebug("db", "SecDbBeginTransaction kSecDbExclusiveRemoteSOSTransactionType %p", dbconn
);
656 dbconn
->source
= kSecDbSOSTransaction
;
657 query
= CFSTR("BEGIN EXCLUSIVE");
659 case kSecDbExclusiveRemoteCKKSTransactionType
:
660 secdebug("db", "SecDbBeginTransaction kSecDbExclusiveRemoteCKKSTransactionType %p", dbconn
);
661 dbconn
->source
= kSecDbCKKSTransaction
;
662 query
= CFSTR("BEGIN EXCLUSIVE");
664 case kSecDbExclusiveTransactionType
:
665 if (type
==kSecDbExclusiveTransactionType
)
666 secdebug("db", "SecDbBeginTransaction kSecDbExclusiveTransactionType %p", dbconn
);
667 query
= CFSTR("BEGIN EXCLUSIVE");
669 case kSecDbNormalTransactionType
:
670 secdebug("db", "SecDbBeginTransaction kSecDbNormalTransactionType %p", dbconn
);
671 query
= CFSTR("BEGIN");
674 secdebug("db", "SecDbBeginTransaction invalid transaction type %lu", type
);
675 ok
= SecDbError(SQLITE_ERROR
, error
, CFSTR("invalid transaction type %d"), (int)type
);
680 if (query
!= NULL
&& sqlite3_get_autocommit(dbconn
->handle
) != 0) {
681 ok
= SecDbExec(dbconn
, query
, error
);
684 dbconn
->inTransaction
= true;
689 static bool SecDbEndTransaction(SecDbConnectionRef dbconn
, bool commit
, CFErrorRef
*error
)
691 __block
bool ok
= true;
692 __block
bool commited
= false;
694 dispatch_block_t notifyAndExec
= ^{
696 //secdebug("db", "SecDbEndTransaction kSecDbTransactionWillCommit %p", dbconn);
697 SecDbNotifyPhase(dbconn
, kSecDbTransactionWillCommit
);
698 commited
= ok
= SecDbExec(dbconn
, CFSTR("END"), error
);
699 //secdebug("db", "SecDbEndTransaction kSecDbTransactionWillCommit %p (after notify)", dbconn);
701 ok
= SecDbExec(dbconn
, CFSTR("ROLLBACK"), error
);
704 dbconn
->inTransaction
= false;
705 SecDbNotifyPhase(dbconn
, commited
? kSecDbTransactionDidCommit
: kSecDbTransactionDidRollback
);
706 secdebug("db", "SecDbEndTransaction %s %p", commited
? "kSecDbTransactionDidCommit" : "kSecDbTransactionDidRollback", dbconn
);
707 dbconn
->source
= kSecDbAPITransaction
;
709 if (commit
&& dbconn
->db
->useRobotVacuum
) {
710 SecDBManagementTasks(dbconn
);
714 SecDbPerformOnCommitQueue(dbconn
, true, notifyAndExec
);
719 bool SecDbTransaction(SecDbConnectionRef dbconn
, SecDbTransactionType type
,
720 CFErrorRef
*error
, void (^transaction
)(bool *commit
))
725 if (dbconn
->inTransaction
) {
726 transaction(&commit
);
728 secinfo("#SecDB", "#SecDB nested transaction asked to not be committed");
731 ok
= SecDbBeginTransaction(dbconn
, type
, error
);
733 transaction(&commit
);
734 ok
= SecDbEndTransaction(dbconn
, commit
, error
);
741 sqlite3
*SecDbHandle(SecDbConnectionRef dbconn
) {
742 return dbconn
->handle
;
745 bool SecDbStep(SecDbConnectionRef dbconn
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, void (^row
)(bool *stop
)) {
747 switch (_SecDbStep(dbconn
, stmt
, error
)) {
748 case kSecDbErrorStep
:
749 secdebug("db", "kSecDbErrorStep %@", error
?*error
:NULL
);
752 secdebug("db", "kSecDbRowStep %@", error
?*error
:NULL
);
754 __block
bool stop
= false;
755 SecAutoreleaseInvokeWithPool(^{
762 SecDbError(SQLITE_ERROR
, error
, CFSTR("SecDbStep SQLITE_ROW returned without a row handler"));
765 secdebug("db", "kSecDbDoneStep %@", error
?*error
:NULL
);
771 bool SecDbCheckpoint(SecDbConnectionRef dbconn
, CFErrorRef
*error
)
773 return SecDbConnectionCheckCode(dbconn
, sqlite3_wal_checkpoint(dbconn
->handle
, NULL
), error
, CFSTR("wal_checkpoint"));
776 static bool SecDbFileControl(SecDbConnectionRef dbconn
, int op
, void *arg
, CFErrorRef
*error
) {
777 return SecDbConnectionCheckCode(dbconn
, sqlite3_file_control(dbconn
->handle
, NULL
, op
, arg
), error
, CFSTR("file_control"));
780 static sqlite3
*_SecDbOpenV2(const char *path
,
785 sqlite3
*handle
= NULL
;
786 int s3e
= sqlite3_open_v2(path
, &handle
, flags
, NULL
);
789 SecDbErrorWithDb(s3e
, handle
, error
, CFSTR("open_v2 \"%s\" 0x%X"), path
, flags
);
790 sqlite3_close(handle
);
793 SecDbError(s3e
, error
, CFSTR("open_v2 \"%s\" 0x%X"), path
, flags
);
795 } else if (SQLITE_OPEN_READWRITE
== (flags
& SQLITE_OPEN_READWRITE
)) {
796 if (useRobotVacuum
) {
797 #define SECDB_SQLITE_AUTO_VACUUM_INCREMENTAL 2
798 sqlite3_stmt
*stmt
= NULL
;
802 * Setting auto_vacuum = incremental on a database that is not empty requires
803 * a VACCUUM, so check if the vacuum mode is not INCREMENTAL, and if its not,
804 * set it to incremental and vacuum.
807 int s3e
= sqlite3_prepare_v2(handle
, "PRAGMA auto_vacuum", -1, &stmt
, NULL
);
809 s3e
= sqlite3_step(stmt
);
810 if (s3e
== SQLITE_ROW
) {
811 vacuumMode
= sqlite3_column_int(stmt
, 0);
815 sqlite3_finalize(stmt
);
817 if (vacuumMode
!= SECDB_SQLITE_AUTO_VACUUM_INCREMENTAL
) {
818 (void)sqlite3_exec(handle
, "PRAGMA auto_vacuum = incremental", NULL
, NULL
, NULL
);
819 (void)sqlite3_exec(handle
, "VACUUM", NULL
, NULL
, NULL
);
823 (void)sqlite3_exec(handle
, "PRAGMA journal_mode = WAL", NULL
, NULL
, NULL
);
826 // Let SQLite handle timeouts.
827 sqlite3_busy_timeout(handle
, 5*1000);
832 static bool SecDbOpenV2(SecDbConnectionRef dbconn
, const char *path
, int flags
, CFErrorRef
*error
) {
833 return (dbconn
->handle
= _SecDbOpenV2(path
, flags
, dbconn
->db
->useWAL
, dbconn
->db
->useRobotVacuum
, error
)) != NULL
;
836 static bool SecDbTruncate(SecDbConnectionRef dbconn
, CFErrorRef
*error
)
838 int flags
= SQLITE_TRUNCATE_AUTOVACUUM_FULL
;
839 if (dbconn
->db
->useWAL
) {
840 flags
|= SQLITE_TRUNCATE_JOURNALMODE_WAL
;
842 __block
bool ok
= SecDbFileControl(dbconn
, SQLITE_TRUNCATE_DATABASE
, &flags
, error
);
844 sqlite3_close(dbconn
->handle
);
845 dbconn
->handle
= NULL
;
846 CFStringPerformWithCString(dbconn
->db
->db_path
, ^(const char *path
) {
848 CFReleaseNull(*error
);
849 if (SecCheckErrno(unlink(path
), error
, CFSTR("unlink %s"), path
)) {
850 ok
= SecDbOpenHandle(dbconn
, NULL
, error
);
854 secinfo("#SecDB", "#SecDB Failed to delete db handle: %{public}@", error
? *error
: NULL
);
862 static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn
, int rc
, CFErrorRef
*error
)
864 if (!dbconn
->db
->allowRepair
) {
865 SecCFCreateErrorWithFormat(rc
, kSecErrnoDomain
, NULL
, error
, NULL
,
866 CFSTR("SecDbHandleCorrupt handled error: [%d] %s"), rc
, strerror(rc
));
867 dbconn
->isCorrupted
= false;
871 // Backup current db.
872 __block
bool didRename
= false;
873 CFStringPerformWithCString(dbconn
->db
->db_path
, ^(const char *db_path
) {
874 sqlite3
*corrupt_db
= NULL
;
875 char buf
[PATH_MAX
+1];
876 snprintf(buf
, sizeof(buf
), "%s-corrupt", db_path
);
877 if (dbconn
->handle
&& (corrupt_db
= _SecDbOpenV2(buf
, SQLITE_OPEN_READWRITE
, dbconn
->db
->useWAL
, dbconn
->db
->useRobotVacuum
, error
))) {
879 didRename
= SecDbErrorWithDb(sqlite3_file_control(corrupt_db
, NULL
, SQLITE_FCNTL_PERSIST_WAL
, &on
), corrupt_db
, error
, CFSTR("persist wal"));
880 didRename
&= SecDbErrorWithDb(sqlite3_file_control(corrupt_db
, NULL
, SQLITE_REPLACE_DATABASE
, (void *)dbconn
->handle
), corrupt_db
, error
, CFSTR("replace database"));
881 sqlite3_close(corrupt_db
);
885 secerror("Tried to rename corrupt database at path %@, but we failed: %@, trying explicit rename", dbconn
->db
->db_path
, error
? *error
: NULL
);
887 CFReleaseNull(*error
);
889 // Explicitly close our connection, plus all other open connections to this db.
891 if (dbconn
->handle
) {
892 closed
&= SecDbError(sqlite3_close(dbconn
->handle
), error
, CFSTR("close"));
893 dbconn
->handle
= NULL
;
895 SecDbRef db
= dbconn
->db
;
896 CFIndex idx
, count
= (db
->connections
) ? CFArrayGetCount(db
->connections
) : 0;
897 for (idx
= 0; idx
< count
; idx
++) {
898 SecDbConnectionRef dbconn
= (SecDbConnectionRef
) CFArrayGetValueAtIndex(db
->connections
, idx
);
899 if (dbconn
&& dbconn
->handle
) {
900 closed
&= SecDbError(sqlite3_close(dbconn
->handle
), error
, CFSTR("close"));
901 dbconn
->handle
= NULL
;
904 CFArrayRemoveAllValues(db
->connections
);
906 // Attempt rename only if all connections closed successfully.
908 if (SecCheckErrno(rename(db_path
, buf
), error
, CFSTR("rename %s %s"), db_path
, buf
)) {
909 if (SecDbOpenHandle(dbconn
, NULL
, error
)) {
916 secerror("Database at path %@ is corrupt. Copied it to %s for further investigation.", dbconn
->db
->db_path
, buf
);
918 seccritical("Tried to copy corrupt database at path %@, but we failed: %@", dbconn
->db
->db_path
, error
? *error
: NULL
);
922 bool ok
= (didRename
&&
923 (dbconn
->handle
|| SecDbOpenHandle(dbconn
, NULL
, error
)) &&
924 SecDbTruncate(dbconn
, error
));
926 // Mark the db as not corrupted, even if something failed.
927 // Always note we are no longer in the corruption handler
928 dbconn
->isCorrupted
= false;
930 // Invoke our callers opened callback, since we just created a new database
931 if (ok
&& dbconn
->db
->opened
) {
932 dbconn
->db
->callOpenedHandlerForNextConnection
= false;
933 ok
= dbconn
->db
->opened(dbconn
->db
, dbconn
, true, &dbconn
->db
->callOpenedHandlerForNextConnection
, error
);
936 if (dbconn
->db
->corruptionReset
) {
937 dbconn
->db
->corruptionReset();
944 SecDbSetCorruptionReset(SecDbRef db
, void (^corruptionReset
)(void))
946 if (db
->corruptionReset
) {
947 Block_release(db
->corruptionReset
);
948 db
->corruptionReset
= NULL
;
950 if (corruptionReset
) {
951 db
->corruptionReset
= Block_copy(corruptionReset
);
955 static bool SecDbLoggingEnabled(CFStringRef type
)
957 CFTypeRef profile
= NULL
;
958 bool enabled
= false;
960 if (csr_check(CSR_ALLOW_APPLE_INTERNAL
) != 0)
963 profile
= (CFNumberRef
)CFPreferencesCopyValue(CFSTR("SQLProfile"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesAnyHost
);
968 if (CFGetTypeID(profile
) == CFBooleanGetTypeID()) {
969 enabled
= CFBooleanGetValue((CFBooleanRef
)profile
);
970 } else if (CFGetTypeID(profile
) == CFNumberGetTypeID()) {
972 CFNumberGetValue(profile
, kCFNumberSInt32Type
, &num
);
976 CFReleaseSafe(profile
);
982 SecDbProfileMask(void)
984 static dispatch_once_t onceToken
;
985 static unsigned profile_mask
= 0;
987 // sudo defaults write /Library/Preferences/com.apple.security SQLProfile -bool true
988 dispatch_once(&onceToken
, ^{
989 if (SecDbLoggingEnabled(CFSTR("SQLProfile")))
990 profile_mask
= SQLITE_TRACE_PROFILE
;
992 profile_mask
|= SQLITE_TRACE_STMT
;
994 if (SecDbLoggingEnabled(CFSTR("SQLTrace")))
995 profile_mask
= SQLITE_TRACE_STMT
;
997 if (SecDbLoggingEnabled(CFSTR("SQLRow")))
998 profile_mask
= SQLITE_TRACE_ROW
;
999 secinfo("#SecDB", "sqlDb: sql trace mask: 0x%08x", profile_mask
);
1001 return profile_mask
;
1005 SecDbTraceV2(unsigned mask
, void *ctx
, void *p
, void *x
) {
1006 SecDbConnectionRef dbconn __unused
= ctx
;
1007 const char *trace
= "unknown";
1009 if (mask
== SQLITE_TRACE_PROFILE
)
1010 trace
= sqlite3_sql(p
);
1011 else if (mask
== SQLITE_TRACE_STMT
) {
1012 trace
= sqlite3_sql(p
);
1013 } else if (mask
== SQLITE_TRACE_ROW
) {
1014 trace
= sqlite3_expanded_sql(p
);
1017 secinfo("#SecDB", "#SecDB %{public}s", trace
);
1022 static bool SecDbOpenHandle(SecDbConnectionRef dbconn
, bool *created
, CFErrorRef
*error
)
1024 __block
bool ok
= true;
1026 CFStringPerformWithCString(dbconn
->db
->db_path
, ^(const char *db_path
) {
1027 int flags
= (dbconn
->db
->readWrite
) ? SQLITE_OPEN_READWRITE
: SQLITE_OPEN_READONLY
;
1028 ok
= created
&& SecDbOpenV2(dbconn
, db_path
, flags
, NULL
);
1032 char *tmp
= dirname((char *)db_path
);
1034 mode_t omode
= dbconn
->db
->mode
;
1035 if (omode
& S_IRUSR
) { omode
|= S_IXUSR
; } // owner can read
1036 if (omode
& S_IRGRP
) { omode
|= S_IXGRP
; } // group can read
1037 if (omode
& S_IROTH
) { omode
|= S_IXOTH
; } // other can read
1038 int errnum
= mkpath_np(tmp
, omode
);
1039 if (errnum
!= 0 && errnum
!= EEXIST
) {
1040 SecCFCreateErrorWithFormat(errnum
, kSecErrnoDomain
, NULL
, error
, NULL
,
1041 CFSTR("mkpath_np %s: [%d] %s"), tmp
, errnum
, strerror(errnum
));
1046 // if the enclosing directory is ok, try to create the database.
1047 // this forces us to open it read-write, so we'll need to be the owner here.
1048 ok
= ok
&& SecDbOpenV2(dbconn
, db_path
, SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE
, error
);
1050 chmod(db_path
, dbconn
->db
->mode
); // default: 0600 (S_IRUSR | S_IWUSR)
1057 unsigned mask
= SecDbProfileMask();
1059 (void)sqlite3_trace_v2(dbconn
->handle
,
1070 static SecDbConnectionRef
1071 SecDbConnectionCreate(SecDbRef db
, bool readOnly
, CFErrorRef
*error
)
1073 SecDbConnectionRef dbconn
= NULL
;
1075 dbconn
= CFTypeAllocate(SecDbConnection
, struct __OpaqueSecDbConnection
, kCFAllocatorDefault
);
1076 require(dbconn
!= NULL
, done
);
1079 dbconn
->readOnly
= readOnly
;
1080 dbconn
->inTransaction
= false;
1081 dbconn
->source
= kSecDbInvalidTransaction
;
1082 dbconn
->isCorrupted
= false;
1083 dbconn
->maybeCorruptedCode
= 0;
1084 dbconn
->hasIOFailure
= false;
1085 dbconn
->corruptionError
= NULL
;
1086 dbconn
->handle
= NULL
;
1087 dbconn
->changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1093 static bool SecDbConnectionIsReadOnly(SecDbConnectionRef dbconn
) {
1094 return dbconn
->readOnly
;
1097 static void SecDbConectionSetReadOnly(SecDbConnectionRef dbconn
, bool readOnly
) {
1098 dbconn
->readOnly
= readOnly
;
1101 /* Read only connections go to the end of the queue, writeable connections
1102 go to the start of the queue. */
1103 SecDbConnectionRef
SecDbConnectionAcquire(SecDbRef db
, bool readOnly
, CFErrorRef
*error
) {
1104 SecDbConnectionRef dbconn
= NULL
;
1105 SecDbConnectionAcquireRefMigrationSafe(db
, readOnly
, &dbconn
, error
);
1109 bool SecDbConnectionAcquireRefMigrationSafe(SecDbRef db
, bool readOnly
, SecDbConnectionRef
* dbconnRef
, CFErrorRef
*error
)
1112 secinfo("dbconn", "acquire %s connection", readOnly
? "ro" : "rw");
1113 dispatch_semaphore_wait(readOnly
? db
->read_semaphore
: db
->write_semaphore
, DISPATCH_TIME_FOREVER
);
1114 __block SecDbConnectionRef dbconn
= NULL
;
1115 __block
bool ok
= true;
1116 __block
bool ranOpenedHandler
= false;
1118 bool (^assignDbConn
)(SecDbConnectionRef
) = ^bool(SecDbConnectionRef connection
) {
1119 dbconn
= connection
;
1121 *dbconnRef
= connection
;
1124 return dbconn
!= NULL
;
1127 dispatch_sync(db
->queue
, ^{
1128 if (!db
->didFirstOpen
) {
1129 bool didCreate
= false;
1130 ok
= assignDbConn(SecDbConnectionCreate(db
, false, error
));
1131 CFErrorRef localError
= NULL
;
1132 if (ok
&& !SecDbOpenHandle(dbconn
, &didCreate
, &localError
)) {
1133 secerror("Unable to create database: %@", localError
);
1134 if (localError
&& CFEqual(CFErrorGetDomain(localError
), kSecDbErrorDomain
)) {
1135 int code
= (int)CFErrorGetCode(localError
);
1136 dbconn
->isCorrupted
= (SQLITE_CORRUPT
== code
) || (SQLITE_NOTADB
== code
);
1138 // If the open failure isn't due to corruption, propagate the error.
1139 ok
= dbconn
->isCorrupted
;
1140 if (!ok
&& error
&& *error
== NULL
) {
1141 *error
= localError
;
1145 CFReleaseNull(localError
);
1148 db
->didFirstOpen
= ok
= SecDbDidCreateFirstConnection(dbconn
, didCreate
, error
);
1149 ranOpenedHandler
= true;
1152 CFReleaseNull(dbconn
);
1154 /* Try to get one from the cache */
1155 CFIndex count
= CFArrayGetCount(db
->connections
);
1156 while (count
&& !dbconn
) {
1157 CFIndex ix
= readOnly
? count
- 1 : 0;
1158 if (assignDbConn((SecDbConnectionRef
)CFArrayGetValueAtIndex(db
->connections
, ix
)))
1159 CFRetainSafe(dbconn
);
1161 secerror("got NULL dbconn at index: %" PRIdCFIndex
" skipping", ix
);
1162 CFArrayRemoveValueAtIndex(db
->connections
, ix
);
1168 /* Make sure the connection we found has the right access */
1169 if (SecDbConnectionIsReadOnly(dbconn
) != readOnly
) {
1170 SecDbConectionSetReadOnly(dbconn
, readOnly
);
1173 /* Nothing found in cache, create a new connection */
1174 bool created
= false;
1175 if (assignDbConn(SecDbConnectionCreate(db
, readOnly
, error
)) && !SecDbOpenHandle(dbconn
, &created
, error
)) {
1176 CFReleaseNull(dbconn
);
1181 if (dbconn
&& !ranOpenedHandler
&& dbconn
->db
->opened
) {
1182 dispatch_sync(db
->queue
, ^{
1183 if (dbconn
->db
->callOpenedHandlerForNextConnection
) {
1184 dbconn
->db
->callOpenedHandlerForNextConnection
= false;
1185 if (!dbconn
->db
->opened(db
, dbconn
, false, &dbconn
->db
->callOpenedHandlerForNextConnection
, error
)) {
1186 if (!dbconn
->isCorrupted
|| !SecDbHandleCorrupt(dbconn
, 0, error
)) {
1187 CFReleaseNull(dbconn
);
1195 *dbconnRef
= dbconn
;
1199 // If acquire fails we need to signal the semaphore again.
1200 dispatch_semaphore_signal(readOnly
? db
->read_semaphore
: db
->write_semaphore
);
1204 return dbconn
? true : false;
1207 void SecDbConnectionRelease(SecDbConnectionRef dbconn
) {
1209 secerror("called with NULL dbconn");
1212 SecDbRef db
= dbconn
->db
;
1213 secinfo("dbconn", "release %@", dbconn
);
1214 dispatch_sync(db
->queue
, ^{
1215 bool readOnly
= SecDbConnectionIsReadOnly(dbconn
);
1216 if (dbconn
->hasIOFailure
) {
1217 // Something wrong on the file layer (e.g. revoked file descriptor for networked home)
1218 // so we don't trust our existing connections anymore.
1219 CFArrayRemoveAllValues(db
->connections
);
1221 CFIndex count
= CFArrayGetCount(db
->connections
);
1222 // Add back possible writable dbconn to the pool.
1223 CFArrayInsertValueAtIndex(db
->connections
, readOnly
? count
: 0, dbconn
);
1224 // Remove the last (probably read-only) dbconn from the pool.
1225 if (count
>= db
->maxIdleHandles
) {
1226 CFArrayRemoveValueAtIndex(db
->connections
, count
);
1229 // Signal after we have put the connection back in the pool of connections
1230 dispatch_semaphore_signal(readOnly
? db
->read_semaphore
: db
->write_semaphore
);
1236 void SecDbReleaseAllConnections(SecDbRef db
) {
1237 // Force all connections to be removed (e.g. file descriptor no longer valid)
1239 secerror("called with NULL db");
1242 dispatch_sync(db
->queue
, ^{
1243 CFArrayRemoveAllValues(db
->connections
);
1244 dispatch_semaphore_signal(db
->write_semaphore
);
1245 dispatch_semaphore_signal(db
->read_semaphore
);
1249 bool SecDbPerformRead(SecDbRef db
, CFErrorRef
*error
, void (^perform
)(SecDbConnectionRef dbconn
)) {
1250 SecDbConnectionRef dbconn
= SecDbConnectionAcquire(db
, true, error
);
1251 bool success
= false;
1255 SecDbConnectionRelease(dbconn
);
1260 bool SecDbPerformWrite(SecDbRef db
, CFErrorRef
*error
, void (^perform
)(SecDbConnectionRef dbconn
)) {
1262 SecError(errSecNotAvailable
, error
, CFSTR("failed to get a db handle"));
1265 SecDbConnectionRef dbconn
= SecDbConnectionAcquire(db
, false, error
);
1266 bool success
= false;
1270 SecDbConnectionRelease(dbconn
);
1276 SecDbConnectionCopyFormatDescription(CFTypeRef value
, CFDictionaryRef formatOptions
)
1278 SecDbConnectionRef dbconn
= (SecDbConnectionRef
)value
;
1279 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<SecDbConnection %s %s>"),
1280 dbconn
->readOnly
? "ro" : "rw", dbconn
->handle
? "open" : "closed");
1284 SecDbConnectionDestroy(CFTypeRef value
)
1286 SecDbConnectionRef dbconn
= (SecDbConnectionRef
)value
;
1287 if (dbconn
->handle
) {
1288 int s3e
= sqlite3_close(dbconn
->handle
);
1289 if (s3e
!= SQLITE_OK
) {
1290 secerror("failed to close database connection (%d) for %@: %s", s3e
, dbconn
->db
->db_path
, sqlite3_errmsg(dbconn
->handle
));
1294 CFReleaseNull(dbconn
->changes
);
1295 CFReleaseNull(dbconn
->corruptionError
);
1299 void SecDbPerformOnCommitQueue(SecDbConnectionRef dbconn
, bool barrier
, dispatch_block_t perform
) {
1301 dispatch_barrier_sync(dbconn
->db
->commitQueue
, ^{
1305 dispatch_sync(dbconn
->db
->commitQueue
, ^{
1312 // MARK: Bind helpers
1315 bool SecDbBindNull(sqlite3_stmt
*stmt
, int param
, CFErrorRef
*error
) {
1316 bool ok
= SecDbErrorWithStmt(sqlite3_bind_null(stmt
, param
),
1317 stmt
, error
, CFSTR("bind_null[%d]"), param
);
1318 secinfo("bind", "bind_null[%d] error: %@", param
, error
? *error
: NULL
);
1323 bool SecDbBindBlob(sqlite3_stmt
*stmt
, int param
, const void *zData
, size_t n
, void(*xDel
)(void*), CFErrorRef
*error
) {
1325 return SecDbErrorWithStmt(SQLITE_TOOBIG
, stmt
, error
,
1326 CFSTR("bind_blob[%d]: blob bigger than INT_MAX"), param
);
1328 bool ok
= SecDbErrorWithStmt(sqlite3_bind_blob(stmt
, param
, zData
, (int)n
, xDel
),
1329 stmt
, error
, CFSTR("bind_blob[%d]"), param
);
1330 secinfo("bind", "bind_blob[%d]: %.*s: %@", param
, (int)n
, zData
, error
? *error
: NULL
);
1334 bool SecDbBindText(sqlite3_stmt
*stmt
, int param
, const char *zData
, size_t n
, void(*xDel
)(void*), CFErrorRef
*error
) {
1336 return SecDbErrorWithStmt(SQLITE_TOOBIG
, stmt
, error
,
1337 CFSTR("bind_text[%d]: text bigger than INT_MAX"), param
);
1339 bool ok
= SecDbErrorWithStmt(sqlite3_bind_text(stmt
, param
, zData
, (int)n
, xDel
), stmt
, error
,
1340 CFSTR("bind_text[%d]"), param
);
1341 secinfo("bind", "bind_text[%d]: \"%s\" error: %@", param
, zData
, error
? *error
: NULL
);
1345 bool SecDbBindDouble(sqlite3_stmt
*stmt
, int param
, double value
, CFErrorRef
*error
) {
1346 bool ok
= SecDbErrorWithStmt(sqlite3_bind_double(stmt
, param
, value
), stmt
, error
,
1347 CFSTR("bind_double[%d]"), param
);
1348 secinfo("bind", "bind_double[%d]: %f error: %@", param
, value
, error
? *error
: NULL
);
1352 bool SecDbBindInt(sqlite3_stmt
*stmt
, int param
, int value
, CFErrorRef
*error
) {
1353 bool ok
= SecDbErrorWithStmt(sqlite3_bind_int(stmt
, param
, value
), stmt
, error
,
1354 CFSTR("bind_int[%d]"), param
);
1355 secinfo("bind", "bind_int[%d]: %d error: %@", param
, value
, error
? *error
: NULL
);
1359 bool SecDbBindInt64(sqlite3_stmt
*stmt
, int param
, sqlite3_int64 value
, CFErrorRef
*error
) {
1360 bool ok
= SecDbErrorWithStmt(sqlite3_bind_int64(stmt
, param
, value
), stmt
, error
,
1361 CFSTR("bind_int64[%d]"), param
);
1362 secinfo("bind", "bind_int64[%d]: %lld error: %@", param
, value
, error
? *error
: NULL
);
1367 /* AUDIT[securityd](done):
1368 value (ok) is a caller provided, non NULL CFTypeRef.
1370 bool SecDbBindObject(sqlite3_stmt
*stmt
, int param
, CFTypeRef value
, CFErrorRef
*error
) {
1372 __block
bool result
= false;
1374 /* TODO: Can we use SQLITE_STATIC below everwhere we currently use
1375 SQLITE_TRANSIENT since we finalize the statement before the value
1376 goes out of scope? */
1377 if (!value
|| (valueId
= CFGetTypeID(value
)) == CFNullGetTypeID()) {
1378 /* Skip bindings for NULL values. sqlite3 will interpret unbound
1379 params as NULL which is exactly what we want. */
1383 result
= SecDbBindNull(stmt
, param
, error
);
1385 } else if (valueId
== CFStringGetTypeID()) {
1386 CFStringPerformWithCStringAndLength(value
, ^(const char *cstr
, size_t clen
) {
1387 result
= SecDbBindText(stmt
, param
, cstr
, clen
, SQLITE_TRANSIENT
, error
);
1389 } else if (valueId
== CFDataGetTypeID()) {
1390 CFIndex len
= CFDataGetLength(value
);
1392 result
= SecDbBindBlob(stmt
, param
, CFDataGetBytePtr(value
),
1393 len
, SQLITE_TRANSIENT
, error
);
1395 result
= SecDbBindText(stmt
, param
, "", 0, SQLITE_TRANSIENT
, error
);
1397 } else if (valueId
== CFDateGetTypeID()) {
1398 CFAbsoluteTime abs_time
= CFDateGetAbsoluteTime(value
);
1399 result
= SecDbBindDouble(stmt
, param
, abs_time
, error
);
1400 } else if (valueId
== CFBooleanGetTypeID()) {
1401 int bval
= CFBooleanGetValue(value
);
1402 result
= SecDbBindInt(stmt
, param
, bval
, error
);
1403 } else if (valueId
== CFNumberGetTypeID()) {
1405 if (CFNumberIsFloatType(value
)) {
1407 convertOk
= CFNumberGetValue(value
, kCFNumberDoubleType
, &nval
);
1408 result
= SecDbBindDouble(stmt
, param
, nval
, error
);
1411 convertOk
= CFNumberGetValue(value
, kCFNumberSInt32Type
, &nval
);
1413 result
= SecDbBindInt(stmt
, param
, nval
, error
);
1415 sqlite_int64 nval64
;
1416 convertOk
= CFNumberGetValue(value
, kCFNumberSInt64Type
, &nval64
);
1418 result
= SecDbBindInt64(stmt
, param
, nval64
, error
);
1422 result
= SecDbError(SQLITE_INTERNAL
, error
, CFSTR("bind CFNumberGetValue failed for %@"), value
);
1426 CFStringRef valueDesc
= CFCopyTypeIDDescription(valueId
);
1427 SecDbError(SQLITE_MISMATCH
, error
, CFSTR("bind unsupported type %@"), valueDesc
);
1428 CFReleaseSafe(valueDesc
);
1436 // MARK: SecDbStatementRef
1438 bool SecDbReset(sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
1439 return SecDbErrorWithStmt(sqlite3_reset(stmt
), stmt
, error
, CFSTR("reset"));
1442 bool SecDbClearBindings(sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
1443 return SecDbErrorWithStmt(sqlite3_clear_bindings(stmt
), stmt
, error
, CFSTR("clear bindings"));
1446 bool SecDbFinalize(sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
1447 sqlite3
*handle
= sqlite3_db_handle(stmt
);
1448 int s3e
= sqlite3_finalize(stmt
);
1449 return s3e
== SQLITE_OK
? true : SecDbErrorWithDb(s3e
, handle
, error
, CFSTR("finalize: %p"), stmt
);
1452 sqlite3_stmt
*SecDbPrepareV2(SecDbConnectionRef dbconn
, const char *sql
, size_t sqlLen
, const char **sqlTail
, CFErrorRef
*error
) {
1453 sqlite3
*db
= SecDbHandle(dbconn
);
1454 if (sqlLen
> INT_MAX
) {
1455 SecDbErrorWithDb(SQLITE_TOOBIG
, db
, error
, CFSTR("prepare_v2: sql bigger than INT_MAX"));
1460 sqlite3_stmt
*stmt
= NULL
;
1461 int s3e
= sqlite3_prepare_v2(db
, sql
, (int)sqlLen
, &stmt
, sqlTail
);
1462 if (s3e
== SQLITE_OK
)
1464 else if (!SecDbWaitIfNeeded(dbconn
, s3e
, NULL
, CFSTR("preparev2"), ntries
, error
))
1470 static sqlite3_stmt
*SecDbCopyStatementWithTailRange(SecDbConnectionRef dbconn
, CFStringRef sql
, CFRange
*sqlTail
, CFErrorRef
*error
) {
1471 __block sqlite3_stmt
*stmt
= NULL
;
1472 if (sql
) CFStringPerformWithCStringAndLength(sql
, ^(const char *sqlStr
, size_t sqlLen
) {
1473 const char *tail
= NULL
;
1474 stmt
= SecDbPrepareV2(dbconn
, sqlStr
, sqlLen
, &tail
, error
);
1475 if (sqlTail
&& sqlStr
< tail
&& tail
< sqlStr
+ sqlLen
) {
1476 sqlTail
->location
= tail
- sqlStr
;
1477 sqlTail
->length
= sqlLen
- sqlTail
->location
;
1484 sqlite3_stmt
*SecDbCopyStmt(SecDbConnectionRef dbconn
, CFStringRef sql
, CFStringRef
*tail
, CFErrorRef
*error
) {
1485 // TODO: Add caching and cache lookup of statements
1486 CFRange sqlTail
= {};
1487 sqlite3_stmt
*stmt
= SecDbCopyStatementWithTailRange(dbconn
, sql
, &sqlTail
, error
);
1488 if (sqlTail
.length
> 0) {
1489 CFStringRef excess
= CFStringCreateWithSubstring(CFGetAllocator(sql
), sql
, sqlTail
);
1493 SecDbError(SQLITE_INTERNAL
, error
,
1494 CFSTR("prepare_v2: %@ unused sql: %@"),
1496 CFReleaseSafe(excess
);
1497 SecDbFinalize(stmt
, error
);
1505 TODO: Could do a hack here with a custom kCFAllocatorNULL allocator for a second CFRuntimeBase inside a SecDbStatement,
1506 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. */
1507 bool SecDbReleaseCachedStmt(SecDbConnectionRef dbconn
, CFStringRef sql
, sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
1509 return SecDbFinalize(stmt
, error
);
1514 bool SecDbPrepare(SecDbConnectionRef dbconn
, CFStringRef sql
, CFErrorRef
*error
, void(^exec
)(sqlite3_stmt
*stmt
)) {
1515 assert(sql
!= NULL
);
1516 sqlite3_stmt
*stmt
= SecDbCopyStmt(dbconn
, sql
, NULL
, error
);
1521 return SecDbReleaseCachedStmt(dbconn
, sql
, stmt
, error
);
1524 bool SecDbWithSQL(SecDbConnectionRef dbconn
, CFStringRef sql
, CFErrorRef
*error
, bool(^perform
)(sqlite3_stmt
*stmt
)) {
1528 CFStringRef tail
= NULL
;
1530 sqlite3_stmt
*stmt
= SecDbCopyStmt(dbconn
, sql
, &tail
, error
);
1536 // TODO: Use a different error scope here.
1537 ok
= SecError(-50 /* errSecParam */, error
, CFSTR("SecDbWithSQL perform block missing"));
1539 ok
&= SecDbReleaseCachedStmt(dbconn
, sql
, stmt
, error
);
1542 // TODO We already have an error here we really just want the left over sql in it's userData
1543 ok
= SecDbError(SQLITE_ERROR
, error
, CFSTR("Error with unexecuted sql remaining %@"), sql
);
1552 /* SecDbForEach returns true if all SQLITE_ROW returns of sqlite3_step() return true from the row block.
1553 If the row block returns false and doesn't set an error (to indicate it has reached a limit),
1554 this entire function returns false. In that case no error will be set. */
1555 bool SecDbForEach(SecDbConnectionRef dbconn
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^row
)(int row_index
)) {
1556 bool result
= false;
1557 for (int row_ix
= 0;;++row_ix
) {
1558 int s3e
= sqlite3_step(stmt
);
1559 if (s3e
== SQLITE_ROW
) {
1565 // If we have no row block then getting SQLITE_ROW is an error
1566 SecDbError(s3e
, error
,
1567 CFSTR("step[%d]: %s returned SQLITE_ROW with NULL row block"),
1568 row_ix
, sqlite3_sql(stmt
));
1571 if (s3e
== SQLITE_DONE
) {
1574 SecDbConnectionCheckCode(dbconn
, s3e
, error
, CFSTR("SecDbForEach step[%d]"), row_ix
);
1582 bool SecDbForEach(sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^row
)(int row_index
)) {
1585 switch (_SecDbStep(dbconn
, stmt
, error
)) {
1586 case kSecDbErrorStep
:
1593 SecDbError(SQLITE_ERROR
, error
, CFSTR("SecDbStep SQLITE_ROW returned without a row handler"));
1596 case kSecDbDoneStep
:
1603 void SecDbRecordChange(SecDbConnectionRef dbconn
, CFTypeRef deleted
, CFTypeRef inserted
) {
1604 if (!dbconn
->db
->notifyPhase
) return;
1605 CFTypeRef entry
= SecDbEventCreateWithComponents(deleted
, inserted
);
1607 CFArrayAppendValue(dbconn
->changes
, entry
);
1610 if (!dbconn
->inTransaction
) {
1611 secerror("db %@ changed outside txn", dbconn
);
1612 // Only notify of DidCommit, since WillCommit code assumes
1614 SecDbOnNotify(dbconn
, ^{
1615 SecDbNotifyPhase(dbconn
, kSecDbTransactionDidCommit
);
1622 CFGiblisFor(SecDbConnection
)
1625 // SecDbEvent Creation and consumption
1628 static SecDbEventRef
SecDbEventCreateInsert(CFTypeRef inserted
) {
1629 return CFRetainSafe(inserted
);
1632 static SecDbEventRef
SecDbEventCreateDelete(CFTypeRef deleted
) {
1633 return CFArrayCreate(kCFAllocatorDefault
, &deleted
, 1, &kCFTypeArrayCallBacks
);
1636 static SecDbEventRef
SecDbEventCreateUpdate(CFTypeRef deleted
, CFTypeRef inserted
) {
1637 const void *values
[2] = { deleted
, inserted
};
1638 return CFArrayCreate(kCFAllocatorDefault
, values
, 2, &kCFTypeArrayCallBacks
);
1641 SecDbEventRef
SecDbEventCreateWithComponents(CFTypeRef deleted
, CFTypeRef inserted
) {
1642 if (deleted
&& inserted
)
1643 return SecDbEventCreateUpdate(deleted
, inserted
);
1645 return SecDbEventCreateDelete(deleted
);
1647 return SecDbEventCreateInsert(inserted
);
1652 void SecDbEventTranslateComponents(SecDbEventRef item
, CFTypeRef
* deleted
, CFTypeRef
* inserted
) {
1653 if(CFGetTypeID(item
) == CFArrayGetTypeID()) {
1654 // One item: deletion. Two: update.
1655 CFIndex arraySize
= CFArrayGetCount(item
);
1656 if(arraySize
== 1) {
1657 if(deleted
) { *deleted
= CFArrayGetValueAtIndex(item
, 0); }
1658 if(inserted
) { *inserted
= NULL
; }
1659 } else if(arraySize
== 2) {
1660 if(deleted
) { *deleted
= CFArrayGetValueAtIndex(item
, 0); }
1661 if(inserted
) { *inserted
= CFArrayGetValueAtIndex(item
, 1); }
1663 if(deleted
) { *deleted
= NULL
; }
1664 if(inserted
) { *inserted
= NULL
; }
1667 if(deleted
) { *deleted
= NULL
; }
1668 if(inserted
) { *inserted
= item
; }
1673 bool SecDbEventGetComponents(SecDbEventRef event
, CFTypeRef
*deleted
, CFTypeRef
*inserted
, CFErrorRef
*error
) {
1674 if (isArray(event
)) {
1675 CFArrayRef array
= event
;
1676 switch (CFArrayGetCount(array
)) {
1678 *deleted
= CFArrayGetValueAtIndex(array
, 0);
1679 *inserted
= CFArrayGetValueAtIndex(array
, 1);
1682 *deleted
= CFArrayGetValueAtIndex(array
, 0);
1686 SecError(errSecParam
, error
, NULL
, CFSTR("invalid entry in changes array: %@"), array
);