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