]> git.saurik.com Git - apple/security.git/blob - utilities/src/SecDb.c
Security-55471.14.tar.gz
[apple/security.git] / utilities / src / SecDb.c
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
31 struct __OpaqueSecDbStatement {
32 CFRuntimeBase _base;
33
34 SecDbConnectionRef dbconn;
35 sqlite3_stmt *stmt;
36 };
37
38 struct __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
50 struct __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
64 CFStringRef kSecDbErrorDomain = CFSTR("com.apple.utilities.sqlite3");
65
66 bool 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
81 bool 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
99 bool 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
123 static bool SecDbOpenHandle(SecDbConnectionRef dbconn, bool *created, CFErrorRef *error);
124 static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn, int rc, CFErrorRef *error);
125
126 #pragma mark -
127 #pragma mark SecDbRef
128
129 static CFStringRef
130 SecDbCopyDescription(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
137 static void
138 SecDbDestroy(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
148 CFGiblisFor(SecDb)
149
150 SecDbRef
151 SecDbCreate(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 }
173 done:
174 return db;
175 }
176
177 CFIndex
178 SecDbIdleConnectionCount(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
190 static 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
209 static 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
247 void SecDbCorrupt(SecDbConnectionRef dbconn)
248 {
249 dbconn->isCorrupted = true;
250 }
251
252
253 static 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.
271 static 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
311 static 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
319 static 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.
340 static 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
362 static 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.
369 static 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
408 enum SecDbStepResult {
409 kSecDbErrorStep = 0,
410 kSecDbRowStep = 1,
411 kSecDbDoneStep = 2,
412 };
413 typedef enum SecDbStepResult SecDbStepResult;
414
415 static 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
430 bool
431 SecDbExec(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
457 static 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
484 static 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
493 bool 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
514 done:
515 return ok && commit;
516 }
517
518 sqlite3 *SecDbHandle(SecDbConnectionRef dbconn) {
519 return dbconn->handle;
520 }
521
522 bool 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
543 bool SecDbCheckpoint(SecDbConnectionRef dbconn, CFErrorRef *error)
544 {
545 return SecDbConnectionCheckCode(dbconn, sqlite3_wal_checkpoint(dbconn->handle, NULL), error, CFSTR("wal_checkpoint"));
546 }
547
548 static 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
552 static 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
570 static bool SecDbOpenV2(SecDbConnectionRef dbconn, const char *path, int flags, CFErrorRef *error) {
571 return (dbconn->handle = _SecDbOpenV2(path, flags, error)) != NULL;
572 }
573
574 static 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
597 static 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
647 static 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
679 static 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
683 static 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
694 static bool SecDbTraceEnabled(void)
695 {
696 #if DEBUG
697 return true;
698 #else
699 return false;
700 #endif
701 }
702
703 static 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
715 static 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
752 done:
753 return ok;
754 }
755
756 static SecDbConnectionRef
757 SecDbConnectionCreate(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
767 done:
768 return dbconn;
769 }
770
771 static bool SecDbConnectionIsReadOnly(SecDbConnectionRef dbconn) {
772 return dbconn->readOnly;
773 }
774
775 static 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. */
781 SecDbConnectionRef 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
849 void 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
872 bool 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
883 bool 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
894 static CFStringRef
895 SecDbConnectionCopyDescription(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
902 static void
903 SecDbConnectionDestroy(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
917 bool 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
925 bool 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
936 bool 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
947 bool 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
954 bool 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
961 bool 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 */
972 bool 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
1040 bool SecDbReset(sqlite3_stmt *stmt, CFErrorRef *error) {
1041 return SecDbErrorWithStmt(sqlite3_reset(stmt), stmt, error, CFSTR("reset"));
1042 }
1043
1044 bool SecDbClearBindings(sqlite3_stmt *stmt, CFErrorRef *error) {
1045 return SecDbErrorWithStmt(sqlite3_clear_bindings(stmt), stmt, error, CFSTR("clear bindings"));
1046 }
1047
1048 bool 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
1053 sqlite3_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
1070 static 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
1084 sqlite3_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. */
1107 bool 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
1114 bool 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
1124 bool 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. */
1155 bool 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
1182 bool 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
1203 CFGiblisFor(SecDbConnection)