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