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