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