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