]> git.saurik.com Git - apple/security.git/blob - OSX/utilities/src/SecDb.c
Security-58286.1.32.tar.gz
[apple/security.git] / OSX / utilities / src / SecDb.c
1 /*
2 * Copyright (c) 2012-2017 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)(SecDbRef db, 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 CFStringRef errno_code = NULL;
128
129 if (sql_code == SQLITE_CANTOPEN) {
130 int errno_number = sqlite3_system_errno(db);
131 errno_code = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), errno_number);
132 } else {
133 errno_code = CFRetain(CFSTR(""));
134 }
135
136 int extended_code = sqlite3_extended_errcode(db);
137 if (sql_code == extended_code)
138 SecDbError(sql_code, error, CFSTR("%@: [%d]%@ %s"), message, sql_code, errno_code, sqlite3_errmsg(db));
139 else
140 SecDbError(sql_code, error, CFSTR("%@: [%d->%d]%@ %s"), message, sql_code, extended_code, errno_code, sqlite3_errmsg(db));
141 CFReleaseSafe(message);
142 CFReleaseSafe(errno_code);
143 }
144 return false;
145 }
146
147 bool SecDbErrorWithStmt(int sql_code, sqlite3_stmt *stmt, CFErrorRef *error, CFStringRef format, ...) {
148 if (sql_code == SQLITE_OK) return true;
149 if (error) {
150 va_list args;
151 va_start(args, format);
152 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
153 va_end(args);
154
155 sqlite3 *db = sqlite3_db_handle(stmt);
156 const char *sql = sqlite3_sql(stmt);
157 int extended_code = sqlite3_extended_errcode(db);
158 if (sql_code == extended_code)
159 SecDbError(sql_code, error, CFSTR("%@: [%d] %s sql: %s"), message, sql_code, sqlite3_errmsg(db), sql);
160 else
161 SecDbError(sql_code, error, CFSTR("%@: [%d->%d] %s sql: %s"), message, sql_code, extended_code, sqlite3_errmsg(db), sql);
162 CFReleaseSafe(message);
163 }
164 return false;
165 }
166
167 // A callback for the sqlite3_log() interface.
168 static void sqlite3Log(void *pArg, int iErrCode, const char *zMsg){
169 secdebug("sqlite3", "(%d) %s", iErrCode, zMsg);
170 }
171
172 void _SecDbServerSetup(void)
173 {
174 static dispatch_once_t onceToken;
175 dispatch_once(&onceToken, ^{
176 int rx = sqlite3_config(SQLITE_CONFIG_LOG, sqlite3Log, NULL);
177 if (SQLITE_OK != rx) {
178 secwarning("Could not set up sqlite global error logging to syslog: %d", rx);
179 }
180 });
181 }
182
183
184 // MARK: -
185 // MARK: Static helper functions
186
187 static bool SecDbOpenHandle(SecDbConnectionRef dbconn, bool *created, CFErrorRef *error);
188 static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn, int rc, CFErrorRef *error);
189
190 #pragma mark -
191 #pragma mark SecDbRef
192
193 static CFStringRef
194 SecDbCopyFormatDescription(CFTypeRef value, CFDictionaryRef formatOptions)
195 {
196 SecDbRef db = (SecDbRef)value;
197 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecDb path:%@ connections: %@>"), db->db_path, db->connections);
198 }
199
200
201 static void
202 SecDbDestroy(CFTypeRef value)
203 {
204 SecDbRef db = (SecDbRef)value;
205 CFReleaseNull(db->connections);
206 CFReleaseNull(db->db_path);
207 if (db->queue) {
208 dispatch_release(db->queue);
209 db->queue = NULL;
210 }
211 if (db->commitQueue) {
212 dispatch_release(db->commitQueue);
213 db->commitQueue = NULL;
214 }
215 if (db->read_semaphore) {
216 dispatch_release(db->read_semaphore);
217 db->read_semaphore = NULL;
218 }
219 if (db->write_semaphore) {
220 dispatch_release(db->write_semaphore);
221 db->write_semaphore = NULL;
222 }
223 if (db->opened) {
224 Block_release(db->opened);
225 db->opened = NULL;
226 }
227 CFReleaseNull(db->notifyPhase);
228 }
229
230 CFGiblisFor(SecDb)
231
232 SecDbRef
233 SecDbCreateWithOptions(CFStringRef dbName, mode_t mode, bool readWrite, bool allowRepair, bool useWAL,
234 bool (^opened)(SecDbRef db, SecDbConnectionRef dbconn, bool didCreate, bool *callMeAgainForNextConnection, CFErrorRef *error))
235 {
236 SecDbRef db = NULL;
237
238 db = CFTypeAllocate(SecDb, struct __OpaqueSecDb, kCFAllocatorDefault);
239 require(db != NULL, done);
240
241 CFStringPerformWithCString(dbName, ^(const char *dbNameStr) {
242 db->queue = dispatch_queue_create(dbNameStr, DISPATCH_QUEUE_SERIAL);
243 });
244 CFStringRef commitQueueStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-commit"), dbName);
245 CFStringPerformWithCString(commitQueueStr, ^(const char *cqNameStr) {
246 db->commitQueue = dispatch_queue_create(cqNameStr, DISPATCH_QUEUE_CONCURRENT);
247 });
248 CFReleaseNull(commitQueueStr);
249 db->read_semaphore = dispatch_semaphore_create(kSecDbMaxReaders);
250 db->write_semaphore = dispatch_semaphore_create(kSecDbMaxWriters);
251 db->connections = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
252 db->opened = opened ? Block_copy(opened) : NULL;
253 if (getenv("__OSINSTALL_ENVIRONMENT") != NULL) {
254 // TODO: Move this code out of this layer
255 secinfo("#SecDB", "SecDB: running from installer");
256
257 db->db_path = CFSTR("file::memory:?cache=shared");
258 } else {
259 db->db_path = CFStringCreateCopy(kCFAllocatorDefault, dbName);
260 }
261 db->mode = mode;
262 db->readWrite = readWrite;
263 db->allowRepair = allowRepair;
264 db->useWAL = useWAL;
265
266 done:
267 return db;
268 }
269
270 SecDbRef
271 SecDbCreate(CFStringRef dbName,
272 bool (^opened)(SecDbRef db, SecDbConnectionRef dbconn, bool didCreate, bool *callMeAgainForNextConnection, CFErrorRef *error))
273 {
274 return SecDbCreateWithOptions(dbName, 0600, true, true, true, opened);
275 }
276
277 CFIndex
278 SecDbIdleConnectionCount(SecDbRef db) {
279 __block CFIndex count = 0;
280 dispatch_sync(db->queue, ^{
281 count = CFArrayGetCount(db->connections);
282 });
283 return count;
284 }
285
286 void SecDbAddNotifyPhaseBlock(SecDbRef db, SecDBNotifyBlock notifyPhase)
287 {
288 // SecDbNotifyPhase seems to mostly be called on the db's commitQueue, and not the db's queue. Therefore, protect the array with that queue.
289 dispatch_sync(db->commitQueue, ^{
290 SecDBNotifyBlock block = Block_copy(notifyPhase); /* Force the block off the stack */
291 if (db->notifyPhase == NULL) {
292 db->notifyPhase = CFArrayCreateMutableForCFTypes(NULL);
293 }
294 CFArrayAppendValue(db->notifyPhase, block);
295 Block_release(block);
296 });
297 }
298
299 static void SecDbNotifyPhase(SecDbConnectionRef dbconn, SecDbTransactionPhase phase) {
300 if (CFArrayGetCount(dbconn->changes)) {
301 CFArrayRef changes = dbconn->changes;
302 dbconn->changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
303 if (dbconn->db->notifyPhase) {
304 CFArrayForEach(dbconn->db->notifyPhase, ^(const void *value) {
305 SecDBNotifyBlock notifyBlock = (SecDBNotifyBlock)value;
306 notifyBlock(dbconn, phase, dbconn->source, changes);
307 });
308 }
309 CFReleaseSafe(changes);
310 }
311 }
312
313 static void SecDbOnNotify(SecDbConnectionRef dbconn, void (^perform)()) {
314 perform();
315 }
316
317 CFStringRef SecDbGetPath(SecDbRef db) {
318 if(!db) {
319 return NULL;
320 }
321 return db->db_path;
322 }
323
324
325 #pragma mark -
326 #pragma mark SecDbConnectionRef
327
328 static bool SecDbCheckCorrupted(SecDbConnectionRef dbconn)
329 {
330 __block bool checkDidRun = false;
331 __block bool isCorrupted = false;
332 __block CFErrorRef error = NULL;
333 SecDbPrepare(dbconn, CFSTR("PRAGMA integrity_check"), &error, ^(sqlite3_stmt *stmt) {
334 SecDbStep(dbconn, stmt, &error, ^(bool *stop) {
335 const char * result = (const char*)sqlite3_column_text(stmt, 0);
336 if (!result || strncasecmp(result, "ok", 3) != 0) {
337 isCorrupted = true;
338 secerror("SecDBCheckCorrupted integrity_check returned %s", (result) ? result : "NULL");
339 }
340 checkDidRun = true;
341 });
342 });
343 if (!checkDidRun) {
344 // An error occurred in SecDbPrepare before we could run the block.
345 if (error) {
346 CFIndex code = CFErrorGetCode(error);
347 if (SQLITE_CORRUPT == code || SQLITE_NOTADB == code) {
348 isCorrupted = true;
349 }
350 secinfo("#SecDB", "#SecDB warning error %{public}@ when running integrity check", error);
351 } else {
352 // We don't have an error ref if SecDbPrepare has called SecDbConnectionCheckCode,
353 // which then called SecDbHandleCorrupt. That code path is only entered when the
354 // original error was SQLITE_CORRUPT or SQLITE_NOTADB. On other errors, the
355 // CFErrorRef is not cleared and we can just check the code above.
356 isCorrupted = true;
357 secinfo("#SecDB", "#SecDB warning: failed to run integrity check due to corruption");
358 }
359 }
360 if (isCorrupted) {
361 if (checkDidRun) {
362 secerror("SecDBCheckCorrupted ran integrity_check, and that didn't return ok");
363 } else {
364 secerror("SecDBCheckCorrupted failed to run integrity check");
365 }
366 }
367 CFReleaseNull(error);
368
369 return isCorrupted;
370 }
371
372 static bool SecDbDidCreateFirstConnection(SecDbConnectionRef dbconn, bool didCreate, CFErrorRef *error)
373 {
374 secinfo("#SecDB", "#SecDB starting maintenance");
375 bool ok = true;
376
377 // Historical note: this used to check for integrity but that became too slow and caused panics at boot.
378 // Now, just react to SQLite errors when doing an operation. If file on disk is borked it'll tell us right away.
379
380 if (!dbconn->isCorrupted && dbconn->db->opened) {
381 CFErrorRef localError = NULL;
382
383 dbconn->db->callOpenedHandlerForNextConnection = false;
384 ok = dbconn->db->opened(dbconn->db, dbconn, didCreate, &dbconn->db->callOpenedHandlerForNextConnection, &localError);
385
386 if (!ok)
387 secerror("opened block failed: %@", localError);
388
389 if (!dbconn->isCorrupted && error && *error == NULL) {
390 *error = localError;
391 localError = NULL;
392 } else {
393 if (localError)
394 secerror("opened block failed: error (%@) is being released and lost", localError);
395 CFReleaseNull(localError);
396 }
397 }
398
399 if (dbconn->isCorrupted) {
400 /* SecDbHandleCorrupt must be executed on the db queue so nobody
401 can acquire a new connection while it is running. */
402 if (dispatch_get_current_queue() == dbconn->db->queue) {
403 ok = SecDbHandleCorrupt(dbconn, 0, error);
404 } else {
405 /* We aren't on the db queue already when we get here. It may be
406 unsafe to dispatch this work from the current queue, so log
407 and return false without attempting recovery. */
408 secerror("WARNING: unable to call SecDbHandleCorrupt (not on db queue)");
409 ok = false;
410 }
411 }
412
413 secinfo("#SecDB", "#SecDB starting maintenance");
414 return ok;
415 }
416
417 void SecDbCorrupt(SecDbConnectionRef dbconn, CFErrorRef error)
418 {
419 CFStringRef str = CFStringCreateWithFormat(NULL, NULL, CFSTR("SecDBCorrupt: %@"), error);
420 if (str) {
421 char buffer[1000] = "?";
422 uint32_t errorCode = 0;
423 CFStringGetCString(str, buffer, sizeof(buffer), kCFStringEncodingUTF8);
424 os_log_fault(secLogObjForScope("SecEmergency"), "%s", buffer);
425 if (error)
426 errorCode = (uint32_t)CFErrorGetCode(error);
427 __security_simulatecrash(str, __sec_exception_code_CorruptDb(errorCode));
428 CFRelease(str);
429 }
430 dbconn->isCorrupted = true;
431 CFRetainAssign(dbconn->corruptionError, error);
432 }
433
434
435 static uint8_t knownDbPathIndex(SecDbConnectionRef dbconn)
436 {
437
438 if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/keychain-2.db")))
439 return 1;
440 if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/ocspcache.sqlite3")))
441 return 2;
442 if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/TrustStore.sqlite3")))
443 return 3;
444 if(CFEqual(dbconn->db->db_path, CFSTR("/Library/Keychains/caissuercache.sqlite3")))
445 return 4;
446
447 /* Unknown DB path */
448 return 0;
449 }
450
451 static bool SecDbConnectionCheckCode(SecDbConnectionRef dbconn, int code, CFErrorRef *error, CFStringRef desc, ...)
452 CF_FORMAT_FUNCTION(4, 5);
453
454
455
456 // Return true if there was no error, returns false otherwise and set *error to an appropriate CFErrorRef.
457 static bool SecDbConnectionCheckCode(SecDbConnectionRef dbconn, int code, CFErrorRef *error, CFStringRef desc, ...) {
458 if (code == SQLITE_OK || code == SQLITE_DONE)
459 return true;
460
461 if (error) {
462 va_list args;
463 va_start(args, desc);
464 CFStringRef msg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, desc, args);
465 va_end(args);
466 SecDbErrorWithDb(code, dbconn->handle, error, CFSTR("%@"), msg);
467 CFRelease(msg);
468 }
469
470 dbconn->hasIOFailure |= (SQLITE_IOERR == code);
471
472 /* If it's already corrupted, don't try to recover */
473 if (dbconn->isCorrupted) {
474 CFStringRef reason = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
475 CFSTR("SQL DB %@ is corrupted already. Not trying to recover, corruption error was: %d (previously %d)"),
476 dbconn->db->db_path, code, dbconn->maybeCorruptedCode);
477 secerror("%@",reason);
478 __security_simulatecrash(reason, __sec_exception_code_TwiceCorruptDb(knownDbPathIndex(dbconn)));
479 CFReleaseSafe(reason);
480 return false;
481 }
482
483 // NOTADB means file is garbage, so it's functionally equivalent to corruption
484 dbconn->isCorrupted = (SQLITE_CORRUPT == code) || (SQLITE_NOTADB == code);
485 if (dbconn->isCorrupted) {
486 /* Run integrity check and only make dbconn->isCorrupted true and
487 run the corruption handler if the integrity check conclusively fails. */
488 dbconn->maybeCorruptedCode = code;
489 dbconn->isCorrupted = SecDbCheckCorrupted(dbconn);
490 if (dbconn->isCorrupted) {
491 secerror("operation returned code: %d integrity check=fail", code);
492 /* SecDbHandleCorrupt must be executed on the db queue so nobody
493 can acquire a new connection while it is running. */
494 if (dispatch_get_current_queue() == dbconn->db->queue) {
495 (void)SecDbHandleCorrupt(dbconn, code, error);
496 } else {
497 /* We aren't on the db queue already when we get here. It may be
498 unsafe to dispatch this work from the current queue, so log
499 and return false without attempting recovery. */
500 secerror("WARNING: unable to call SecDbHandleCorrupt (not on db queue)");
501 }
502 } else {
503 secerror("operation returned code: %d: integrity check=pass", code);
504 }
505 }
506
507 return false;
508 }
509
510 #if HAVE_UNLOCK_NOTIFY
511
512 static void SecDbUnlockNotify(void **apArg, int nArg) {
513 int i;
514 for(i=0; i<nArg; i++) {
515 dispatch_semaphore_t dsema = (dispatch_semaphore_t)apArg[i];
516 dispatch_semaphore_signal(dsema);
517 }
518 }
519
520 static bool SecDbWaitForUnlockNotify(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error) {
521 int rc;
522 dispatch_semaphore_t dsema = dispatch_semaphore_create(0);
523 rc = sqlite3_unlock_notify(dbconn->handle, SecDbUnlockNotify, dsema);
524 assert(rc == SQLITE_LOCKED || rc == SQLITE_OK);
525 if (rc == SQLITE_OK) {
526 dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER);
527 }
528 dispatch_release(dsema);
529 return (rc == SQLITE_OK
530 ? true
531 : (stmt
532 ? SecDbErrorWithStmt(rc, stmt, error, CFSTR("sqlite3_unlock_notify"))
533 : SecDbErrorWithDb(rc, dbconn->handle, error, CFSTR("sqlite3_unlock_notify"))));
534 }
535
536 #endif
537
538 #define BUSY_TIMEOUT_MS (5 * 60 * 1000) /* 5 minutes */
539
540 static bool SecDbBusyHandler(SecDbConnectionRef dbconn, CFErrorRef *error) {
541 return SecDbErrorWithDb(sqlite3_busy_timeout(dbconn->handle, BUSY_TIMEOUT_MS), dbconn->handle, error, CFSTR("busy_handler"));
542 }
543
544 static int sleepBackoff[] = { 10, 20, 50, 100, 250 };
545 static int sumBackoff[] = { 10, 30, 80, 180, 430 };
546 static int NumberOfSleepBackoff = sizeof(sleepBackoff)/sizeof(sleepBackoff[0]);
547
548 // Return true causes the operation to be tried again.
549 static bool SecDbWaitIfNeeded(SecDbConnectionRef dbconn, int s3e, sqlite3_stmt *stmt, CFStringRef desc, int nTries, CFErrorRef *error) {
550 #if HAVE_UNLOCK_NOTIFY
551 if (s3e == SQLITE_LOCKED) { // Optionally check for extended code being SQLITE_LOCKED_SHAREDCACHE
552 return SecDbWaitForUnlockNotify(dbconn, stmt, error))
553 }
554 #endif
555 if (((0xFF & s3e) == SQLITE_BUSY) || ((0xFF & s3e) == SQLITE_LOCKED)) {
556 int totaltimeout, timeout;
557
558 _Static_assert(sizeof(sumBackoff) == sizeof(sleepBackoff), "matching arrays not matching");
559 _Static_assert(sizeof(sumBackoff[0]) == sizeof(sleepBackoff[0]), "matching arrays not matching");
560
561 if (nTries < NumberOfSleepBackoff) {
562 timeout = sleepBackoff[nTries];
563 totaltimeout = sumBackoff[nTries];
564 } else {
565 timeout = sleepBackoff[NumberOfSleepBackoff - 1];
566 totaltimeout = sumBackoff[NumberOfSleepBackoff - 1] + (timeout * (nTries - NumberOfSleepBackoff));
567 }
568 if (totaltimeout < BUSY_TIMEOUT_MS) {
569 secinfo("#SecDB", "sqlite busy/locked: %d ntries: %d totaltimeout: %d", s3e, nTries, totaltimeout);
570 sqlite3_sleep(timeout);
571 return true;
572 } else {
573 secinfo("#SecDB", "sqlite busy/locked: too long: %d ms, giving up", totaltimeout);
574 }
575 }
576
577 return SecDbConnectionCheckCode(dbconn, s3e, error, CFSTR("%@"), desc);
578 }
579
580 enum SecDbStepResult {
581 kSecDbErrorStep = 0,
582 kSecDbRowStep = 1,
583 kSecDbDoneStep = 2,
584 };
585 typedef enum SecDbStepResult SecDbStepResult;
586
587 static SecDbStepResult _SecDbStep(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error) {
588 assert(stmt != NULL);
589 int s3e;
590 int ntries = 0;
591 for (;;) {
592 s3e = sqlite3_step(stmt);
593 if (s3e == SQLITE_ROW) {
594 return kSecDbRowStep;
595 } else if (s3e == SQLITE_DONE) {
596 /*
597 ** ^[SQLITE_DONE] means that the statement has finished executing
598 ** successfully. sqlite3_step() should not be called again on this virtual
599 ** machine without first calling [] to reset the virtual
600 ** machine back to its initial state.
601 */
602 sqlite3_reset(stmt);
603 return kSecDbDoneStep;
604 } else if (!SecDbWaitIfNeeded(dbconn, s3e, stmt, CFSTR("step"), ntries, error)) {
605 return kSecDbErrorStep;
606 }
607 ntries++;
608 };
609 }
610
611 bool
612 SecDbExec(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error)
613 {
614 bool ok = true;
615 CFRetain(sql);
616 while (sql) {
617 CFStringRef tail = NULL;
618 if (ok) {
619 sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, &tail, error);
620 ok = stmt != NULL;
621 if (stmt) {
622 SecDbStepResult sr;
623 while ((sr = _SecDbStep(dbconn, stmt, error)) == kSecDbRowStep);
624 if (sr == kSecDbErrorStep)
625 ok = false;
626 ok &= SecDbReleaseCachedStmt(dbconn, sql, stmt, error);
627 }
628 } else {
629 // TODO We already have an error here we really just want the left over sql in it's userData
630 ok = SecDbError(SQLITE_ERROR, error, CFSTR("Error with unexecuted sql remaining %@"), sql);
631 }
632 CFRelease(sql);
633 sql = tail;
634 }
635 return ok;
636 }
637
638 static bool SecDbBeginTransaction(SecDbConnectionRef dbconn, SecDbTransactionType type, CFErrorRef *error)
639 {
640 bool ok = true;
641 CFStringRef query;
642 switch (type) {
643 case kSecDbImmediateTransactionType:
644 secdebug("db", "SecDbBeginTransaction SecDbBeginTransaction %p", dbconn);
645 query = CFSTR("BEGIN IMMEDIATE");
646 break;
647 case kSecDbExclusiveRemoteSOSTransactionType:
648 secdebug("db", "SecDbBeginTransaction kSecDbExclusiveRemoteSOSTransactionType %p", dbconn);
649 dbconn->source = kSecDbSOSTransaction;
650 query = CFSTR("BEGIN EXCLUSIVE");
651 break;
652 case kSecDbExclusiveRemoteCKKSTransactionType:
653 secdebug("db", "SecDbBeginTransaction kSecDbExclusiveRemoteCKKSTransactionType %p", dbconn);
654 dbconn->source = kSecDbCKKSTransaction;
655 query = CFSTR("BEGIN EXCLUSIVE");
656 break;
657 case kSecDbExclusiveTransactionType:
658 if (type==kSecDbExclusiveTransactionType)
659 secdebug("db", "SecDbBeginTransaction kSecDbExclusiveTransactionType %p", dbconn);
660 query = CFSTR("BEGIN EXCLUSIVE");
661 break;
662 case kSecDbNormalTransactionType:
663 secdebug("db", "SecDbBeginTransaction kSecDbNormalTransactionType %p", dbconn);
664 query = CFSTR("BEGIN");
665 break;
666 default:
667 secdebug("db", "SecDbBeginTransaction invalid transaction type %lu", type);
668 ok = SecDbError(SQLITE_ERROR, error, CFSTR("invalid transaction type %d"), (int)type);
669 query = NULL;
670 break;
671 }
672
673 if (query != NULL && sqlite3_get_autocommit(dbconn->handle) != 0) {
674 ok = SecDbExec(dbconn, query, error);
675 }
676 if (ok)
677 dbconn->inTransaction = true;
678
679 return ok;
680 }
681
682 static bool SecDbEndTransaction(SecDbConnectionRef dbconn, bool commit, CFErrorRef *error)
683 {
684 __block bool ok = true;
685 __block bool commited = false;
686
687 dispatch_block_t notifyAndExec = ^{
688 if (commit) {
689 //secdebug("db", "SecDbEndTransaction kSecDbTransactionWillCommit %p", dbconn);
690 SecDbNotifyPhase(dbconn, kSecDbTransactionWillCommit);
691 commited = ok = SecDbExec(dbconn, CFSTR("END"), error);
692 //secdebug("db", "SecDbEndTransaction kSecDbTransactionWillCommit %p (after notify)", dbconn);
693 } else {
694 ok = SecDbExec(dbconn, CFSTR("ROLLBACK"), error);
695 commited = false;
696 }
697 dbconn->inTransaction = false;
698 SecDbNotifyPhase(dbconn, commited ? kSecDbTransactionDidCommit : kSecDbTransactionDidRollback);
699 secdebug("db", "SecDbEndTransaction %s %p", commited ? "kSecDbTransactionDidCommit" : "kSecDbTransactionDidRollback", dbconn);
700 dbconn->source = kSecDbAPITransaction;
701 };
702
703 SecDbPerformOnCommitQueue(dbconn, true, notifyAndExec);
704
705 return ok;
706 }
707
708 bool SecDbTransaction(SecDbConnectionRef dbconn, SecDbTransactionType type,
709 CFErrorRef *error, void (^transaction)(bool *commit))
710 {
711 bool ok = true;
712 bool commit = true;
713
714 if (dbconn->inTransaction) {
715 transaction(&commit);
716 if (!commit) {
717 secinfo("#SecDB", "#SecDB nested transaction asked to not be committed");
718 }
719 } else {
720 ok = SecDbBeginTransaction(dbconn, type, error);
721 if (ok) {
722 transaction(&commit);
723 ok = SecDbEndTransaction(dbconn, commit, error);
724 }
725 }
726
727 return ok && commit;
728 }
729
730 sqlite3 *SecDbHandle(SecDbConnectionRef dbconn) {
731 return dbconn->handle;
732 }
733
734 bool SecDbStep(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error, void (^row)(bool *stop)) {
735 for (;;) {
736 switch (_SecDbStep(dbconn, stmt, error)) {
737 case kSecDbErrorStep:
738 secdebug("db", "kSecDbErrorStep %@", error?*error:NULL);
739 return false;
740 case kSecDbRowStep:
741 secdebug("db", "kSecDbRowStep %@", error?*error:NULL);
742 if (row) {
743 bool stop = false;
744 row(&stop);
745 if (stop)
746 return true;
747 break;
748 }
749 SecDbError(SQLITE_ERROR, error, CFSTR("SecDbStep SQLITE_ROW returned without a row handler"));
750 return false;
751 case kSecDbDoneStep:
752 secdebug("db", "kSecDbDoneStep %@", error?*error:NULL);
753 return true;
754 }
755 }
756 }
757
758 bool SecDbCheckpoint(SecDbConnectionRef dbconn, CFErrorRef *error)
759 {
760 return SecDbConnectionCheckCode(dbconn, sqlite3_wal_checkpoint(dbconn->handle, NULL), error, CFSTR("wal_checkpoint"));
761 }
762
763 static bool SecDbFileControl(SecDbConnectionRef dbconn, int op, void *arg, CFErrorRef *error) {
764 return SecDbConnectionCheckCode(dbconn, sqlite3_file_control(dbconn->handle, NULL, op, arg), error, CFSTR("file_control"));
765 }
766
767 static sqlite3 *_SecDbOpenV2(const char *path, int flags, CFErrorRef *error) {
768 #if HAVE_UNLOCK_NOTIFY
769 flags |= SQLITE_OPEN_SHAREDCACHE;
770 #endif
771 sqlite3 *handle = NULL;
772 int s3e = sqlite3_open_v2(path, &handle, flags, NULL);
773 if (s3e) {
774 if (handle) {
775 SecDbErrorWithDb(s3e, handle, error, CFSTR("open_v2 \"%s\" 0x%X"), path, flags);
776 sqlite3_close(handle);
777 handle = NULL;
778 } else {
779 SecDbError(s3e, error, CFSTR("open_v2 \"%s\" 0x%X"), path, flags);
780 }
781 }
782 return handle;
783 }
784
785 static bool SecDbOpenV2(SecDbConnectionRef dbconn, const char *path, int flags, CFErrorRef *error) {
786 return (dbconn->handle = _SecDbOpenV2(path, flags, error)) != NULL;
787 }
788
789 static bool SecDbTruncate(SecDbConnectionRef dbconn, CFErrorRef *error)
790 {
791 int flags = SQLITE_TRUNCATE_AUTOVACUUM_FULL;
792 if (dbconn->db->useWAL) {
793 flags |= SQLITE_TRUNCATE_JOURNALMODE_WAL;
794 }
795 __block bool ok = SecDbFileControl(dbconn, SQLITE_TRUNCATE_DATABASE, &flags, error);
796 if (!ok) {
797 sqlite3_close(dbconn->handle);
798 dbconn->handle = NULL;
799 CFStringPerformWithCString(dbconn->db->db_path, ^(const char *path) {
800 if (error)
801 CFReleaseNull(*error);
802 if (SecCheckErrno(unlink(path), error, CFSTR("unlink %s"), path)) {
803 ok = SecDbOpenHandle(dbconn, NULL, error);
804 }
805 });
806 if (!ok) {
807 secinfo("#SecDB", "#SecDB Failed to delete db handle: %{public}@", error ? *error : NULL);
808 abort();
809 }
810 }
811
812 return ok;
813 }
814
815 static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn, int rc, CFErrorRef *error)
816 {
817 if (!dbconn->db->allowRepair) {
818 SecCFCreateErrorWithFormat(rc, kSecErrnoDomain, NULL, error, NULL,
819 CFSTR("SecDbHandleCorrupt handled error: [%d] %s"), rc, strerror(rc));
820 dbconn->isCorrupted = false;
821 return false;
822 }
823
824 // Backup current db.
825 __block bool didRename = false;
826 CFStringPerformWithCString(dbconn->db->db_path, ^(const char *db_path) {
827 sqlite3 *corrupt_db = NULL;
828 char buf[PATH_MAX+1];
829 snprintf(buf, sizeof(buf), "%s-corrupt", db_path);
830 if (dbconn->handle && (corrupt_db = _SecDbOpenV2(buf, SQLITE_OPEN_READWRITE, error))) {
831 int on = 1;
832 didRename = SecDbErrorWithDb(sqlite3_file_control(corrupt_db, NULL, SQLITE_FCNTL_PERSIST_WAL, &on), corrupt_db, error, CFSTR("persist wal"));
833 didRename &= SecDbErrorWithDb(sqlite3_file_control(corrupt_db, NULL, SQLITE_REPLACE_DATABASE, (void *)dbconn->handle), corrupt_db, error, CFSTR("replace database"));
834 sqlite3_close(corrupt_db);
835 }
836 if (!didRename) {
837 if (dbconn->handle)
838 secerror("Tried to rename corrupt database at path %@, but we failed: %@, trying explicit rename", dbconn->db->db_path, error ? *error : NULL);
839 if (error)
840 CFReleaseNull(*error);
841
842 // Explicitly close our connection, plus all other open connections to this db.
843 bool closed = true;
844 if (dbconn->handle) {
845 closed &= SecDbError(sqlite3_close(dbconn->handle), error, CFSTR("close"));
846 dbconn->handle = NULL;
847 }
848 SecDbRef db = dbconn->db;
849 CFIndex idx, count = (db->connections) ? CFArrayGetCount(db->connections) : 0;
850 for (idx = 0; idx < count; idx++) {
851 SecDbConnectionRef dbconn = (SecDbConnectionRef) CFArrayGetValueAtIndex(db->connections, idx);
852 if (dbconn && dbconn->handle) {
853 closed &= SecDbError(sqlite3_close(dbconn->handle), error, CFSTR("close"));
854 dbconn->handle = NULL;
855 }
856 }
857 CFArrayRemoveAllValues(db->connections);
858
859 // Attempt rename only if all connections closed successfully.
860 if (closed) {
861 if (SecCheckErrno(rename(db_path, buf), error, CFSTR("rename %s %s"), db_path, buf)) {
862 if (SecDbOpenHandle(dbconn, NULL, error)) {
863 didRename = true;
864 }
865 }
866 }
867 }
868 if (didRename) {
869 secerror("Database at path %@ is corrupt. Copied it to %s for further investigation.", dbconn->db->db_path, buf);
870 } else {
871 seccritical("Tried to copy corrupt database at path %@, but we failed: %@", dbconn->db->db_path, error ? *error : NULL);
872 }
873 });
874
875 bool ok = (didRename &&
876 (dbconn->handle || SecDbOpenHandle(dbconn, NULL, error)) &&
877 SecDbTruncate(dbconn, error));
878
879 // Mark the db as not corrupted, even if something failed.
880 // Always note we are no longer in the corruption handler
881 dbconn->isCorrupted = false;
882
883 // Invoke our callers opened callback, since we just created a new database
884 if (ok && dbconn->db->opened) {
885 dbconn->db->callOpenedHandlerForNextConnection = false;
886 ok = dbconn->db->opened(dbconn->db, dbconn, true, &dbconn->db->callOpenedHandlerForNextConnection, error);
887 }
888
889 return ok;
890 }
891
892 static bool SecDbLoggingEnabled(CFStringRef type)
893 {
894 CFTypeRef profile = NULL;
895 bool enabled = false;
896
897 if (csr_check(CSR_ALLOW_APPLE_INTERNAL) != 0)
898 return false;
899
900 profile = (CFNumberRef)CFPreferencesCopyValue(CFSTR("SQLProfile"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesAnyHost);
901
902 if (profile == NULL)
903 return false;
904
905 if (CFGetTypeID(profile) == CFBooleanGetTypeID()) {
906 enabled = CFBooleanGetValue((CFBooleanRef)profile);
907 } else if (CFGetTypeID(profile) == CFNumberGetTypeID()) {
908 int32_t num = 0;
909 CFNumberGetValue(profile, kCFNumberSInt32Type, &num);
910 enabled = !!num;
911 }
912
913 CFReleaseSafe(profile);
914
915 return enabled;
916 }
917
918 static unsigned
919 SecDbProfileMask(void)
920 {
921 static dispatch_once_t onceToken;
922 static unsigned profile_mask = 0;
923
924 // sudo defaults write /Library/Preferences/com.apple.security SQLProfile -bool true
925 dispatch_once(&onceToken, ^{
926 if (SecDbLoggingEnabled(CFSTR("SQLProfile")))
927 profile_mask = SQLITE_TRACE_PROFILE;
928 #if DEBUG
929 profile_mask |= SQLITE_TRACE_STMT;
930 #else
931 if (SecDbLoggingEnabled(CFSTR("SQLTrace")))
932 profile_mask = SQLITE_TRACE_STMT;
933 #endif
934 if (SecDbLoggingEnabled(CFSTR("SQLRow")))
935 profile_mask = SQLITE_TRACE_ROW;
936 secinfo("#SecDB", "sqlDb: sql trace mask: 0x%08x", profile_mask);
937 });
938 return profile_mask;
939 }
940
941 static int
942 SecDbTraceV2(unsigned mask, void *ctx, void *p, void *x) {
943 SecDbConnectionRef dbconn __unused = ctx;
944 const char *trace = "unknown";
945
946 if (mask == SQLITE_TRACE_PROFILE)
947 trace = sqlite3_sql(p);
948 else if (mask == SQLITE_TRACE_STMT) {
949 trace = sqlite3_sql(p);
950 } else if (mask == SQLITE_TRACE_ROW) {
951 trace = sqlite3_expanded_sql(p);
952 }
953
954 secinfo("#SecDB", "#SecDB %{public}s", trace);
955 return 0;
956 }
957
958
959 static bool SecDbOpenHandle(SecDbConnectionRef dbconn, bool *created, CFErrorRef *error)
960 {
961 __block bool ok = true;
962
963 CFStringPerformWithCString(dbconn->db->db_path, ^(const char *db_path) {
964 int flags = (dbconn->db->readWrite) ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
965 ok = created && SecDbOpenV2(dbconn, db_path, flags, NULL);
966 if (!ok) {
967 ok = true;
968 if (created) {
969 char *tmp = dirname((char *)db_path);
970 if (tmp) {
971 mode_t omode = dbconn->db->mode;
972 if (omode & S_IRUSR) { omode |= S_IXUSR; } // owner can read
973 if (omode & S_IRGRP) { omode |= S_IXGRP; } // group can read
974 if (omode & S_IROTH) { omode |= S_IXOTH; } // other can read
975 int errnum = mkpath_np(tmp, omode);
976 if (errnum != 0 && errnum != EEXIST) {
977 SecCFCreateErrorWithFormat(errnum, kSecErrnoDomain, NULL, error, NULL,
978 CFSTR("mkpath_np %s: [%d] %s"), tmp, errnum, strerror(errnum));
979 ok = false;
980 }
981 }
982 }
983 // if the enclosing directory is ok, try to create the database.
984 // this forces us to open it read-write, so we'll need to be the owner here.
985 ok = ok && SecDbOpenV2(dbconn, db_path, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, error);
986 if (ok) {
987 chmod(db_path, dbconn->db->mode); // default: 0600 (S_IRUSR | S_IWUSR)
988 if (created)
989 *created = true;
990 }
991 }
992
993 if (ok) {
994 unsigned mask = SecDbProfileMask();
995 if (mask) {
996 (void)sqlite3_trace_v2(dbconn->handle,
997 mask,
998 SecDbTraceV2,
999 dbconn);
1000 }
1001 }
1002
1003 ok = ok && SecDbBusyHandler(dbconn, error);
1004 });
1005
1006 return ok;
1007 }
1008
1009 static SecDbConnectionRef
1010 SecDbConnectionCreate(SecDbRef db, bool readOnly, CFErrorRef *error)
1011 {
1012 SecDbConnectionRef dbconn = NULL;
1013
1014 dbconn = CFTypeAllocate(SecDbConnection, struct __OpaqueSecDbConnection, kCFAllocatorDefault);
1015 require(dbconn != NULL, done);
1016
1017 dbconn->db = db;
1018 dbconn->readOnly = readOnly;
1019 dbconn->inTransaction = false;
1020 dbconn->source = kSecDbInvalidTransaction;
1021 dbconn->isCorrupted = false;
1022 dbconn->maybeCorruptedCode = 0;
1023 dbconn->hasIOFailure = false;
1024 dbconn->corruptionError = NULL;
1025 dbconn->handle = NULL;
1026 dbconn->changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1027
1028 done:
1029 return dbconn;
1030 }
1031
1032 static bool SecDbConnectionIsReadOnly(SecDbConnectionRef dbconn) {
1033 return dbconn->readOnly;
1034 }
1035
1036 static void SecDbConectionSetReadOnly(SecDbConnectionRef dbconn, bool readOnly) {
1037 dbconn->readOnly = readOnly;
1038 }
1039
1040 /* Read only connections go to the end of the queue, writeable connections
1041 go to the start of the queue. */
1042 SecDbConnectionRef SecDbConnectionAcquire(SecDbRef db, bool readOnly, CFErrorRef *error) {
1043 CFRetain(db);
1044 secinfo("dbconn", "acquire %s connection", readOnly ? "ro" : "rw");
1045 dispatch_semaphore_wait(readOnly ? db->read_semaphore : db->write_semaphore, DISPATCH_TIME_FOREVER);
1046 __block SecDbConnectionRef dbconn = NULL;
1047 __block bool ok = true;
1048 __block bool ranOpenedHandler = false;
1049 dispatch_sync(db->queue, ^{
1050 if (!db->didFirstOpen) {
1051 bool didCreate = false;
1052 ok = dbconn = SecDbConnectionCreate(db, false, error);
1053 CFErrorRef localError = NULL;
1054 if (ok && !SecDbOpenHandle(dbconn, &didCreate, &localError)) {
1055 secerror("Unable to create database: %@", localError);
1056 if (localError && CFEqual(CFErrorGetDomain(localError), kSecDbErrorDomain)) {
1057 int code = (int)CFErrorGetCode(localError);
1058 dbconn->isCorrupted = (SQLITE_CORRUPT == code) || (SQLITE_NOTADB == code);
1059 }
1060 // If the open failure isn't due to corruption, propagate the error.
1061 ok = dbconn->isCorrupted;
1062 if (!ok && error && *error == NULL) {
1063 *error = localError;
1064 localError = NULL;
1065 }
1066 }
1067 CFReleaseNull(localError);
1068
1069 if (ok) {
1070 db->didFirstOpen = ok = SecDbDidCreateFirstConnection(dbconn, didCreate, error);
1071 ranOpenedHandler = true;
1072 }
1073 if (!ok)
1074 CFReleaseNull(dbconn);
1075 } else {
1076 /* Try to get one from the cache */
1077 CFIndex count = CFArrayGetCount(db->connections);
1078 while (count && !dbconn) {
1079 CFIndex ix = readOnly ? count - 1 : 0;
1080 dbconn = (SecDbConnectionRef)CFArrayGetValueAtIndex(db->connections, ix);
1081 if (dbconn)
1082 CFRetain(dbconn);
1083 else
1084 secerror("got NULL dbconn at index: %" PRIdCFIndex " skipping", ix);
1085 CFArrayRemoveValueAtIndex(db->connections, ix);
1086 }
1087 }
1088 });
1089
1090 if (dbconn) {
1091 /* Make sure the connection we found has the right access */
1092 if (SecDbConnectionIsReadOnly(dbconn) != readOnly) {
1093 SecDbConectionSetReadOnly(dbconn, readOnly);
1094 }
1095 } else if (ok) {
1096 /* Nothing found in cache, create a new connection */
1097 bool created = false;
1098 dbconn = SecDbConnectionCreate(db, readOnly, error);
1099 if (dbconn && !SecDbOpenHandle(dbconn, &created, error)) {
1100 CFReleaseNull(dbconn);
1101 }
1102 }
1103
1104 if (dbconn && !ranOpenedHandler && dbconn->db->opened) {
1105 dispatch_sync(db->queue, ^{
1106 if (dbconn->db->callOpenedHandlerForNextConnection) {
1107 dbconn->db->callOpenedHandlerForNextConnection = false;
1108 if (!dbconn->db->opened(db, dbconn, false, &dbconn->db->callOpenedHandlerForNextConnection, error)) {
1109 if (!dbconn->isCorrupted || !SecDbHandleCorrupt(dbconn, 0, error)) {
1110 CFReleaseNull(dbconn);
1111 }
1112 }
1113 }
1114 });
1115 }
1116
1117 if (!dbconn) {
1118 // If acquire fails we need to signal the semaphore again.
1119 dispatch_semaphore_signal(readOnly ? db->read_semaphore : db->write_semaphore);
1120 CFRelease(db);
1121 }
1122
1123 return dbconn;
1124 }
1125
1126 void SecDbConnectionRelease(SecDbConnectionRef dbconn) {
1127 if (!dbconn) {
1128 secerror("called with NULL dbconn");
1129 return;
1130 }
1131 SecDbRef db = dbconn->db;
1132 secinfo("dbconn", "release %@", dbconn);
1133 dispatch_sync(db->queue, ^{
1134 bool readOnly = SecDbConnectionIsReadOnly(dbconn);
1135 if (dbconn->hasIOFailure) {
1136 // Something wrong on the file layer (e.g. revoked file descriptor for networked home)
1137 // so we don't trust our existing connections anymore.
1138 CFArrayRemoveAllValues(db->connections);
1139 } else {
1140 CFIndex count = CFArrayGetCount(db->connections);
1141 // Add back possible writable dbconn to the pool.
1142 CFArrayInsertValueAtIndex(db->connections, readOnly ? count : 0, dbconn);
1143 // Remove the last (probably read-only) dbconn from the pool.
1144 if (count >= kSecDbMaxIdleHandles) {
1145 CFArrayRemoveValueAtIndex(db->connections, count);
1146 }
1147 }
1148 // Signal after we have put the connection back in the pool of connections
1149 dispatch_semaphore_signal(readOnly ? db->read_semaphore : db->write_semaphore);
1150 CFRelease(dbconn);
1151 CFRelease(db);
1152 });
1153 }
1154
1155 void SecDbReleaseAllConnections(SecDbRef db) {
1156 // Force all connections to be removed (e.g. file descriptor no longer valid)
1157 if (!db) {
1158 secerror("called with NULL db");
1159 return;
1160 }
1161 dispatch_sync(db->queue, ^{
1162 CFArrayRemoveAllValues(db->connections);
1163 dispatch_semaphore_signal(db->write_semaphore);
1164 dispatch_semaphore_signal(db->read_semaphore);
1165 });
1166 }
1167
1168 bool SecDbReplace(SecDbRef db, CFStringRef newDbPath, CFErrorRef *error) {
1169 // Replace the given database with the contents of the database
1170 // at newDbPath, as an atomic operation on db->queue.
1171
1172 __block bool result = false;
1173 if (!db || !newDbPath) {
1174 secerror("called with NULL argument");
1175 return result;
1176 }
1177 dispatch_sync(db->queue, ^{
1178 // Explicitly close all database connections
1179 CFIndex idx, count = (db->connections) ? CFArrayGetCount(db->connections) : 0;
1180 for (idx = 0; idx < count; idx++) {
1181 SecDbConnectionRef dbconn = (SecDbConnectionRef) CFArrayGetValueAtIndex(db->connections, idx);
1182 if (dbconn->handle) {
1183 sqlite3_close(dbconn->handle);
1184 dbconn->handle = NULL;
1185 }
1186 }
1187 CFArrayRemoveAllValues(db->connections);
1188
1189 __block sqlite3 *old_db = NULL;
1190 __block sqlite3 *new_db = NULL;
1191 CFStringPerformWithCString(db->db_path, ^(const char *db_path) {
1192 int s3e = sqlite3_open_v2(db_path, &old_db, SQLITE_OPEN_READWRITE, NULL);
1193 if (SQLITE_OK != s3e) {
1194 secerror("Unable to open db for writing: %s (error %d)", db_path, s3e);
1195 }
1196 });
1197 CFStringPerformWithCString(newDbPath, ^(const char *new_db_path) {
1198 int s3e = sqlite3_open_v2(new_db_path, &new_db, SQLITE_OPEN_READONLY, NULL);
1199 if (SQLITE_OK != s3e) {
1200 secerror("Unable to open db for reading: %s (error %d)", new_db_path, s3e);
1201 }
1202 });
1203 if (old_db && new_db) {
1204 // Replace old_db with contents of new_db
1205 int s3e = sqlite3_file_control(old_db, NULL, SQLITE_FCNTL_REPLACE_DATABASE, new_db);
1206 if (SQLITE_OK != s3e) {
1207 secerror("Unable to replace db: %@ (error %d)", db->db_path, s3e);
1208 } else {
1209 result = true;
1210 }
1211 }
1212 if (old_db) {
1213 sqlite3_close(old_db);
1214 }
1215 if (new_db) {
1216 sqlite3_close(new_db);
1217 }
1218
1219 // Signal that connections are available again.
1220 dispatch_semaphore_signal(db->write_semaphore);
1221 dispatch_semaphore_signal(db->read_semaphore);
1222 });
1223
1224 return result;
1225 }
1226
1227 bool SecDbPerformRead(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) {
1228 SecDbConnectionRef dbconn = SecDbConnectionAcquire(db, true, error);
1229 bool success = false;
1230 if (dbconn) {
1231 perform(dbconn);
1232 success = true;
1233 SecDbConnectionRelease(dbconn);
1234 }
1235 return success;
1236 }
1237
1238 bool SecDbPerformWrite(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) {
1239 if(!db) {
1240 SecError(errSecNotAvailable, error, CFSTR("failed to get a db handle"));
1241 return false;
1242 }
1243 SecDbConnectionRef dbconn = SecDbConnectionAcquire(db, false, error);
1244 bool success = false;
1245 if (dbconn) {
1246 perform(dbconn);
1247 success = true;
1248 SecDbConnectionRelease(dbconn);
1249 }
1250 return success;
1251 }
1252
1253 static CFStringRef
1254 SecDbConnectionCopyFormatDescription(CFTypeRef value, CFDictionaryRef formatOptions)
1255 {
1256 SecDbConnectionRef dbconn = (SecDbConnectionRef)value;
1257 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecDbConnection %s %s>"),
1258 dbconn->readOnly ? "ro" : "rw", dbconn->handle ? "open" : "closed");
1259 }
1260
1261 static void
1262 SecDbConnectionDestroy(CFTypeRef value)
1263 {
1264 SecDbConnectionRef dbconn = (SecDbConnectionRef)value;
1265 if (dbconn->handle) {
1266 sqlite3_close(dbconn->handle);
1267 }
1268 dbconn->db = NULL;
1269 CFReleaseNull(dbconn->changes);
1270 CFReleaseNull(dbconn->corruptionError);
1271
1272 }
1273
1274 void SecDbPerformOnCommitQueue(SecDbConnectionRef dbconn, bool barrier, dispatch_block_t perform) {
1275 if (barrier) {
1276 dispatch_barrier_sync(dbconn->db->commitQueue, ^{
1277 perform();
1278 });
1279 } else {
1280 dispatch_sync(dbconn->db->commitQueue, ^{
1281 perform();
1282 });
1283 }
1284 }
1285
1286 // MARK: -
1287 // MARK: Bind helpers
1288
1289 #if 0
1290 bool SecDbBindNull(sqlite3_stmt *stmt, int param, CFErrorRef *error) {
1291 bool ok = SecDbErrorWithStmt(sqlite3_bind_null(stmt, param),
1292 stmt, error, CFSTR("bind_null[%d]"), param);
1293 secinfo("bind", "bind_null[%d] error: %@", param, error ? *error : NULL);
1294 return ok;
1295 }
1296 #endif
1297
1298 bool SecDbBindBlob(sqlite3_stmt *stmt, int param, const void *zData, size_t n, void(*xDel)(void*), CFErrorRef *error) {
1299 if (n > INT_MAX) {
1300 return SecDbErrorWithStmt(SQLITE_TOOBIG, stmt, error,
1301 CFSTR("bind_blob[%d]: blob bigger than INT_MAX"), param);
1302 }
1303 bool ok = SecDbErrorWithStmt(sqlite3_bind_blob(stmt, param, zData, (int)n, xDel),
1304 stmt, error, CFSTR("bind_blob[%d]"), param);
1305 secinfo("bind", "bind_blob[%d]: %.*s: %@", param, (int)n, zData, error ? *error : NULL);
1306 return ok;
1307 }
1308
1309 bool SecDbBindText(sqlite3_stmt *stmt, int param, const char *zData, size_t n, void(*xDel)(void*), CFErrorRef *error) {
1310 if (n > INT_MAX) {
1311 return SecDbErrorWithStmt(SQLITE_TOOBIG, stmt, error,
1312 CFSTR("bind_text[%d]: text bigger than INT_MAX"), param);
1313 }
1314 bool ok = SecDbErrorWithStmt(sqlite3_bind_text(stmt, param, zData, (int)n, xDel), stmt, error,
1315 CFSTR("bind_text[%d]"), param);
1316 secinfo("bind", "bind_text[%d]: \"%s\" error: %@", param, zData, error ? *error : NULL);
1317 return ok;
1318 }
1319
1320 bool SecDbBindDouble(sqlite3_stmt *stmt, int param, double value, CFErrorRef *error) {
1321 bool ok = SecDbErrorWithStmt(sqlite3_bind_double(stmt, param, value), stmt, error,
1322 CFSTR("bind_double[%d]"), param);
1323 secinfo("bind", "bind_double[%d]: %f error: %@", param, value, error ? *error : NULL);
1324 return ok;
1325 }
1326
1327 bool SecDbBindInt(sqlite3_stmt *stmt, int param, int value, CFErrorRef *error) {
1328 bool ok = SecDbErrorWithStmt(sqlite3_bind_int(stmt, param, value), stmt, error,
1329 CFSTR("bind_int[%d]"), param);
1330 secinfo("bind", "bind_int[%d]: %d error: %@", param, value, error ? *error : NULL);
1331 return ok;
1332 }
1333
1334 bool SecDbBindInt64(sqlite3_stmt *stmt, int param, sqlite3_int64 value, CFErrorRef *error) {
1335 bool ok = SecDbErrorWithStmt(sqlite3_bind_int64(stmt, param, value), stmt, error,
1336 CFSTR("bind_int64[%d]"), param);
1337 secinfo("bind", "bind_int64[%d]: %lld error: %@", param, value, error ? *error : NULL);
1338 return ok;
1339 }
1340
1341
1342 /* AUDIT[securityd](done):
1343 value (ok) is a caller provided, non NULL CFTypeRef.
1344 */
1345 bool SecDbBindObject(sqlite3_stmt *stmt, int param, CFTypeRef value, CFErrorRef *error) {
1346 CFTypeID valueId;
1347 __block bool result = false;
1348
1349 /* TODO: Can we use SQLITE_STATIC below everwhere we currently use
1350 SQLITE_TRANSIENT since we finalize the statement before the value
1351 goes out of scope? */
1352 if (!value || (valueId = CFGetTypeID(value)) == CFNullGetTypeID()) {
1353 /* Skip bindings for NULL values. sqlite3 will interpret unbound
1354 params as NULL which is exactly what we want. */
1355 #if 1
1356 result = true;
1357 #else
1358 result = SecDbBindNull(stmt, param, error);
1359 #endif
1360 } else if (valueId == CFStringGetTypeID()) {
1361 CFStringPerformWithCStringAndLength(value, ^(const char *cstr, size_t clen) {
1362 result = SecDbBindText(stmt, param, cstr, clen, SQLITE_TRANSIENT, error);
1363 });
1364 } else if (valueId == CFDataGetTypeID()) {
1365 CFIndex len = CFDataGetLength(value);
1366 if (len) {
1367 result = SecDbBindBlob(stmt, param, CFDataGetBytePtr(value),
1368 len, SQLITE_TRANSIENT, error);
1369 } else {
1370 result = SecDbBindText(stmt, param, "", 0, SQLITE_TRANSIENT, error);
1371 }
1372 } else if (valueId == CFDateGetTypeID()) {
1373 CFAbsoluteTime abs_time = CFDateGetAbsoluteTime(value);
1374 result = SecDbBindDouble(stmt, param, abs_time, error);
1375 } else if (valueId == CFBooleanGetTypeID()) {
1376 int bval = CFBooleanGetValue(value);
1377 result = SecDbBindInt(stmt, param, bval, error);
1378 } else if (valueId == CFNumberGetTypeID()) {
1379 Boolean convertOk;
1380 if (CFNumberIsFloatType(value)) {
1381 double nval;
1382 convertOk = CFNumberGetValue(value, kCFNumberDoubleType, &nval);
1383 result = SecDbBindDouble(stmt, param, nval, error);
1384 } else {
1385 SInt32 nval;
1386 convertOk = CFNumberGetValue(value, kCFNumberSInt32Type, &nval);
1387 if (convertOk) {
1388 result = SecDbBindInt(stmt, param, nval, error);
1389 } else {
1390 sqlite_int64 nval64;
1391 convertOk = CFNumberGetValue(value, kCFNumberSInt64Type, &nval64);
1392 if (convertOk)
1393 result = SecDbBindInt64(stmt, param, nval64, error);
1394 }
1395 }
1396 if (!convertOk) {
1397 result = SecDbError(SQLITE_INTERNAL, error, CFSTR("bind CFNumberGetValue failed for %@"), value);
1398 }
1399 } else {
1400 if (error) {
1401 CFStringRef valueDesc = CFCopyTypeIDDescription(valueId);
1402 SecDbError(SQLITE_MISMATCH, error, CFSTR("bind unsupported type %@"), valueDesc);
1403 CFReleaseSafe(valueDesc);
1404 }
1405 }
1406
1407 return result;
1408 }
1409
1410 // MARK: -
1411 // MARK: SecDbStatementRef
1412
1413 bool SecDbReset(sqlite3_stmt *stmt, CFErrorRef *error) {
1414 return SecDbErrorWithStmt(sqlite3_reset(stmt), stmt, error, CFSTR("reset"));
1415 }
1416
1417 bool SecDbClearBindings(sqlite3_stmt *stmt, CFErrorRef *error) {
1418 return SecDbErrorWithStmt(sqlite3_clear_bindings(stmt), stmt, error, CFSTR("clear bindings"));
1419 }
1420
1421 bool SecDbFinalize(sqlite3_stmt *stmt, CFErrorRef *error) {
1422 sqlite3 *handle = sqlite3_db_handle(stmt);
1423 int s3e = sqlite3_finalize(stmt);
1424 return s3e == SQLITE_OK ? true : SecDbErrorWithDb(s3e, handle, error, CFSTR("finalize: %p"), stmt);
1425 }
1426
1427 sqlite3_stmt *SecDbPrepareV2(SecDbConnectionRef dbconn, const char *sql, size_t sqlLen, const char **sqlTail, CFErrorRef *error) {
1428 sqlite3 *db = SecDbHandle(dbconn);
1429 if (sqlLen > INT_MAX) {
1430 SecDbErrorWithDb(SQLITE_TOOBIG, db, error, CFSTR("prepare_v2: sql bigger than INT_MAX"));
1431 return NULL;
1432 }
1433 int ntries = 0;
1434 for (;;) {
1435 sqlite3_stmt *stmt = NULL;
1436 int s3e = sqlite3_prepare_v2(db, sql, (int)sqlLen, &stmt, sqlTail);
1437 if (s3e == SQLITE_OK)
1438 return stmt;
1439 else if (!SecDbWaitIfNeeded(dbconn, s3e, NULL, CFSTR("preparev2"), ntries, error))
1440 return NULL;
1441 ntries++;
1442 }
1443 }
1444
1445 static sqlite3_stmt *SecDbCopyStatementWithTailRange(SecDbConnectionRef dbconn, CFStringRef sql, CFRange *sqlTail, CFErrorRef *error) {
1446 __block sqlite3_stmt *stmt = NULL;
1447 if (sql) CFStringPerformWithCStringAndLength(sql, ^(const char *sqlStr, size_t sqlLen) {
1448 const char *tail = NULL;
1449 stmt = SecDbPrepareV2(dbconn, sqlStr, sqlLen, &tail, error);
1450 if (sqlTail && sqlStr < tail && tail < sqlStr + sqlLen) {
1451 sqlTail->location = tail - sqlStr;
1452 sqlTail->length = sqlLen - sqlTail->location;
1453 }
1454 });
1455
1456 return stmt;
1457 }
1458
1459 sqlite3_stmt *SecDbCopyStmt(SecDbConnectionRef dbconn, CFStringRef sql, CFStringRef *tail, CFErrorRef *error) {
1460 // TODO: Add caching and cache lookup of statements
1461 CFRange sqlTail = {};
1462 sqlite3_stmt *stmt = SecDbCopyStatementWithTailRange(dbconn, sql, &sqlTail, error);
1463 if (sqlTail.length > 0) {
1464 CFStringRef excess = CFStringCreateWithSubstring(CFGetAllocator(sql), sql, sqlTail);
1465 if (tail) {
1466 *tail = excess;
1467 } else {
1468 SecDbError(SQLITE_INTERNAL, error,
1469 CFSTR("prepare_v2: %@ unused sql: %@"),
1470 sql, excess);
1471 CFReleaseSafe(excess);
1472 SecDbFinalize(stmt, error);
1473 stmt = NULL;
1474 }
1475 }
1476 return stmt;
1477 }
1478
1479 /*
1480 TODO: Could do a hack here with a custom kCFAllocatorNULL allocator for a second CFRuntimeBase inside a SecDbStatement,
1481 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. */
1482 bool SecDbReleaseCachedStmt(SecDbConnectionRef dbconn, CFStringRef sql, sqlite3_stmt *stmt, CFErrorRef *error) {
1483 if (stmt) {
1484 return SecDbFinalize(stmt, error);
1485 }
1486 return true;
1487 }
1488
1489 bool SecDbPrepare(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error, void(^exec)(sqlite3_stmt *stmt)) {
1490 assert(sql != NULL);
1491 sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, NULL, error);
1492 if (!stmt)
1493 return false;
1494
1495 exec(stmt);
1496 return SecDbReleaseCachedStmt(dbconn, sql, stmt, error);
1497 }
1498
1499 bool SecDbWithSQL(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error, bool(^perform)(sqlite3_stmt *stmt)) {
1500 bool ok = true;
1501 CFRetain(sql);
1502 while (sql) {
1503 CFStringRef tail = NULL;
1504 if (ok) {
1505 sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, &tail, error);
1506 ok = stmt != NULL;
1507 if (stmt) {
1508 if (perform) {
1509 ok = perform(stmt);
1510 } else {
1511 // TODO: Use a different error scope here.
1512 ok = SecError(-50 /* errSecParam */, error, CFSTR("SecDbWithSQL perform block missing"));
1513 }
1514 ok &= SecDbReleaseCachedStmt(dbconn, sql, stmt, error);
1515 }
1516 } else {
1517 // TODO We already have an error here we really just want the left over sql in it's userData
1518 ok = SecDbError(SQLITE_ERROR, error, CFSTR("Error with unexecuted sql remaining %@"), sql);
1519 }
1520 CFRelease(sql);
1521 sql = tail;
1522 }
1523 return ok;
1524 }
1525
1526 #if 1
1527 /* SecDbForEach returns true if all SQLITE_ROW returns of sqlite3_step() return true from the row block.
1528 If the row block returns false and doesn't set an error (to indicate it has reached a limit),
1529 this entire function returns false. In that case no error will be set. */
1530 bool SecDbForEach(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error, bool(^row)(int row_index)) {
1531 bool result = false;
1532 for (int row_ix = 0;;++row_ix) {
1533 int s3e = sqlite3_step(stmt);
1534 if (s3e == SQLITE_ROW) {
1535 if (row) {
1536 if (!row(row_ix)) {
1537 break;
1538 }
1539 } else {
1540 // If we have no row block then getting SQLITE_ROW is an error
1541 SecDbError(s3e, error,
1542 CFSTR("step[%d]: %s returned SQLITE_ROW with NULL row block"),
1543 row_ix, sqlite3_sql(stmt));
1544 }
1545 } else {
1546 if (s3e == SQLITE_DONE) {
1547 result = true;
1548 } else {
1549 dbconn->hasIOFailure |= (s3e == SQLITE_IOERR);
1550 SecDbErrorWithStmt(s3e, stmt, error, CFSTR("step[%d]"), row_ix);
1551 }
1552 break;
1553 }
1554 }
1555 return result;
1556 }
1557 #else
1558 bool SecDbForEach(sqlite3_stmt *stmt, CFErrorRef *error, bool(^row)(int row_index)) {
1559 int row_ix = 0;
1560 for (;;) {
1561 switch (_SecDbStep(dbconn, stmt, error)) {
1562 case kSecDbErrorStep:
1563 return false;
1564 case kSecDbRowStep:
1565 if (row) {
1566 if (row(row_ix++))
1567 break;
1568 } else {
1569 SecDbError(SQLITE_ERROR, error, CFSTR("SecDbStep SQLITE_ROW returned without a row handler"));
1570 }
1571 return false;
1572 case kSecDbDoneStep:
1573 return true;
1574 }
1575 }
1576 }
1577 #endif
1578
1579 void SecDbRecordChange(SecDbConnectionRef dbconn, CFTypeRef deleted, CFTypeRef inserted) {
1580 if (!dbconn->db->notifyPhase) return;
1581 CFTypeRef entry = SecDbEventCreateWithComponents(deleted, inserted);
1582 if (entry) {
1583 CFArrayAppendValue(dbconn->changes, entry);
1584 CFRelease(entry);
1585
1586 if (!dbconn->inTransaction) {
1587 secerror("db %@ changed outside txn", dbconn);
1588 // Only notify of DidCommit, since WillCommit code assumes
1589 // we are in a txn.
1590 SecDbOnNotify(dbconn, ^{
1591 SecDbNotifyPhase(dbconn, kSecDbTransactionDidCommit);
1592 });
1593 }
1594 }
1595 }
1596
1597
1598 CFGiblisFor(SecDbConnection)
1599
1600 //
1601 // SecDbEvent Creation and consumption
1602 //
1603
1604 static SecDbEventRef SecDbEventCreateInsert(CFTypeRef inserted) {
1605 return CFRetainSafe(inserted);
1606 }
1607
1608 static SecDbEventRef SecDbEventCreateDelete(CFTypeRef deleted) {
1609 return CFArrayCreate(kCFAllocatorDefault, &deleted, 1, &kCFTypeArrayCallBacks);
1610 }
1611
1612 static SecDbEventRef SecDbEventCreateUpdate(CFTypeRef deleted, CFTypeRef inserted) {
1613 const void *values[2] = { deleted, inserted };
1614 return CFArrayCreate(kCFAllocatorDefault, values, 2, &kCFTypeArrayCallBacks);
1615 }
1616
1617 SecDbEventRef SecDbEventCreateWithComponents(CFTypeRef deleted, CFTypeRef inserted) {
1618 if (deleted && inserted)
1619 return SecDbEventCreateUpdate(deleted, inserted);
1620 else if (deleted)
1621 return SecDbEventCreateDelete(deleted);
1622 else if (inserted)
1623 return SecDbEventCreateInsert(inserted);
1624 else
1625 return NULL;
1626 }
1627
1628 void SecDbEventTranslateComponents(SecDbEventRef item, CFTypeRef* deleted, CFTypeRef* inserted) {
1629 if(CFGetTypeID(item) == CFArrayGetTypeID()) {
1630 // One item: deletion. Two: update.
1631 CFIndex arraySize = CFArrayGetCount(item);
1632 if(arraySize == 1) {
1633 if(deleted) { *deleted = CFArrayGetValueAtIndex(item, 0); }
1634 if(inserted) { *inserted = NULL; }
1635 } else if(arraySize == 2) {
1636 if(deleted) { *deleted = CFArrayGetValueAtIndex(item, 0); }
1637 if(inserted) { *inserted = CFArrayGetValueAtIndex(item, 1); }
1638 } else {
1639 if(deleted) { *deleted = NULL; }
1640 if(inserted) { *inserted = NULL; }
1641 }
1642 } else {
1643 if(deleted) { *deleted = NULL; }
1644 if(inserted) { *inserted = item; }
1645 }
1646
1647 }
1648
1649 bool SecDbEventGetComponents(SecDbEventRef event, CFTypeRef *deleted, CFTypeRef *inserted, CFErrorRef *error) {
1650 if (isArray(event)) {
1651 CFArrayRef array = event;
1652 switch (CFArrayGetCount(array)) {
1653 case 2:
1654 *deleted = CFArrayGetValueAtIndex(array, 0);
1655 *inserted = CFArrayGetValueAtIndex(array, 1);
1656 break;
1657 case 1:
1658 *deleted = CFArrayGetValueAtIndex(array, 0);
1659 *inserted = NULL;
1660 break;
1661 default:
1662 SecError(errSecParam, error, NULL, CFSTR("invalid entry in changes array: %@"), array);
1663 break;
1664 }
1665 } else {
1666 *deleted = NULL;
1667 *inserted = event;
1668 }
1669 return true;
1670 }