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