2 * Copyright (c) 2012-2014 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>
33 #include <AssertMacros.h>
34 #include "SecCFWrappers.h"
35 #include "SecCFError.h"
36 #include "SecIOFormat.h"
38 #include "Security/SecBase.h"
42 // Architecturally inverted files
43 // These are in SecureObjectSync but utilities depends on them
44 // <rdar://problem/20802079> Fix layer violation (SOSDigestVector, SOSManifest, SecDB.c)
46 #include <Security/SecureObjectSync/SOSDigestVector.h>
47 #include <Security/SecureObjectSync/SOSManifest.h>
49 #define LOGE(ARG,...) secerror(ARG, ## __VA_ARGS__)
50 #define LOGV(ARG,...) secdebug("secdb", ARG, ## __VA_ARGS__)
51 #define LOGD(ARG,...) secdebug("secdb", ARG, ## __VA_ARGS__)
53 #define HAVE_UNLOCK_NOTIFY 0
54 #define USE_BUSY_HANDLER 1
56 struct __OpaqueSecDbStatement
{
59 SecDbConnectionRef dbconn
;
63 struct __OpaqueSecDbConnection
{
66 //CFMutableDictionaryRef statements;
68 SecDbRef db
; // NONRETAINED, since db or block retains us
71 SecDbTransactionSource source
;
73 CFErrorRef corruptionError
;
75 // Pending deletions and additions for the current transaction
76 // Entires are either:
77 // 1) a CFArrayRef of 1 element representing a deletion,
78 // 2) a CFArrayRef of 2 elements representing the element 0 having been replaced with element 1
79 // 3) a CFTypeRef that is not a CFArrayRef, representing an add of the element in question.
80 CFMutableArrayRef changes
;
83 struct __OpaqueSecDb
{
87 dispatch_queue_t queue
;
88 CFMutableArrayRef connections
;
89 dispatch_semaphore_t write_semaphore
;
90 dispatch_semaphore_t read_semaphore
;
92 bool (^opened
)(SecDbConnectionRef dbconn
, bool didCreate
, bool *callMeAgainForNextConnection
, CFErrorRef
*error
);
93 bool callOpenedHandlerForNextConnection
;
94 dispatch_queue_t notifyQueue
;
95 SecDBNotifyBlock notifyPhase
;
98 // MARK: Error domains and error helper functions
100 CFStringRef kSecDbErrorDomain
= CFSTR("com.apple.utilities.sqlite3");
102 bool SecDbError(int sql_code
, CFErrorRef
*error
, CFStringRef format
, ...) {
103 if (sql_code
== SQLITE_OK
) return true;
106 CFIndex code
= sql_code
;
107 CFErrorRef previousError
= *error
;
110 va_start(args
, format
);
111 SecCFCreateErrorWithFormatAndArguments(code
, kSecDbErrorDomain
, previousError
, error
, NULL
, format
, args
);
112 CFReleaseNull(previousError
);
118 bool SecDbErrorWithDb(int sql_code
, sqlite3
*db
, CFErrorRef
*error
, CFStringRef format
, ...) {
119 if (sql_code
== SQLITE_OK
) return true;
122 va_start(args
, format
);
123 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
126 int extended_code
= sqlite3_extended_errcode(db
);
127 if (sql_code
== extended_code
)
128 SecDbError(sql_code
, error
, CFSTR("%@: [%d] %s"), message
, sql_code
, sqlite3_errmsg(db
));
130 SecDbError(sql_code
, error
, CFSTR("%@: [%d->%d] %s"), message
, sql_code
, extended_code
, sqlite3_errmsg(db
));
131 CFReleaseSafe(message
);
136 bool SecDbErrorWithStmt(int sql_code
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, CFStringRef format
, ...) {
137 if (sql_code
== SQLITE_OK
) return true;
140 va_start(args
, format
);
141 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
144 sqlite3
*db
= sqlite3_db_handle(stmt
);
145 const char *sql
= sqlite3_sql(stmt
);
146 int extended_code
= sqlite3_extended_errcode(db
);
147 if (sql_code
== extended_code
)
148 SecDbError(sql_code
, error
, CFSTR("%@: [%d] %s sql: %s"), message
, sql_code
, sqlite3_errmsg(db
), sql
);
150 SecDbError(sql_code
, error
, CFSTR("%@: [%d->%d] %s sql: %s"), message
, sql_code
, extended_code
, sqlite3_errmsg(db
), sql
);
151 CFReleaseSafe(message
);
158 // MARK: Static helper functions
160 static bool SecDbOpenHandle(SecDbConnectionRef dbconn
, bool *created
, CFErrorRef
*error
);
161 static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn
, int rc
, CFErrorRef
*error
);
164 #pragma mark SecDbRef
167 SecDbCopyFormatDescription(CFTypeRef value
, CFDictionaryRef formatOptions
)
169 SecDbRef db
= (SecDbRef
)value
;
170 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<SecDb path:%@ connections: %@>"), db
->db_path
, db
->connections
);
175 SecDbDestroy(CFTypeRef value
)
177 SecDbRef db
= (SecDbRef
)value
;
178 CFReleaseSafe(db
->connections
);
179 CFReleaseSafe(db
->db_path
);
180 dispatch_release(db
->queue
);
181 dispatch_release(db
->read_semaphore
);
182 dispatch_release(db
->write_semaphore
);
184 Block_release(db
->opened
);
190 SecDbCreate(CFStringRef dbName
,
191 bool (^opened
)(SecDbConnectionRef dbconn
, bool didCreate
, bool *callMeAgainForNextConnection
, CFErrorRef
*error
))
195 db
= CFTypeAllocate(SecDb
, struct __OpaqueSecDb
, kCFAllocatorDefault
);
196 require(db
!= NULL
, done
);
198 CFStringPerformWithCString(dbName
, ^(const char *dbNameStr
) {
199 db
->queue
= dispatch_queue_create(dbNameStr
, DISPATCH_QUEUE_SERIAL
);
201 db
->read_semaphore
= dispatch_semaphore_create(kSecDbMaxReaders
);
202 db
->write_semaphore
= dispatch_semaphore_create(kSecDbMaxWriters
);
203 db
->connections
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
204 db
->opened
= opened
? Block_copy(opened
) : NULL
;
205 if (getenv("__OSINSTALL_ENVIRONMENT") != NULL
) {
206 // TODO: Move this code out of this layer
207 LOGV("sqlDb: running from installer");
208 db
->db_path
= CFSTR("file::memory:?cache=shared");
210 db
->db_path
= CFStringCreateCopy(kCFAllocatorDefault
, dbName
);
217 SecDbIdleConnectionCount(SecDbRef db
) {
218 __block CFIndex count
= 0;
219 dispatch_sync(db
->queue
, ^{
220 count
= CFArrayGetCount(db
->connections
);
225 void SecDbSetNotifyPhaseBlock(SecDbRef db
, dispatch_queue_t queue
, SecDBNotifyBlock notifyPhase
) {
227 dispatch_release(db
->notifyQueue
);
229 Block_release(db
->notifyPhase
);
232 db
->notifyQueue
= queue
;
233 dispatch_retain(db
->notifyQueue
);
235 db
->notifyQueue
= NULL
;
238 db
->notifyPhase
= Block_copy(notifyPhase
);
240 db
->notifyPhase
= NULL
;
243 static void SecDbNotifyPhase(SecDbConnectionRef dbconn
, SecDbTransactionPhase phase
) {
244 if (CFArrayGetCount(dbconn
->changes
)) {
245 CFArrayRef changes
= dbconn
->changes
;
246 dbconn
->changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
247 if (dbconn
->db
->notifyPhase
)
248 dbconn
->db
->notifyPhase(dbconn
, phase
, dbconn
->source
, changes
);
249 CFReleaseSafe(changes
);
253 static void SecDbOnNotifyQueue(SecDbConnectionRef dbconn
, void (^perform
)()) {
254 if (dbconn
->db
->notifyQueue
) {
255 dispatch_sync(dbconn
->db
->notifyQueue
, ^{
263 CFStringRef
SecDbGetPath(SecDbRef db
) {
269 #pragma mark SecDbConnectionRef
271 static bool SecDbCheckCorrupted(SecDbConnectionRef dbconn
)
273 __block
bool isCorrupted
= true;
274 __block CFErrorRef error
= NULL
;
275 SecDbPrepare(dbconn
, CFSTR("PRAGMA integrity_check"), &error
, ^(sqlite3_stmt
*stmt
) {
276 SecDbStep(dbconn
, stmt
, &error
, ^(bool *stop
) {
277 const char * result
= (const char*)sqlite3_column_text(stmt
, 0);
278 if (result
&& strncasecmp(result
, "ok", 3) == 0) {
284 LOGV("sqlDb: warning error %@ when running integrity check", error
);
290 static bool SecDbDidCreateFirstConnection(SecDbConnectionRef dbconn
, bool didCreate
, CFErrorRef
*error
)
292 LOGD("sqlDb: starting maintenance");
295 if (!didCreate
&& !dbconn
->isCorrupted
) {
296 dbconn
->isCorrupted
= SecDbCheckCorrupted(dbconn
);
297 if (dbconn
->isCorrupted
)
298 secerror("integrity check=fail");
300 LOGD("sqlDb: integrity check=pass");
303 if (!dbconn
->isCorrupted
&& dbconn
->db
->opened
) {
304 CFErrorRef localError
= NULL
;
306 dbconn
->db
->callOpenedHandlerForNextConnection
= false;
307 ok
= dbconn
->db
->opened(dbconn
, didCreate
, &dbconn
->db
->callOpenedHandlerForNextConnection
, &localError
);
310 secerror("opened block failed: %@", localError
);
312 if (!dbconn
->isCorrupted
&& error
&& *error
== NULL
) {
316 secerror("opened block failed: error is released and lost");
317 CFReleaseNull(localError
);
321 if (dbconn
->isCorrupted
) {
322 ok
= SecDbHandleCorrupt(dbconn
, 0, error
);
325 LOGD("sqlDb: finished maintenance");
329 void SecDbCorrupt(SecDbConnectionRef dbconn
, CFErrorRef error
)
331 dbconn
->isCorrupted
= true;
332 CFAssignRetained(dbconn
->corruptionError
, error
);
336 static uint8_t knownDbPathIndex(SecDbConnectionRef dbconn
)
339 if(CFEqual(dbconn
->db
->db_path
, CFSTR("/Library/Keychains/keychain-2.db")))
341 if(CFEqual(dbconn
->db
->db_path
, CFSTR("/Library/Keychains/ocspcache.sqlite3")))
343 if(CFEqual(dbconn
->db
->db_path
, CFSTR("/Library/Keychains/TrustStore.sqlite3")))
345 if(CFEqual(dbconn
->db
->db_path
, CFSTR("/Library/Keychains/caissuercache.sqlite3")))
348 /* Unknown DB path */
353 // Return true if there was no error, returns false otherwise and set *error to an appropriate CFErrorRef.
354 static bool SecDbConnectionCheckCode(SecDbConnectionRef dbconn
, int code
, CFErrorRef
*error
, CFStringRef desc
, ...) {
355 if (code
== SQLITE_OK
|| code
== SQLITE_DONE
)
360 va_start(args
, desc
);
361 CFStringRef msg
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, desc
, args
);
363 SecDbErrorWithDb(code
, dbconn
->handle
, error
, msg
);
367 /* If it's already corrupted, don't try to recover */
368 if (dbconn
->isCorrupted
) {
369 CFStringRef reason
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("SQL DB %@ is corrupted already. Not trying to recover"), dbconn
->db
->db_path
);
370 secerror("%@",reason
);
371 __security_simulatecrash(reason
, __sec_exception_code_TwiceCorruptDb(knownDbPathIndex(dbconn
)));
372 CFReleaseSafe(reason
);
376 dbconn
->isCorrupted
= (SQLITE_CORRUPT
== code
) || (SQLITE_NOTADB
== code
) || (SQLITE_IOERR
== code
) || (SQLITE_CANTOPEN
== code
);
377 if (dbconn
->isCorrupted
) {
378 /* Run integrity check and only make dbconn->isCorrupted true and
379 run the corruption handler if the integrity check conclusively fails. */
380 dbconn
->isCorrupted
= SecDbCheckCorrupted(dbconn
);
381 if (dbconn
->isCorrupted
) {
382 secerror("operation returned code: %d integrity check=fail", code
);
383 SecDbHandleCorrupt(dbconn
, code
, error
);
385 secerror("operation returned code: %d: integrity check=pass", code
);
392 #if HAVE_UNLOCK_NOTIFY
394 static void SecDbUnlockNotify(void **apArg
, int nArg
) {
396 for(i
=0; i
<nArg
; i
++) {
397 dispatch_semaphore_t dsema
= (dispatch_semaphore_t
)apArg
[i
];
398 dispatch_semaphore_signal(dsema
);
402 static bool SecDbWaitForUnlockNotify(SecDbConnectionRef dbconn
, sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
404 dispatch_semaphore_t dsema
= dispatch_semaphore_create(0);
405 rc
= sqlite3_unlock_notify(dbconn
->handle
, SecDbUnlockNotify
, dsema
);
406 assert(rc
== SQLITE_LOCKED
|| rc
== SQLITE_OK
);
407 if (rc
== SQLITE_OK
) {
408 dispatch_semaphore_wait(dsema
, DISPATCH_TIME_FOREVER
);
410 dispatch_release(dsema
);
411 return (rc
== SQLITE_OK
414 ? SecDbErrorWithStmt(rc
, stmt
, error
, CFSTR("sqlite3_unlock_notify"))
415 : SecDbErrorWithDb(rc
, dbconn
->handle
, error
, CFSTR("sqlite3_unlock_notify"))));
422 // Return 0 to stop retrying.
423 static int SecDbHandleBusy(void *ctx
, int retryCount
) {
424 SecDbConnectionRef dbconn __unused
= ctx
;
425 struct timespec sleeptime
= { .tv_sec
= 0, .tv_nsec
= 10000 };
426 while (retryCount
--) {
427 // Double sleeptime until we hit one second then add one
428 // second more every time we sleep.
429 if (sleeptime
.tv_sec
) {
432 sleeptime
.tv_nsec
*= 2;
433 if (sleeptime
.tv_nsec
> NSEC_PER_SEC
) {
434 sleeptime
.tv_nsec
= 0;
439 struct timespec unslept
= {};
440 nanosleep(&sleeptime
, &unslept
);
445 static bool SecDbBusyHandler(SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
446 return SecDbErrorWithDb(sqlite3_busy_handler(dbconn
->handle
, SecDbHandleBusy
, dbconn
), dbconn
->handle
, error
, CFSTR("busy_handler"));
449 #endif // USE_BUSY_HANDLER
451 // Return true causes the operation to be tried again.
452 static bool SecDbWaitIfNeeded(SecDbConnectionRef dbconn
, int s3e
, sqlite3_stmt
*stmt
, CFStringRef desc
, struct timespec
*sleeptime
, CFErrorRef
*error
) {
453 #if HAVE_UNLOCK_NOTIFY
454 if (s3e
== SQLITE_LOCKED
) { // Optionally check for extended code being SQLITE_LOCKED_SHAREDCACHE
455 return SecDbWaitForUnlockNotify(dbconn
, stmt
, error
))
459 #if !USE_BUSY_HANDLER
460 if (s3e
== SQLITE_LOCKED
|| s3e
== SQLITE_BUSY
) {
461 LOGV("sqlDb: %s", sqlite3_errmsg(dbconn
->handle
));
462 while (s3e
== SQLITE_LOCKED
|| s3e
== SQLITE_BUSY
) {
463 struct timespec unslept
= {};
464 nanosleep(sleeptime
, &unslept
);
467 s3e
= sqlite3_reset(stmt
);
469 // Double sleeptime until we hit one second the add one
470 // second more every time we sleep.
471 if (sleeptime
->tv_sec
) {
474 sleeptime
->tv_nsec
*= 2;
475 if (sleeptime
->tv_nsec
> NSEC_PER_SEC
) {
476 sleeptime
->tv_nsec
= 0;
482 return SecDbErrorWithStmt(s3e
, stmt
, error
, CFSTR("reset"));
484 #endif // !USE_BUSY_HANDLER
486 return SecDbConnectionCheckCode(dbconn
, s3e
, error
, desc
);
491 enum SecDbStepResult
{
496 typedef enum SecDbStepResult SecDbStepResult
;
498 static SecDbStepResult
_SecDbStep(SecDbConnectionRef dbconn
, sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
499 assert(stmt
!= NULL
);
501 struct timespec sleeptime
= { .tv_sec
= 0, .tv_nsec
= 10000 };
503 s3e
= sqlite3_step(stmt
);
504 if (s3e
== SQLITE_ROW
)
505 return kSecDbRowStep
;
506 else if (s3e
== SQLITE_DONE
)
507 return kSecDbDoneStep
;
508 else if (!SecDbWaitIfNeeded(dbconn
, s3e
, stmt
, CFSTR("step"), &sleeptime
, error
))
509 return kSecDbErrorStep
;
514 SecDbExec(SecDbConnectionRef dbconn
, CFStringRef sql
, CFErrorRef
*error
)
519 CFStringRef tail
= NULL
;
521 sqlite3_stmt
*stmt
= SecDbCopyStmt(dbconn
, sql
, &tail
, error
);
525 while ((sr
= _SecDbStep(dbconn
, stmt
, error
)) == kSecDbRowStep
);
526 if (sr
== kSecDbErrorStep
)
528 ok
&= SecDbReleaseCachedStmt(dbconn
, sql
, stmt
, error
);
531 // TODO We already have an error here we really just want the left over sql in it's userData
532 ok
= SecDbError(SQLITE_ERROR
, error
, CFSTR("Error with unexecuted sql remaining %@"), sql
);
540 static bool SecDbBeginTransaction(SecDbConnectionRef dbconn
, SecDbTransactionType type
, CFErrorRef
*error
)
545 case kSecDbImmediateTransactionType
:
546 query
= CFSTR("BEGIN IMMEDATE");
548 case kSecDbExclusiveRemoteTransactionType
:
549 dbconn
->source
= kSecDbSOSTransaction
;
550 case kSecDbExclusiveTransactionType
:
551 query
= CFSTR("BEGIN EXCLUSIVE");
553 case kSecDbNormalTransactionType
:
554 query
= CFSTR("BEGIN");
557 ok
= SecDbError(SQLITE_ERROR
, error
, CFSTR("invalid transaction type %" PRIu32
), type
);
562 if (query
!= NULL
&& sqlite3_get_autocommit(dbconn
->handle
) != 0) {
563 ok
= SecDbExec(dbconn
, query
, error
);
566 dbconn
->inTransaction
= true;
571 static bool SecDbEndTransaction(SecDbConnectionRef dbconn
, bool commit
, CFErrorRef
*error
)
573 __block
bool ok
= true;
574 SecDbOnNotifyQueue(dbconn
, ^{
575 bool commited
= false;
577 SecDbNotifyPhase(dbconn
, kSecDbTransactionWillCommit
);
578 commited
= ok
= SecDbExec(dbconn
, CFSTR("END"), error
);
580 ok
= SecDbExec(dbconn
, CFSTR("ROLLBACK"), error
);
583 dbconn
->inTransaction
= false;
584 SecDbNotifyPhase(dbconn
, commited
? kSecDbTransactionDidCommit
: kSecDbTransactionDidRollback
);
585 dbconn
->source
= kSecDbAPITransaction
;
590 bool SecDbTransaction(SecDbConnectionRef dbconn
, SecDbTransactionType type
,
591 CFErrorRef
*error
, void (^transaction
)(bool *commit
))
596 if (dbconn
->inTransaction
) {
597 transaction(&commit
);
599 LOGV("sqlDb: nested transaction asked to not be committed");
602 ok
= SecDbBeginTransaction(dbconn
, type
, error
);
604 transaction(&commit
);
605 ok
= SecDbEndTransaction(dbconn
, commit
, error
);
613 sqlite3
*SecDbHandle(SecDbConnectionRef dbconn
) {
614 return dbconn
->handle
;
617 bool SecDbStep(SecDbConnectionRef dbconn
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, void (^row
)(bool *stop
)) {
619 switch (_SecDbStep(dbconn
, stmt
, error
)) {
620 case kSecDbErrorStep
:
630 SecDbError(SQLITE_ERROR
, error
, CFSTR("SecDbStep SQLITE_ROW returned without a row handler"));
638 bool SecDbCheckpoint(SecDbConnectionRef dbconn
, CFErrorRef
*error
)
640 return SecDbConnectionCheckCode(dbconn
, sqlite3_wal_checkpoint(dbconn
->handle
, NULL
), error
, CFSTR("wal_checkpoint"));
643 static bool SecDbFileControl(SecDbConnectionRef dbconn
, int op
, void *arg
, CFErrorRef
*error
) {
644 return SecDbConnectionCheckCode(dbconn
, sqlite3_file_control(dbconn
->handle
, NULL
, op
, arg
), error
, CFSTR("file_control"));
647 static sqlite3
*_SecDbOpenV2(const char *path
, int flags
, CFErrorRef
*error
) {
648 #if HAVE_UNLOCK_NOTIFY
649 flags
|= SQLITE_OPEN_SHAREDCACHE
;
651 sqlite3
*handle
= NULL
;
652 int s3e
= sqlite3_open_v2(path
, &handle
, flags
, NULL
);
655 SecDbErrorWithDb(s3e
, handle
, error
, CFSTR("open_v2 \"%s\" 0x%X"), path
, flags
);
656 sqlite3_close(handle
);
659 SecDbError(s3e
, error
, CFSTR("open_v2 \"%s\" 0x%X"), path
, flags
);
665 static bool SecDbOpenV2(SecDbConnectionRef dbconn
, const char *path
, int flags
, CFErrorRef
*error
) {
666 return (dbconn
->handle
= _SecDbOpenV2(path
, flags
, error
)) != NULL
;
669 static bool SecDbTruncate(SecDbConnectionRef dbconn
, CFErrorRef
*error
)
671 int flags
= SQLITE_TRUNCATE_JOURNALMODE_WAL
| SQLITE_TRUNCATE_AUTOVACUUM_FULL
;
672 __block
bool ok
= SecDbFileControl(dbconn
, SQLITE_TRUNCATE_DATABASE
, &flags
, error
);
674 sqlite3_close(dbconn
->handle
);
675 dbconn
->handle
= NULL
;
676 CFStringPerformWithCString(dbconn
->db
->db_path
, ^(const char *path
) {
678 CFReleaseNull(*error
);
679 if (SecCheckErrno(unlink(path
), error
, CFSTR("unlink %s"), path
)) {
680 ok
= SecDbOpenHandle(dbconn
, NULL
, error
);
684 secerror("Failed to delete db handle: %@", error
? *error
: NULL
);
692 static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn
, int rc
, CFErrorRef
*error
)
694 CFStringRef reason
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("SQL DB %@ is corrupted, trying to recover (rc=%d) %@"), dbconn
->db
->db_path
, rc
, dbconn
->corruptionError
);
695 __security_simulatecrash(reason
, __sec_exception_code_CorruptDb(knownDbPathIndex(dbconn
), rc
));
696 CFReleaseSafe(reason
);
698 // Backup current db.
699 __block
bool didRename
= false;
700 CFStringPerformWithCString(dbconn
->db
->db_path
, ^(const char *db_path
) {
701 sqlite3
*corrupt_db
= NULL
;
702 char buf
[PATH_MAX
+1];
703 snprintf(buf
, sizeof(buf
), "%s-corrupt", db_path
);
704 if (dbconn
->handle
&& (corrupt_db
= _SecDbOpenV2(buf
, SQLITE_OPEN_READWRITE
, error
))) {
706 didRename
= SecDbErrorWithDb(sqlite3_file_control(corrupt_db
, NULL
, SQLITE_FCNTL_PERSIST_WAL
, &on
), corrupt_db
, error
, CFSTR("persist wal"));
707 didRename
&= SecDbErrorWithDb(sqlite3_file_control(corrupt_db
, NULL
, SQLITE_REPLACE_DATABASE
, (void *)dbconn
->handle
), corrupt_db
, error
, CFSTR("replace database"));
708 sqlite3_close(corrupt_db
);
712 secerror("Tried to rename corrupt database at path %@, but we failed: %@, trying explicit rename", dbconn
->db
->db_path
, error
? *error
: NULL
);
714 CFReleaseNull(*error
);
716 didRename
= SecCheckErrno(rename(db_path
, buf
), error
, CFSTR("rename %s %s"), db_path
, buf
) &&
717 (!dbconn
->handle
|| SecDbError(sqlite3_close(dbconn
->handle
), error
, CFSTR("close"))) &&
718 SecDbOpenHandle(dbconn
, NULL
, error
);
721 secerror("Database at path %@ is corrupt. Copied it to %s for further investigation.", dbconn
->db
->db_path
, buf
);
723 seccritical("Tried to copy corrupt database at path %@, but we failed: %@", dbconn
->db
->db_path
, error
? *error
: NULL
);
727 bool ok
= (didRename
&&
728 (dbconn
->handle
|| SecDbOpenHandle(dbconn
, NULL
, error
)) &&
729 SecDbTruncate(dbconn
, error
));
731 // Mark the db as not corrupted, even if something failed.
732 // Always note we are no longer in the corruption handler
733 dbconn
->isCorrupted
= false;
735 // Invoke our callers opened callback, since we just created a new database
736 if (ok
&& dbconn
->db
->opened
) {
737 dbconn
->db
->callOpenedHandlerForNextConnection
= false;
738 ok
= dbconn
->db
->opened(dbconn
, true, &dbconn
->db
->callOpenedHandlerForNextConnection
, error
);
744 static bool SecDbProfileEnabled(void)
747 static dispatch_once_t onceToken
;
748 static bool profile_enabled
= false;
751 //sudo defaults write /Library/Preferences/com.apple.security.auth profile -bool true
752 dispatch_once(&onceToken
, ^{
753 CFTypeRef profile
= (CFNumberRef
)CFPreferencesCopyValue(CFSTR("profile"), CFSTR(SECURITY_AUTH_NAME
), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
755 if (profile
&& CFGetTypeID(profile
) == CFBooleanGetTypeID()) {
756 profile_enabled
= CFBooleanGetValue((CFBooleanRef
)profile
);
759 LOGV("sqlDb: sql profile: %s", profile_enabled
? "enabled" : "disabled");
761 CFReleaseSafe(profile
);
765 return profile_enabled
;
776 static void SecDbProfile(void *context __unused
, const char *sql
, sqlite3_uint64 ns
) {
777 LOGV("==\nsqlDb: %s\nTime: %llu ms\n", sql
, ns
>> 20);
780 static void SecDbProfile(void *context
, const char *sql
, sqlite3_uint64 ns
) {
781 sqlite3
*s3h
= context
;
782 int code
= sqlite3_extended_errcode(s3h
);
783 if (code
== SQLITE_OK
|| code
== SQLITE_DONE
) {
784 secdebug("profile", "==\nsqlDb: %s\nTime: %llu ms\n", sql
, ns
>> 20);
786 secdebug("profile", "==error[%d]: %s==\nsqlDb: %s\nTime: %llu ms \n", code
, sqlite3_errmsg(s3h
), sql
, ns
>> 20);
791 static bool SecDbTraceEnabled(void)
800 static void SecDbTrace(void *ctx
, const char *trace
) {
801 SecDbConnectionRef dbconn __unused
= ctx
;
802 static dispatch_queue_t queue
;
803 static dispatch_once_t once
;
804 dispatch_once(&once
, ^{
805 queue
= dispatch_queue_create("trace_queue", DISPATCH_QUEUE_SERIAL
);
807 dispatch_sync(queue
, ^{
808 __security_debug(CFSTR("trace"), "", "", 0, CFSTR("%s"), trace
);
812 static bool SecDbOpenHandle(SecDbConnectionRef dbconn
, bool *created
, CFErrorRef
*error
)
814 __block
bool ok
= true;
815 CFStringPerformWithCString(dbconn
->db
->db_path
, ^(const char *db_path
) {
816 ok
= created
&& SecDbOpenV2(dbconn
, db_path
, SQLITE_OPEN_READWRITE
, NULL
);
820 char *tmp
= dirname((char *)db_path
);
822 int errnum
= mkpath_np(tmp
, 0700);
823 if (errnum
!= 0 && errnum
!= EEXIST
) {
824 SecCFCreateErrorWithFormat(errnum
, kSecErrnoDomain
, NULL
, error
, NULL
,
825 CFSTR("mkpath_np %s: [%d] %s"), tmp
, errnum
, strerror(errnum
));
830 ok
= ok
&& SecDbOpenV2(dbconn
, db_path
, SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE
, error
);
832 chmod(db_path
, S_IRUSR
| S_IWUSR
);
838 if (ok
&& SecDbProfileEnabled()) {
839 sqlite3_profile(dbconn
->handle
, SecDbProfile
, dbconn
->handle
);
841 if (ok
&& SecDbTraceEnabled()) {
842 sqlite3_trace(dbconn
->handle
, SecDbTrace
, dbconn
);
845 ok
= ok
&& SecDbBusyHandler(dbconn
, error
);
853 static SecDbConnectionRef
854 SecDbConnectionCreate(SecDbRef db
, bool readOnly
, CFErrorRef
*error
)
856 SecDbConnectionRef dbconn
= NULL
;
858 dbconn
= CFTypeAllocate(SecDbConnection
, struct __OpaqueSecDbConnection
, kCFAllocatorDefault
);
859 require(dbconn
!= NULL
, done
);
862 dbconn
->readOnly
= readOnly
;
863 dbconn
->inTransaction
= false;
864 dbconn
->source
= NULL
;
865 dbconn
->isCorrupted
= false;
866 dbconn
->corruptionError
= NULL
;
867 dbconn
->handle
= NULL
;
868 dbconn
->changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
874 static bool SecDbConnectionIsReadOnly(SecDbConnectionRef dbconn
) {
875 return dbconn
->readOnly
;
878 static void SecDbConectionSetReadOnly(SecDbConnectionRef dbconn
, bool readOnly
) {
879 dbconn
->readOnly
= readOnly
;
882 /* Read only connections go to the end of the queue, writeable connections
883 go to the start of the queue. */
884 SecDbConnectionRef
SecDbConnectionAquire(SecDbRef db
, bool readOnly
, CFErrorRef
*error
) {
886 secdebug("dbconn", "acquire %s connection", readOnly
? "ro" : "rw");
887 dispatch_semaphore_wait(readOnly
? db
->read_semaphore
: db
->write_semaphore
, DISPATCH_TIME_FOREVER
);
888 __block SecDbConnectionRef dbconn
= NULL
;
889 __block
bool ok
= true;
890 __block
bool ranOpenedHandler
= false;
891 dispatch_sync(db
->queue
, ^{
892 if (!db
->didFirstOpen
) {
893 bool didCreate
= false;
894 ok
= dbconn
= SecDbConnectionCreate(db
, false, error
);
895 CFErrorRef localError
= NULL
;
896 if (ok
&& !SecDbOpenHandle(dbconn
, &didCreate
, &localError
)) {
897 secerror("Unable to create database: %@", localError
);
898 if (localError
&& CFEqual(CFErrorGetDomain(localError
), kSecDbErrorDomain
)) {
899 int code
= (int)CFErrorGetCode(localError
);
900 dbconn
->isCorrupted
= (SQLITE_CORRUPT
== code
) || (SQLITE_NOTADB
== code
) || (SQLITE_IOERR
== code
) || (SQLITE_CANTOPEN
== code
);
902 // If the open failure isn't due to corruption, propagte the error.
903 ok
= dbconn
->isCorrupted
;
904 if (!ok
&& error
&& *error
== NULL
) {
909 CFReleaseNull(localError
);
912 db
->didFirstOpen
= ok
= SecDbDidCreateFirstConnection(dbconn
, didCreate
, error
);
913 ranOpenedHandler
= true;
916 CFReleaseNull(dbconn
);
918 /* Try to get one from the cache */
919 CFIndex count
= CFArrayGetCount(db
->connections
);
920 while (count
&& !dbconn
) {
921 CFIndex ix
= readOnly
? count
- 1 : 0;
922 dbconn
= (SecDbConnectionRef
)CFArrayGetValueAtIndex(db
->connections
, ix
);
926 secerror("got NULL dbconn at index: %" PRIdCFIndex
" skipping", ix
);
927 CFArrayRemoveValueAtIndex(db
->connections
, ix
);
933 /* Make sure the connection we found has the right access */
934 if (SecDbConnectionIsReadOnly(dbconn
) != readOnly
) {
935 SecDbConectionSetReadOnly(dbconn
, readOnly
);
938 /* Nothing found in cache, create a new connection */
939 bool created
= false;
940 dbconn
= SecDbConnectionCreate(db
, readOnly
, error
);
941 if (dbconn
&& !SecDbOpenHandle(dbconn
, &created
, error
)) {
942 CFReleaseNull(dbconn
);
946 if (dbconn
&& !ranOpenedHandler
&& dbconn
->db
->opened
) {
947 dispatch_sync(db
->queue
, ^{
948 if (dbconn
->db
->callOpenedHandlerForNextConnection
) {
949 dbconn
->db
->callOpenedHandlerForNextConnection
= false;
950 if (!dbconn
->db
->opened(dbconn
, false, &dbconn
->db
->callOpenedHandlerForNextConnection
, error
)) {
951 if (!dbconn
->isCorrupted
|| !SecDbHandleCorrupt(dbconn
, 0, error
)) {
952 CFReleaseNull(dbconn
);
960 // If aquire fails we need to signal the semaphore again.
961 dispatch_semaphore_signal(readOnly
? db
->read_semaphore
: db
->write_semaphore
);
968 void SecDbConnectionRelease(SecDbConnectionRef dbconn
) {
970 secerror("called with NULL dbconn");
973 SecDbRef db
= dbconn
->db
;
974 secdebug("dbconn", "release %@", dbconn
);
975 dispatch_sync(db
->queue
, ^{
976 CFIndex count
= CFArrayGetCount(db
->connections
);
977 // Add back possible writable dbconn to the pool.
978 bool readOnly
= SecDbConnectionIsReadOnly(dbconn
);
979 CFArrayInsertValueAtIndex(db
->connections
, readOnly
? count
: 0, dbconn
);
980 // Remove the last (probably read-only) dbconn from the pool.
981 if (count
>= kSecDbMaxIdleHandles
) {
982 CFArrayRemoveValueAtIndex(db
->connections
, count
);
984 // Signal after we have put the connection back in the pool of connections
985 dispatch_semaphore_signal(readOnly
? db
->read_semaphore
: db
->write_semaphore
);
991 bool SecDbPerformRead(SecDbRef db
, CFErrorRef
*error
, void (^perform
)(SecDbConnectionRef dbconn
)) {
992 SecDbConnectionRef dbconn
= SecDbConnectionAquire(db
, true, error
);
993 bool success
= false;
997 SecDbConnectionRelease(dbconn
);
1002 bool SecDbPerformWrite(SecDbRef db
, CFErrorRef
*error
, void (^perform
)(SecDbConnectionRef dbconn
)) {
1003 SecDbConnectionRef dbconn
= SecDbConnectionAquire(db
, false, error
);
1004 bool success
= false;
1008 SecDbConnectionRelease(dbconn
);
1014 SecDbConnectionCopyFormatDescription(CFTypeRef value
, CFDictionaryRef formatOptions
)
1016 SecDbConnectionRef dbconn
= (SecDbConnectionRef
)value
;
1017 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<SecDbConnection %s %s>"),
1018 dbconn
->readOnly
? "ro" : "rw", dbconn
->handle
? "open" : "closed");
1022 SecDbConnectionDestroy(CFTypeRef value
)
1024 SecDbConnectionRef dbconn
= (SecDbConnectionRef
)value
;
1025 if (dbconn
->handle
) {
1026 sqlite3_close(dbconn
->handle
);
1029 CFReleaseNull(dbconn
->changes
);
1030 CFReleaseNull(dbconn
->corruptionError
);
1036 // MARK: Bind helpers
1039 bool SecDbBindNull(sqlite3_stmt
*stmt
, int param
, CFErrorRef
*error
) {
1040 bool ok
= SecDbErrorWithStmt(sqlite3_bind_null(stmt
, param
),
1041 stmt
, error
, CFSTR("bind_null[%d]"), param
);
1042 secdebug("bind", "bind_null[%d]: %@", param
, error
? *error
: NULL
);
1047 bool SecDbBindBlob(sqlite3_stmt
*stmt
, int param
, const void *zData
, size_t n
, void(*xDel
)(void*), CFErrorRef
*error
) {
1049 return SecDbErrorWithStmt(SQLITE_TOOBIG
, stmt
, error
,
1050 CFSTR("bind_blob[%d]: blob bigger than INT_MAX"), param
);
1052 bool ok
= SecDbErrorWithStmt(sqlite3_bind_blob(stmt
, param
, zData
, (int)n
, xDel
),
1053 stmt
, error
, CFSTR("bind_blob[%d]"), param
);
1054 secdebug("bind", "bind_blob[%d]: %.*s: %@", param
, (int)n
, zData
, error
? *error
: NULL
);
1058 bool SecDbBindText(sqlite3_stmt
*stmt
, int param
, const char *zData
, size_t n
, void(*xDel
)(void*), CFErrorRef
*error
) {
1060 return SecDbErrorWithStmt(SQLITE_TOOBIG
, stmt
, error
,
1061 CFSTR("bind_text[%d]: text bigger than INT_MAX"), param
);
1063 bool ok
= SecDbErrorWithStmt(sqlite3_bind_text(stmt
, param
, zData
, (int)n
, xDel
), stmt
, error
,
1064 CFSTR("bind_text[%d]"), param
);
1065 secdebug("bind", "bind_text[%d]: \"%s\": %@", param
, zData
, error
? *error
: NULL
);
1069 bool SecDbBindDouble(sqlite3_stmt
*stmt
, int param
, double value
, CFErrorRef
*error
) {
1070 bool ok
= SecDbErrorWithStmt(sqlite3_bind_double(stmt
, param
, value
), stmt
, error
,
1071 CFSTR("bind_double[%d]"), param
);
1072 secdebug("bind", "bind_double[%d]: %f: %@", param
, value
, error
? *error
: NULL
);
1076 bool SecDbBindInt(sqlite3_stmt
*stmt
, int param
, int value
, CFErrorRef
*error
) {
1077 bool ok
= SecDbErrorWithStmt(sqlite3_bind_int(stmt
, param
, value
), stmt
, error
,
1078 CFSTR("bind_int[%d]"), param
);
1079 secdebug("bind", "bind_int[%d]: %d: %@", param
, value
, error
? *error
: NULL
);
1083 bool SecDbBindInt64(sqlite3_stmt
*stmt
, int param
, sqlite3_int64 value
, CFErrorRef
*error
) {
1084 bool ok
= SecDbErrorWithStmt(sqlite3_bind_int64(stmt
, param
, value
), stmt
, error
,
1085 CFSTR("bind_int64[%d]"), param
);
1086 secdebug("bind", "bind_int64[%d]: %lld: %@", param
, value
, error
? *error
: NULL
);
1091 /* AUDIT[securityd](done):
1092 value (ok) is a caller provided, non NULL CFTypeRef.
1094 bool SecDbBindObject(sqlite3_stmt
*stmt
, int param
, CFTypeRef value
, CFErrorRef
*error
) {
1096 __block
bool result
= false;
1098 /* TODO: Can we use SQLITE_STATIC below everwhere we currently use
1099 SQLITE_TRANSIENT since we finalize the statement before the value
1100 goes out of scope? */
1101 if (!value
|| (valueId
= CFGetTypeID(value
)) == CFNullGetTypeID()) {
1102 /* Skip bindings for NULL values. sqlite3 will interpret unbound
1103 params as NULL which is exactly what we want. */
1107 result
= SecDbBindNull(stmt
, param
, error
);
1109 } else if (valueId
== CFStringGetTypeID()) {
1110 CFStringPerformWithCStringAndLength(value
, ^(const char *cstr
, size_t clen
) {
1111 result
= SecDbBindText(stmt
, param
, cstr
, clen
, SQLITE_TRANSIENT
, error
);
1113 } else if (valueId
== CFDataGetTypeID()) {
1114 CFIndex len
= CFDataGetLength(value
);
1116 result
= SecDbBindBlob(stmt
, param
, CFDataGetBytePtr(value
),
1117 len
, SQLITE_TRANSIENT
, error
);
1119 result
= SecDbBindText(stmt
, param
, "", 0, SQLITE_TRANSIENT
, error
);
1121 } else if (valueId
== CFDateGetTypeID()) {
1122 CFAbsoluteTime abs_time
= CFDateGetAbsoluteTime(value
);
1123 result
= SecDbBindDouble(stmt
, param
, abs_time
, error
);
1124 } else if (valueId
== CFBooleanGetTypeID()) {
1125 int bval
= CFBooleanGetValue(value
);
1126 result
= SecDbBindInt(stmt
, param
, bval
, error
);
1127 } else if (valueId
== CFNumberGetTypeID()) {
1129 if (CFNumberIsFloatType(value
)) {
1131 convertOk
= CFNumberGetValue(value
, kCFNumberDoubleType
, &nval
);
1132 result
= SecDbBindDouble(stmt
, param
, nval
, error
);
1135 convertOk
= CFNumberGetValue(value
, kCFNumberSInt32Type
, &nval
);
1137 result
= SecDbBindInt(stmt
, param
, nval
, error
);
1139 sqlite_int64 nval64
;
1140 convertOk
= CFNumberGetValue(value
, kCFNumberSInt64Type
, &nval64
);
1142 result
= SecDbBindInt64(stmt
, param
, nval64
, error
);
1146 result
= SecDbError(SQLITE_INTERNAL
, error
, CFSTR("bind CFNumberGetValue failed for %@"), value
);
1150 CFStringRef valueDesc
= CFCopyTypeIDDescription(valueId
);
1151 SecDbError(SQLITE_MISMATCH
, error
, CFSTR("bind unsupported type %@"), valueDesc
);
1152 CFReleaseSafe(valueDesc
);
1160 // MARK: SecDbStatementRef
1162 bool SecDbReset(sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
1163 return SecDbErrorWithStmt(sqlite3_reset(stmt
), stmt
, error
, CFSTR("reset"));
1166 bool SecDbClearBindings(sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
1167 return SecDbErrorWithStmt(sqlite3_clear_bindings(stmt
), stmt
, error
, CFSTR("clear bindings"));
1170 bool SecDbFinalize(sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
1171 sqlite3
*handle
= sqlite3_db_handle(stmt
);
1172 int s3e
= sqlite3_finalize(stmt
);
1173 return s3e
== SQLITE_OK
? true : SecDbErrorWithDb(s3e
, handle
, error
, CFSTR("finalize: %p"), stmt
);
1176 sqlite3_stmt
*SecDbPrepareV2(SecDbConnectionRef dbconn
, const char *sql
, size_t sqlLen
, const char **sqlTail
, CFErrorRef
*error
) {
1177 sqlite3
*db
= SecDbHandle(dbconn
);
1178 if (sqlLen
> INT_MAX
) {
1179 SecDbErrorWithDb(SQLITE_TOOBIG
, db
, error
, CFSTR("prepare_v2: sql bigger than INT_MAX"));
1182 struct timespec sleeptime
= { .tv_sec
= 0, .tv_nsec
= 10000 };
1184 sqlite3_stmt
*stmt
= NULL
;
1185 int s3e
= sqlite3_prepare_v2(db
, sql
, (int)sqlLen
, &stmt
, sqlTail
);
1186 if (s3e
== SQLITE_OK
)
1188 else if (!SecDbWaitIfNeeded(dbconn
, s3e
, NULL
, CFSTR("preparev2"), &sleeptime
, error
))
1193 static sqlite3_stmt
*SecDbCopyStatementWithTailRange(SecDbConnectionRef dbconn
, CFStringRef sql
, CFRange
*sqlTail
, CFErrorRef
*error
) {
1194 __block sqlite3_stmt
*stmt
= NULL
;
1195 if (sql
) CFStringPerformWithCStringAndLength(sql
, ^(const char *sqlStr
, size_t sqlLen
) {
1196 const char *tail
= NULL
;
1197 stmt
= SecDbPrepareV2(dbconn
, sqlStr
, sqlLen
, &tail
, error
);
1198 if (sqlTail
&& sqlStr
< tail
&& tail
< sqlStr
+ sqlLen
) {
1199 sqlTail
->location
= tail
- sqlStr
;
1200 sqlTail
->length
= sqlLen
- sqlTail
->location
;
1207 sqlite3_stmt
*SecDbCopyStmt(SecDbConnectionRef dbconn
, CFStringRef sql
, CFStringRef
*tail
, CFErrorRef
*error
) {
1208 // TODO: Add caching and cache lookup of statements
1209 CFRange sqlTail
= {};
1210 sqlite3_stmt
*stmt
= SecDbCopyStatementWithTailRange(dbconn
, sql
, &sqlTail
, error
);
1211 if (sqlTail
.length
> 0) {
1212 CFStringRef excess
= CFStringCreateWithSubstring(CFGetAllocator(sql
), sql
, sqlTail
);
1216 SecDbError(SQLITE_INTERNAL
, error
,
1217 CFSTR("prepare_v2: %@ unused sql: %@"),
1219 CFReleaseSafe(excess
);
1220 SecDbFinalize(stmt
, error
);
1228 TODO: Could do a hack here with a custom kCFAllocatorNULL allocator for a second CFRuntimeBase inside a SecDbStatement,
1229 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. */
1230 bool SecDbReleaseCachedStmt(SecDbConnectionRef dbconn
, CFStringRef sql
, sqlite3_stmt
*stmt
, CFErrorRef
*error
) {
1232 return SecDbFinalize(stmt
, error
);
1237 bool SecDbPrepare(SecDbConnectionRef dbconn
, CFStringRef sql
, CFErrorRef
*error
, void(^exec
)(sqlite3_stmt
*stmt
)) {
1238 assert(sql
!= NULL
);
1239 sqlite3_stmt
*stmt
= SecDbCopyStmt(dbconn
, sql
, NULL
, error
);
1244 return SecDbReleaseCachedStmt(dbconn
, sql
, stmt
, error
);
1247 bool SecDbWithSQL(SecDbConnectionRef dbconn
, CFStringRef sql
, CFErrorRef
*error
, bool(^perform
)(sqlite3_stmt
*stmt
)) {
1251 CFStringRef tail
= NULL
;
1253 sqlite3_stmt
*stmt
= SecDbCopyStmt(dbconn
, sql
, &tail
, error
);
1259 // TODO: Use a different error scope here.
1260 ok
= SecError(-50 /* errSecParam */, error
, CFSTR("SecDbWithSQL perform block missing"));
1262 ok
&= SecDbReleaseCachedStmt(dbconn
, sql
, stmt
, error
);
1265 // TODO We already have an error here we really just want the left over sql in it's userData
1266 ok
= SecDbError(SQLITE_ERROR
, error
, CFSTR("Error with unexecuted sql remaining %@"), sql
);
1275 /* SecDbForEach returns true if all SQLITE_ROW returns of sqlite3_step() return true from the row block.
1276 If the row block returns false and doesn't set an error (to indicate it has reached a limit),
1277 this entire function returns false. In that case no error will be set. */
1278 bool SecDbForEach(sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^row
)(int row_index
)) {
1279 bool result
= false;
1280 for (int row_ix
= 0;;++row_ix
) {
1281 int s3e
= sqlite3_step(stmt
);
1282 if (s3e
== SQLITE_ROW
) {
1288 // If we have no row block then getting SQLITE_ROW is an error
1289 SecDbError(s3e
, error
,
1290 CFSTR("step[%d]: %s returned SQLITE_ROW with NULL row block"),
1291 row_ix
, sqlite3_sql(stmt
));
1294 if (s3e
== SQLITE_DONE
) {
1297 SecDbErrorWithStmt(s3e
, stmt
, error
, CFSTR("step[%d]"), row_ix
);
1305 bool SecDbForEach(sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^row
)(int row_index
)) {
1308 switch (_SecDbStep(dbconn
, stmt
, error
)) {
1309 case kSecDbErrorStep
:
1316 SecDbError(SQLITE_ERROR
, error
, CFSTR("SecDbStep SQLITE_ROW returned without a row handler"));
1319 case kSecDbDoneStep
:
1326 void SecDbRecordChange(SecDbConnectionRef dbconn
, CFTypeRef deleted
, CFTypeRef inserted
) {
1327 if (!dbconn
->db
->notifyPhase
) return;
1328 CFTypeRef entry
= SecDbEventCreateWithComponents(deleted
, inserted
);
1330 CFArrayAppendValue(dbconn
->changes
, entry
);
1333 if (!dbconn
->inTransaction
) {
1334 secerror("db %@ changed outside txn", dbconn
);
1335 // Only notify of DidCommit, since WillCommit code assumes
1337 SecDbOnNotifyQueue(dbconn
, ^{
1338 SecDbNotifyPhase(dbconn
, kSecDbTransactionDidCommit
);
1345 CFGiblisFor(SecDbConnection
)
1348 // SecDbEvent Creation and consumption
1351 static SecDbEventRef
SecDbEventCreateInsert(CFTypeRef inserted
) {
1352 return CFRetainSafe(inserted
);
1355 static SecDbEventRef
SecDbEventCreateDelete(CFTypeRef deleted
) {
1356 return CFArrayCreate(kCFAllocatorDefault
, &deleted
, 1, &kCFTypeArrayCallBacks
);
1359 static SecDbEventRef
SecDbEventCreateUpdate(CFTypeRef deleted
, CFTypeRef inserted
) {
1360 const void *values
[2] = { deleted
, inserted
};
1361 return CFArrayCreate(kCFAllocatorDefault
, values
, 2, &kCFTypeArrayCallBacks
);
1364 SecDbEventRef
SecDbEventCreateWithComponents(CFTypeRef deleted
, CFTypeRef inserted
) {
1365 if (deleted
&& inserted
)
1366 return SecDbEventCreateUpdate(deleted
, inserted
);
1368 return SecDbEventCreateDelete(deleted
);
1370 return SecDbEventCreateInsert(inserted
);
1375 bool SecDbEventGetComponents(SecDbEventRef event
, CFTypeRef
*deleted
, CFTypeRef
*inserted
, CFErrorRef
*error
) {
1376 if (isArray(event
)) {
1377 CFArrayRef array
= event
;
1378 switch (CFArrayGetCount(array
)) {
1380 *deleted
= CFArrayGetValueAtIndex(array
, 0);
1381 *inserted
= CFArrayGetValueAtIndex(array
, 1);
1384 *deleted
= CFArrayGetValueAtIndex(array
, 0);
1388 SecError(errSecParam
, error
, NULL
, CFSTR("invalid entry in changes array: %@"), array
);