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