]> git.saurik.com Git - apple/security.git/blob - OSX/utilities/src/SecDb.c
Security-58286.251.4.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 = NULL;
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 sqlite3_finalize(stmt);
816
817 if (vacuumMode != SECDB_SQLITE_AUTO_VACUUM_INCREMENTAL) {
818 (void)sqlite3_exec(handle, "PRAGMA auto_vacuum = incremental", NULL, NULL, NULL);
819 (void)sqlite3_exec(handle, "VACUUM", NULL, NULL, NULL);
820 }
821 }
822 if (useWAL) {
823 (void)sqlite3_exec(handle, "PRAGMA journal_mode = WAL", NULL, NULL, NULL);
824 }
825
826 // Let SQLite handle timeouts.
827 sqlite3_busy_timeout(handle, 5*1000);
828 }
829 return handle;
830 }
831
832 static bool SecDbOpenV2(SecDbConnectionRef dbconn, const char *path, int flags, CFErrorRef *error) {
833 return (dbconn->handle = _SecDbOpenV2(path, flags, dbconn->db->useWAL, dbconn->db->useRobotVacuum, error)) != NULL;
834 }
835
836 static bool SecDbTruncate(SecDbConnectionRef dbconn, CFErrorRef *error)
837 {
838 int flags = SQLITE_TRUNCATE_AUTOVACUUM_FULL;
839 if (dbconn->db->useWAL) {
840 flags |= SQLITE_TRUNCATE_JOURNALMODE_WAL;
841 }
842 __block bool ok = SecDbFileControl(dbconn, SQLITE_TRUNCATE_DATABASE, &flags, error);
843 if (!ok) {
844 sqlite3_close(dbconn->handle);
845 dbconn->handle = NULL;
846 CFStringPerformWithCString(dbconn->db->db_path, ^(const char *path) {
847 if (error)
848 CFReleaseNull(*error);
849 if (SecCheckErrno(unlink(path), error, CFSTR("unlink %s"), path)) {
850 ok = SecDbOpenHandle(dbconn, NULL, error);
851 }
852 });
853 if (!ok) {
854 secinfo("#SecDB", "#SecDB Failed to delete db handle: %{public}@", error ? *error : NULL);
855 abort();
856 }
857 }
858
859 return ok;
860 }
861
862 static bool SecDbHandleCorrupt(SecDbConnectionRef dbconn, int rc, CFErrorRef *error)
863 {
864 if (!dbconn->db->allowRepair) {
865 SecCFCreateErrorWithFormat(rc, kSecErrnoDomain, NULL, error, NULL,
866 CFSTR("SecDbHandleCorrupt handled error: [%d] %s"), rc, strerror(rc));
867 dbconn->isCorrupted = false;
868 return false;
869 }
870
871 // Backup current db.
872 __block bool didRename = false;
873 CFStringPerformWithCString(dbconn->db->db_path, ^(const char *db_path) {
874 sqlite3 *corrupt_db = NULL;
875 char buf[PATH_MAX+1];
876 snprintf(buf, sizeof(buf), "%s-corrupt", db_path);
877 if (dbconn->handle && (corrupt_db = _SecDbOpenV2(buf, SQLITE_OPEN_READWRITE, dbconn->db->useWAL, dbconn->db->useRobotVacuum, error))) {
878 int on = 1;
879 didRename = SecDbErrorWithDb(sqlite3_file_control(corrupt_db, NULL, SQLITE_FCNTL_PERSIST_WAL, &on), corrupt_db, error, CFSTR("persist wal"));
880 didRename &= SecDbErrorWithDb(sqlite3_file_control(corrupt_db, NULL, SQLITE_REPLACE_DATABASE, (void *)dbconn->handle), corrupt_db, error, CFSTR("replace database"));
881 sqlite3_close(corrupt_db);
882 }
883 if (!didRename) {
884 if (dbconn->handle)
885 secerror("Tried to rename corrupt database at path %@, but we failed: %@, trying explicit rename", dbconn->db->db_path, error ? *error : NULL);
886 if (error)
887 CFReleaseNull(*error);
888
889 // Explicitly close our connection, plus all other open connections to this db.
890 bool closed = true;
891 if (dbconn->handle) {
892 closed &= SecDbError(sqlite3_close(dbconn->handle), error, CFSTR("close"));
893 dbconn->handle = NULL;
894 }
895 SecDbRef db = dbconn->db;
896 CFIndex idx, count = (db->connections) ? CFArrayGetCount(db->connections) : 0;
897 for (idx = 0; idx < count; idx++) {
898 SecDbConnectionRef dbconn = (SecDbConnectionRef) CFArrayGetValueAtIndex(db->connections, idx);
899 if (dbconn && dbconn->handle) {
900 closed &= SecDbError(sqlite3_close(dbconn->handle), error, CFSTR("close"));
901 dbconn->handle = NULL;
902 }
903 }
904 CFArrayRemoveAllValues(db->connections);
905
906 // Attempt rename only if all connections closed successfully.
907 if (closed) {
908 if (SecCheckErrno(rename(db_path, buf), error, CFSTR("rename %s %s"), db_path, buf)) {
909 if (SecDbOpenHandle(dbconn, NULL, error)) {
910 didRename = true;
911 }
912 }
913 }
914 }
915 if (didRename) {
916 secerror("Database at path %@ is corrupt. Copied it to %s for further investigation.", dbconn->db->db_path, buf);
917 } else {
918 seccritical("Tried to copy corrupt database at path %@, but we failed: %@", dbconn->db->db_path, error ? *error : NULL);
919 }
920 });
921
922 bool ok = (didRename &&
923 (dbconn->handle || SecDbOpenHandle(dbconn, NULL, error)) &&
924 SecDbTruncate(dbconn, error));
925
926 // Mark the db as not corrupted, even if something failed.
927 // Always note we are no longer in the corruption handler
928 dbconn->isCorrupted = false;
929
930 // Invoke our callers opened callback, since we just created a new database
931 if (ok && dbconn->db->opened) {
932 dbconn->db->callOpenedHandlerForNextConnection = false;
933 ok = dbconn->db->opened(dbconn->db, dbconn, true, &dbconn->db->callOpenedHandlerForNextConnection, error);
934 }
935
936 if (dbconn->db->corruptionReset) {
937 dbconn->db->corruptionReset();
938 }
939
940 return ok;
941 }
942
943 void
944 SecDbSetCorruptionReset(SecDbRef db, void (^corruptionReset)(void))
945 {
946 if (db->corruptionReset) {
947 Block_release(db->corruptionReset);
948 db->corruptionReset = NULL;
949 }
950 if (corruptionReset) {
951 db->corruptionReset = Block_copy(corruptionReset);
952 }
953 }
954
955 static bool SecDbLoggingEnabled(CFStringRef type)
956 {
957 CFTypeRef profile = NULL;
958 bool enabled = false;
959
960 if (csr_check(CSR_ALLOW_APPLE_INTERNAL) != 0)
961 return false;
962
963 profile = (CFNumberRef)CFPreferencesCopyValue(CFSTR("SQLProfile"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesAnyHost);
964
965 if (profile == NULL)
966 return false;
967
968 if (CFGetTypeID(profile) == CFBooleanGetTypeID()) {
969 enabled = CFBooleanGetValue((CFBooleanRef)profile);
970 } else if (CFGetTypeID(profile) == CFNumberGetTypeID()) {
971 int32_t num = 0;
972 CFNumberGetValue(profile, kCFNumberSInt32Type, &num);
973 enabled = !!num;
974 }
975
976 CFReleaseSafe(profile);
977
978 return enabled;
979 }
980
981 static unsigned
982 SecDbProfileMask(void)
983 {
984 static dispatch_once_t onceToken;
985 static unsigned profile_mask = 0;
986
987 // sudo defaults write /Library/Preferences/com.apple.security SQLProfile -bool true
988 dispatch_once(&onceToken, ^{
989 if (SecDbLoggingEnabled(CFSTR("SQLProfile")))
990 profile_mask = SQLITE_TRACE_PROFILE;
991 #if DEBUG
992 profile_mask |= SQLITE_TRACE_STMT;
993 #else
994 if (SecDbLoggingEnabled(CFSTR("SQLTrace")))
995 profile_mask = SQLITE_TRACE_STMT;
996 #endif
997 if (SecDbLoggingEnabled(CFSTR("SQLRow")))
998 profile_mask = SQLITE_TRACE_ROW;
999 secinfo("#SecDB", "sqlDb: sql trace mask: 0x%08x", profile_mask);
1000 });
1001 return profile_mask;
1002 }
1003
1004 static int
1005 SecDbTraceV2(unsigned mask, void *ctx, void *p, void *x) {
1006 SecDbConnectionRef dbconn __unused = ctx;
1007 const char *trace = "unknown";
1008
1009 if (mask == SQLITE_TRACE_PROFILE)
1010 trace = sqlite3_sql(p);
1011 else if (mask == SQLITE_TRACE_STMT) {
1012 trace = sqlite3_sql(p);
1013 } else if (mask == SQLITE_TRACE_ROW) {
1014 trace = sqlite3_expanded_sql(p);
1015 }
1016
1017 secinfo("#SecDB", "#SecDB %{public}s", trace);
1018 return 0;
1019 }
1020
1021
1022 static bool SecDbOpenHandle(SecDbConnectionRef dbconn, bool *created, CFErrorRef *error)
1023 {
1024 __block bool ok = true;
1025
1026 CFStringPerformWithCString(dbconn->db->db_path, ^(const char *db_path) {
1027 int flags = (dbconn->db->readWrite) ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
1028 ok = created && SecDbOpenV2(dbconn, db_path, flags, NULL);
1029 if (!ok) {
1030 ok = true;
1031 if (created) {
1032 char *tmp = dirname((char *)db_path);
1033 if (tmp) {
1034 mode_t omode = dbconn->db->mode;
1035 if (omode & S_IRUSR) { omode |= S_IXUSR; } // owner can read
1036 if (omode & S_IRGRP) { omode |= S_IXGRP; } // group can read
1037 if (omode & S_IROTH) { omode |= S_IXOTH; } // other can read
1038 int errnum = mkpath_np(tmp, omode);
1039 if (errnum != 0 && errnum != EEXIST) {
1040 SecCFCreateErrorWithFormat(errnum, kSecErrnoDomain, NULL, error, NULL,
1041 CFSTR("mkpath_np %s: [%d] %s"), tmp, errnum, strerror(errnum));
1042 ok = false;
1043 }
1044 }
1045 }
1046 // if the enclosing directory is ok, try to create the database.
1047 // this forces us to open it read-write, so we'll need to be the owner here.
1048 ok = ok && SecDbOpenV2(dbconn, db_path, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, error);
1049 if (ok) {
1050 chmod(db_path, dbconn->db->mode); // default: 0600 (S_IRUSR | S_IWUSR)
1051 if (created)
1052 *created = true;
1053 }
1054 }
1055
1056 if (ok) {
1057 unsigned mask = SecDbProfileMask();
1058 if (mask) {
1059 (void)sqlite3_trace_v2(dbconn->handle,
1060 mask,
1061 SecDbTraceV2,
1062 dbconn);
1063 }
1064 }
1065 });
1066
1067 return ok;
1068 }
1069
1070 static SecDbConnectionRef
1071 SecDbConnectionCreate(SecDbRef db, bool readOnly, CFErrorRef *error)
1072 {
1073 SecDbConnectionRef dbconn = NULL;
1074
1075 dbconn = CFTypeAllocate(SecDbConnection, struct __OpaqueSecDbConnection, kCFAllocatorDefault);
1076 require(dbconn != NULL, done);
1077
1078 dbconn->db = db;
1079 dbconn->readOnly = readOnly;
1080 dbconn->inTransaction = false;
1081 dbconn->source = kSecDbInvalidTransaction;
1082 dbconn->isCorrupted = false;
1083 dbconn->maybeCorruptedCode = 0;
1084 dbconn->hasIOFailure = false;
1085 dbconn->corruptionError = NULL;
1086 dbconn->handle = NULL;
1087 dbconn->changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1088
1089 done:
1090 return dbconn;
1091 }
1092
1093 static bool SecDbConnectionIsReadOnly(SecDbConnectionRef dbconn) {
1094 return dbconn->readOnly;
1095 }
1096
1097 static void SecDbConectionSetReadOnly(SecDbConnectionRef dbconn, bool readOnly) {
1098 dbconn->readOnly = readOnly;
1099 }
1100
1101 /* Read only connections go to the end of the queue, writeable connections
1102 go to the start of the queue. */
1103 SecDbConnectionRef SecDbConnectionAcquire(SecDbRef db, bool readOnly, CFErrorRef *error) {
1104 SecDbConnectionRef dbconn = NULL;
1105 SecDbConnectionAcquireRefMigrationSafe(db, readOnly, &dbconn, error);
1106 return dbconn;
1107 }
1108
1109 bool SecDbConnectionAcquireRefMigrationSafe(SecDbRef db, bool readOnly, SecDbConnectionRef* dbconnRef, CFErrorRef *error)
1110 {
1111 CFRetain(db);
1112 secinfo("dbconn", "acquire %s connection", readOnly ? "ro" : "rw");
1113 dispatch_semaphore_wait(readOnly ? db->read_semaphore : db->write_semaphore, DISPATCH_TIME_FOREVER);
1114 __block SecDbConnectionRef dbconn = NULL;
1115 __block bool ok = true;
1116 __block bool ranOpenedHandler = false;
1117
1118 bool (^assignDbConn)(SecDbConnectionRef) = ^bool(SecDbConnectionRef connection) {
1119 dbconn = connection;
1120 if (dbconnRef) {
1121 *dbconnRef = connection;
1122 }
1123
1124 return dbconn != NULL;
1125 };
1126
1127 dispatch_sync(db->queue, ^{
1128 if (!db->didFirstOpen) {
1129 bool didCreate = false;
1130 ok = assignDbConn(SecDbConnectionCreate(db, false, error));
1131 CFErrorRef localError = NULL;
1132 if (ok && !SecDbOpenHandle(dbconn, &didCreate, &localError)) {
1133 secerror("Unable to create database: %@", localError);
1134 if (localError && CFEqual(CFErrorGetDomain(localError), kSecDbErrorDomain)) {
1135 int code = (int)CFErrorGetCode(localError);
1136 dbconn->isCorrupted = (SQLITE_CORRUPT == code) || (SQLITE_NOTADB == code);
1137 }
1138 // If the open failure isn't due to corruption, propagate the error.
1139 ok = dbconn->isCorrupted;
1140 if (!ok && error && *error == NULL) {
1141 *error = localError;
1142 localError = NULL;
1143 }
1144 }
1145 CFReleaseNull(localError);
1146
1147 if (ok) {
1148 db->didFirstOpen = ok = SecDbDidCreateFirstConnection(dbconn, didCreate, error);
1149 ranOpenedHandler = true;
1150 }
1151 if (!ok)
1152 CFReleaseNull(dbconn);
1153 } else {
1154 /* Try to get one from the cache */
1155 CFIndex count = CFArrayGetCount(db->connections);
1156 while (count && !dbconn) {
1157 CFIndex ix = readOnly ? count - 1 : 0;
1158 if (assignDbConn((SecDbConnectionRef)CFArrayGetValueAtIndex(db->connections, ix)))
1159 CFRetainSafe(dbconn);
1160 else
1161 secerror("got NULL dbconn at index: %" PRIdCFIndex " skipping", ix);
1162 CFArrayRemoveValueAtIndex(db->connections, ix);
1163 }
1164 }
1165 });
1166
1167 if (dbconn) {
1168 /* Make sure the connection we found has the right access */
1169 if (SecDbConnectionIsReadOnly(dbconn) != readOnly) {
1170 SecDbConectionSetReadOnly(dbconn, readOnly);
1171 }
1172 } else if (ok) {
1173 /* Nothing found in cache, create a new connection */
1174 bool created = false;
1175 if (assignDbConn(SecDbConnectionCreate(db, readOnly, error)) && !SecDbOpenHandle(dbconn, &created, error)) {
1176 CFReleaseNull(dbconn);
1177 }
1178 }
1179
1180
1181 if (dbconn && !ranOpenedHandler && dbconn->db->opened) {
1182 dispatch_sync(db->queue, ^{
1183 if (dbconn->db->callOpenedHandlerForNextConnection) {
1184 dbconn->db->callOpenedHandlerForNextConnection = false;
1185 if (!dbconn->db->opened(db, dbconn, false, &dbconn->db->callOpenedHandlerForNextConnection, error)) {
1186 if (!dbconn->isCorrupted || !SecDbHandleCorrupt(dbconn, 0, error)) {
1187 CFReleaseNull(dbconn);
1188 }
1189 }
1190 }
1191 });
1192 }
1193
1194 if (dbconnRef) {
1195 *dbconnRef = dbconn;
1196 }
1197
1198 if (!dbconn) {
1199 // If acquire fails we need to signal the semaphore again.
1200 dispatch_semaphore_signal(readOnly ? db->read_semaphore : db->write_semaphore);
1201 CFRelease(db);
1202 }
1203
1204 return dbconn ? true : false;
1205 }
1206
1207 void SecDbConnectionRelease(SecDbConnectionRef dbconn) {
1208 if (!dbconn) {
1209 secerror("called with NULL dbconn");
1210 return;
1211 }
1212 SecDbRef db = dbconn->db;
1213 secinfo("dbconn", "release %@", dbconn);
1214 dispatch_sync(db->queue, ^{
1215 bool readOnly = SecDbConnectionIsReadOnly(dbconn);
1216 if (dbconn->hasIOFailure) {
1217 // Something wrong on the file layer (e.g. revoked file descriptor for networked home)
1218 // so we don't trust our existing connections anymore.
1219 CFArrayRemoveAllValues(db->connections);
1220 } else {
1221 CFIndex count = CFArrayGetCount(db->connections);
1222 // Add back possible writable dbconn to the pool.
1223 CFArrayInsertValueAtIndex(db->connections, readOnly ? count : 0, dbconn);
1224 // Remove the last (probably read-only) dbconn from the pool.
1225 if (count >= db->maxIdleHandles) {
1226 CFArrayRemoveValueAtIndex(db->connections, count);
1227 }
1228 }
1229 // Signal after we have put the connection back in the pool of connections
1230 dispatch_semaphore_signal(readOnly ? db->read_semaphore : db->write_semaphore);
1231 CFRelease(dbconn);
1232 CFRelease(db);
1233 });
1234 }
1235
1236 void SecDbReleaseAllConnections(SecDbRef db) {
1237 // Force all connections to be removed (e.g. file descriptor no longer valid)
1238 if (!db) {
1239 secerror("called with NULL db");
1240 return;
1241 }
1242 dispatch_sync(db->queue, ^{
1243 CFArrayRemoveAllValues(db->connections);
1244 dispatch_semaphore_signal(db->write_semaphore);
1245 dispatch_semaphore_signal(db->read_semaphore);
1246 });
1247 }
1248
1249 bool SecDbPerformRead(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) {
1250 SecDbConnectionRef dbconn = SecDbConnectionAcquire(db, true, error);
1251 bool success = false;
1252 if (dbconn) {
1253 perform(dbconn);
1254 success = true;
1255 SecDbConnectionRelease(dbconn);
1256 }
1257 return success;
1258 }
1259
1260 bool SecDbPerformWrite(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) {
1261 if(!db) {
1262 SecError(errSecNotAvailable, error, CFSTR("failed to get a db handle"));
1263 return false;
1264 }
1265 SecDbConnectionRef dbconn = SecDbConnectionAcquire(db, false, error);
1266 bool success = false;
1267 if (dbconn) {
1268 perform(dbconn);
1269 success = true;
1270 SecDbConnectionRelease(dbconn);
1271 }
1272 return success;
1273 }
1274
1275 static CFStringRef
1276 SecDbConnectionCopyFormatDescription(CFTypeRef value, CFDictionaryRef formatOptions)
1277 {
1278 SecDbConnectionRef dbconn = (SecDbConnectionRef)value;
1279 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecDbConnection %s %s>"),
1280 dbconn->readOnly ? "ro" : "rw", dbconn->handle ? "open" : "closed");
1281 }
1282
1283 static void
1284 SecDbConnectionDestroy(CFTypeRef value)
1285 {
1286 SecDbConnectionRef dbconn = (SecDbConnectionRef)value;
1287 if (dbconn->handle) {
1288 int s3e = sqlite3_close(dbconn->handle);
1289 if (s3e != SQLITE_OK) {
1290 secerror("failed to close database connection (%d) for %@: %s", s3e, dbconn->db->db_path, sqlite3_errmsg(dbconn->handle));
1291 }
1292 }
1293 dbconn->db = NULL;
1294 CFReleaseNull(dbconn->changes);
1295 CFReleaseNull(dbconn->corruptionError);
1296
1297 }
1298
1299 void SecDbPerformOnCommitQueue(SecDbConnectionRef dbconn, bool barrier, dispatch_block_t perform) {
1300 if (barrier) {
1301 dispatch_barrier_sync(dbconn->db->commitQueue, ^{
1302 perform();
1303 });
1304 } else {
1305 dispatch_sync(dbconn->db->commitQueue, ^{
1306 perform();
1307 });
1308 }
1309 }
1310
1311 // MARK: -
1312 // MARK: Bind helpers
1313
1314 #if 0
1315 bool SecDbBindNull(sqlite3_stmt *stmt, int param, CFErrorRef *error) {
1316 bool ok = SecDbErrorWithStmt(sqlite3_bind_null(stmt, param),
1317 stmt, error, CFSTR("bind_null[%d]"), param);
1318 secinfo("bind", "bind_null[%d] error: %@", param, error ? *error : NULL);
1319 return ok;
1320 }
1321 #endif
1322
1323 bool SecDbBindBlob(sqlite3_stmt *stmt, int param, const void *zData, size_t n, void(*xDel)(void*), CFErrorRef *error) {
1324 if (n > INT_MAX) {
1325 return SecDbErrorWithStmt(SQLITE_TOOBIG, stmt, error,
1326 CFSTR("bind_blob[%d]: blob bigger than INT_MAX"), param);
1327 }
1328 bool ok = SecDbErrorWithStmt(sqlite3_bind_blob(stmt, param, zData, (int)n, xDel),
1329 stmt, error, CFSTR("bind_blob[%d]"), param);
1330 secinfo("bind", "bind_blob[%d]: %.*s: %@", param, (int)n, zData, error ? *error : NULL);
1331 return ok;
1332 }
1333
1334 bool SecDbBindText(sqlite3_stmt *stmt, int param, const char *zData, size_t n, void(*xDel)(void*), CFErrorRef *error) {
1335 if (n > INT_MAX) {
1336 return SecDbErrorWithStmt(SQLITE_TOOBIG, stmt, error,
1337 CFSTR("bind_text[%d]: text bigger than INT_MAX"), param);
1338 }
1339 bool ok = SecDbErrorWithStmt(sqlite3_bind_text(stmt, param, zData, (int)n, xDel), stmt, error,
1340 CFSTR("bind_text[%d]"), param);
1341 secinfo("bind", "bind_text[%d]: \"%s\" error: %@", param, zData, error ? *error : NULL);
1342 return ok;
1343 }
1344
1345 bool SecDbBindDouble(sqlite3_stmt *stmt, int param, double value, CFErrorRef *error) {
1346 bool ok = SecDbErrorWithStmt(sqlite3_bind_double(stmt, param, value), stmt, error,
1347 CFSTR("bind_double[%d]"), param);
1348 secinfo("bind", "bind_double[%d]: %f error: %@", param, value, error ? *error : NULL);
1349 return ok;
1350 }
1351
1352 bool SecDbBindInt(sqlite3_stmt *stmt, int param, int value, CFErrorRef *error) {
1353 bool ok = SecDbErrorWithStmt(sqlite3_bind_int(stmt, param, value), stmt, error,
1354 CFSTR("bind_int[%d]"), param);
1355 secinfo("bind", "bind_int[%d]: %d error: %@", param, value, error ? *error : NULL);
1356 return ok;
1357 }
1358
1359 bool SecDbBindInt64(sqlite3_stmt *stmt, int param, sqlite3_int64 value, CFErrorRef *error) {
1360 bool ok = SecDbErrorWithStmt(sqlite3_bind_int64(stmt, param, value), stmt, error,
1361 CFSTR("bind_int64[%d]"), param);
1362 secinfo("bind", "bind_int64[%d]: %lld error: %@", param, value, error ? *error : NULL);
1363 return ok;
1364 }
1365
1366
1367 /* AUDIT[securityd](done):
1368 value (ok) is a caller provided, non NULL CFTypeRef.
1369 */
1370 bool SecDbBindObject(sqlite3_stmt *stmt, int param, CFTypeRef value, CFErrorRef *error) {
1371 CFTypeID valueId;
1372 __block bool result = false;
1373
1374 /* TODO: Can we use SQLITE_STATIC below everwhere we currently use
1375 SQLITE_TRANSIENT since we finalize the statement before the value
1376 goes out of scope? */
1377 if (!value || (valueId = CFGetTypeID(value)) == CFNullGetTypeID()) {
1378 /* Skip bindings for NULL values. sqlite3 will interpret unbound
1379 params as NULL which is exactly what we want. */
1380 #if 1
1381 result = true;
1382 #else
1383 result = SecDbBindNull(stmt, param, error);
1384 #endif
1385 } else if (valueId == CFStringGetTypeID()) {
1386 CFStringPerformWithCStringAndLength(value, ^(const char *cstr, size_t clen) {
1387 result = SecDbBindText(stmt, param, cstr, clen, SQLITE_TRANSIENT, error);
1388 });
1389 } else if (valueId == CFDataGetTypeID()) {
1390 CFIndex len = CFDataGetLength(value);
1391 if (len) {
1392 result = SecDbBindBlob(stmt, param, CFDataGetBytePtr(value),
1393 len, SQLITE_TRANSIENT, error);
1394 } else {
1395 result = SecDbBindText(stmt, param, "", 0, SQLITE_TRANSIENT, error);
1396 }
1397 } else if (valueId == CFDateGetTypeID()) {
1398 CFAbsoluteTime abs_time = CFDateGetAbsoluteTime(value);
1399 result = SecDbBindDouble(stmt, param, abs_time, error);
1400 } else if (valueId == CFBooleanGetTypeID()) {
1401 int bval = CFBooleanGetValue(value);
1402 result = SecDbBindInt(stmt, param, bval, error);
1403 } else if (valueId == CFNumberGetTypeID()) {
1404 Boolean convertOk;
1405 if (CFNumberIsFloatType(value)) {
1406 double nval;
1407 convertOk = CFNumberGetValue(value, kCFNumberDoubleType, &nval);
1408 result = SecDbBindDouble(stmt, param, nval, error);
1409 } else {
1410 SInt32 nval;
1411 convertOk = CFNumberGetValue(value, kCFNumberSInt32Type, &nval);
1412 if (convertOk) {
1413 result = SecDbBindInt(stmt, param, nval, error);
1414 } else {
1415 sqlite_int64 nval64;
1416 convertOk = CFNumberGetValue(value, kCFNumberSInt64Type, &nval64);
1417 if (convertOk)
1418 result = SecDbBindInt64(stmt, param, nval64, error);
1419 }
1420 }
1421 if (!convertOk) {
1422 result = SecDbError(SQLITE_INTERNAL, error, CFSTR("bind CFNumberGetValue failed for %@"), value);
1423 }
1424 } else {
1425 if (error) {
1426 CFStringRef valueDesc = CFCopyTypeIDDescription(valueId);
1427 SecDbError(SQLITE_MISMATCH, error, CFSTR("bind unsupported type %@"), valueDesc);
1428 CFReleaseSafe(valueDesc);
1429 }
1430 }
1431
1432 return result;
1433 }
1434
1435 // MARK: -
1436 // MARK: SecDbStatementRef
1437
1438 bool SecDbReset(sqlite3_stmt *stmt, CFErrorRef *error) {
1439 return SecDbErrorWithStmt(sqlite3_reset(stmt), stmt, error, CFSTR("reset"));
1440 }
1441
1442 bool SecDbClearBindings(sqlite3_stmt *stmt, CFErrorRef *error) {
1443 return SecDbErrorWithStmt(sqlite3_clear_bindings(stmt), stmt, error, CFSTR("clear bindings"));
1444 }
1445
1446 bool SecDbFinalize(sqlite3_stmt *stmt, CFErrorRef *error) {
1447 sqlite3 *handle = sqlite3_db_handle(stmt);
1448 int s3e = sqlite3_finalize(stmt);
1449 return s3e == SQLITE_OK ? true : SecDbErrorWithDb(s3e, handle, error, CFSTR("finalize: %p"), stmt);
1450 }
1451
1452 sqlite3_stmt *SecDbPrepareV2(SecDbConnectionRef dbconn, const char *sql, size_t sqlLen, const char **sqlTail, CFErrorRef *error) {
1453 sqlite3 *db = SecDbHandle(dbconn);
1454 if (sqlLen > INT_MAX) {
1455 SecDbErrorWithDb(SQLITE_TOOBIG, db, error, CFSTR("prepare_v2: sql bigger than INT_MAX"));
1456 return NULL;
1457 }
1458 int ntries = 0;
1459 for (;;) {
1460 sqlite3_stmt *stmt = NULL;
1461 int s3e = sqlite3_prepare_v2(db, sql, (int)sqlLen, &stmt, sqlTail);
1462 if (s3e == SQLITE_OK)
1463 return stmt;
1464 else if (!SecDbWaitIfNeeded(dbconn, s3e, NULL, CFSTR("preparev2"), ntries, error))
1465 return NULL;
1466 ntries++;
1467 }
1468 }
1469
1470 static sqlite3_stmt *SecDbCopyStatementWithTailRange(SecDbConnectionRef dbconn, CFStringRef sql, CFRange *sqlTail, CFErrorRef *error) {
1471 __block sqlite3_stmt *stmt = NULL;
1472 if (sql) CFStringPerformWithCStringAndLength(sql, ^(const char *sqlStr, size_t sqlLen) {
1473 const char *tail = NULL;
1474 stmt = SecDbPrepareV2(dbconn, sqlStr, sqlLen, &tail, error);
1475 if (sqlTail && sqlStr < tail && tail < sqlStr + sqlLen) {
1476 sqlTail->location = tail - sqlStr;
1477 sqlTail->length = sqlLen - sqlTail->location;
1478 }
1479 });
1480
1481 return stmt;
1482 }
1483
1484 sqlite3_stmt *SecDbCopyStmt(SecDbConnectionRef dbconn, CFStringRef sql, CFStringRef *tail, CFErrorRef *error) {
1485 // TODO: Add caching and cache lookup of statements
1486 CFRange sqlTail = {};
1487 sqlite3_stmt *stmt = SecDbCopyStatementWithTailRange(dbconn, sql, &sqlTail, error);
1488 if (sqlTail.length > 0) {
1489 CFStringRef excess = CFStringCreateWithSubstring(CFGetAllocator(sql), sql, sqlTail);
1490 if (tail) {
1491 *tail = excess;
1492 } else {
1493 SecDbError(SQLITE_INTERNAL, error,
1494 CFSTR("prepare_v2: %@ unused sql: %@"),
1495 sql, excess);
1496 CFReleaseSafe(excess);
1497 SecDbFinalize(stmt, error);
1498 stmt = NULL;
1499 }
1500 }
1501 return stmt;
1502 }
1503
1504 /*
1505 TODO: Could do a hack here with a custom kCFAllocatorNULL allocator for a second CFRuntimeBase inside a SecDbStatement,
1506 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. */
1507 bool SecDbReleaseCachedStmt(SecDbConnectionRef dbconn, CFStringRef sql, sqlite3_stmt *stmt, CFErrorRef *error) {
1508 if (stmt) {
1509 return SecDbFinalize(stmt, error);
1510 }
1511 return true;
1512 }
1513
1514 bool SecDbPrepare(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error, void(^exec)(sqlite3_stmt *stmt)) {
1515 assert(sql != NULL);
1516 sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, NULL, error);
1517 if (!stmt)
1518 return false;
1519
1520 exec(stmt);
1521 return SecDbReleaseCachedStmt(dbconn, sql, stmt, error);
1522 }
1523
1524 bool SecDbWithSQL(SecDbConnectionRef dbconn, CFStringRef sql, CFErrorRef *error, bool(^perform)(sqlite3_stmt *stmt)) {
1525 bool ok = true;
1526 CFRetain(sql);
1527 while (sql) {
1528 CFStringRef tail = NULL;
1529 if (ok) {
1530 sqlite3_stmt *stmt = SecDbCopyStmt(dbconn, sql, &tail, error);
1531 ok = stmt != NULL;
1532 if (stmt) {
1533 if (perform) {
1534 ok = perform(stmt);
1535 } else {
1536 // TODO: Use a different error scope here.
1537 ok = SecError(-50 /* errSecParam */, error, CFSTR("SecDbWithSQL perform block missing"));
1538 }
1539 ok &= SecDbReleaseCachedStmt(dbconn, sql, stmt, error);
1540 }
1541 } else {
1542 // TODO We already have an error here we really just want the left over sql in it's userData
1543 ok = SecDbError(SQLITE_ERROR, error, CFSTR("Error with unexecuted sql remaining %@"), sql);
1544 }
1545 CFRelease(sql);
1546 sql = tail;
1547 }
1548 return ok;
1549 }
1550
1551 #if 1
1552 /* SecDbForEach returns true if all SQLITE_ROW returns of sqlite3_step() return true from the row block.
1553 If the row block returns false and doesn't set an error (to indicate it has reached a limit),
1554 this entire function returns false. In that case no error will be set. */
1555 bool SecDbForEach(SecDbConnectionRef dbconn, sqlite3_stmt *stmt, CFErrorRef *error, bool(^row)(int row_index)) {
1556 bool result = false;
1557 for (int row_ix = 0;;++row_ix) {
1558 int s3e = sqlite3_step(stmt);
1559 if (s3e == SQLITE_ROW) {
1560 if (row) {
1561 if (!row(row_ix)) {
1562 break;
1563 }
1564 } else {
1565 // If we have no row block then getting SQLITE_ROW is an error
1566 SecDbError(s3e, error,
1567 CFSTR("step[%d]: %s returned SQLITE_ROW with NULL row block"),
1568 row_ix, sqlite3_sql(stmt));
1569 }
1570 } else {
1571 if (s3e == SQLITE_DONE) {
1572 result = true;
1573 } else {
1574 SecDbConnectionCheckCode(dbconn, s3e, error, CFSTR("SecDbForEach step[%d]"), row_ix);
1575 }
1576 break;
1577 }
1578 }
1579 return result;
1580 }
1581 #else
1582 bool SecDbForEach(sqlite3_stmt *stmt, CFErrorRef *error, bool(^row)(int row_index)) {
1583 int row_ix = 0;
1584 for (;;) {
1585 switch (_SecDbStep(dbconn, stmt, error)) {
1586 case kSecDbErrorStep:
1587 return false;
1588 case kSecDbRowStep:
1589 if (row) {
1590 if (row(row_ix++))
1591 break;
1592 } else {
1593 SecDbError(SQLITE_ERROR, error, CFSTR("SecDbStep SQLITE_ROW returned without a row handler"));
1594 }
1595 return false;
1596 case kSecDbDoneStep:
1597 return true;
1598 }
1599 }
1600 }
1601 #endif
1602
1603 void SecDbRecordChange(SecDbConnectionRef dbconn, CFTypeRef deleted, CFTypeRef inserted) {
1604 if (!dbconn->db->notifyPhase) return;
1605 CFTypeRef entry = SecDbEventCreateWithComponents(deleted, inserted);
1606 if (entry) {
1607 CFArrayAppendValue(dbconn->changes, entry);
1608 CFRelease(entry);
1609
1610 if (!dbconn->inTransaction) {
1611 secerror("db %@ changed outside txn", dbconn);
1612 // Only notify of DidCommit, since WillCommit code assumes
1613 // we are in a txn.
1614 SecDbOnNotify(dbconn, ^{
1615 SecDbNotifyPhase(dbconn, kSecDbTransactionDidCommit);
1616 });
1617 }
1618 }
1619 }
1620
1621
1622 CFGiblisFor(SecDbConnection)
1623
1624 //
1625 // SecDbEvent Creation and consumption
1626 //
1627
1628 static SecDbEventRef SecDbEventCreateInsert(CFTypeRef inserted) {
1629 return CFRetainSafe(inserted);
1630 }
1631
1632 static SecDbEventRef SecDbEventCreateDelete(CFTypeRef deleted) {
1633 return CFArrayCreate(kCFAllocatorDefault, &deleted, 1, &kCFTypeArrayCallBacks);
1634 }
1635
1636 static SecDbEventRef SecDbEventCreateUpdate(CFTypeRef deleted, CFTypeRef inserted) {
1637 const void *values[2] = { deleted, inserted };
1638 return CFArrayCreate(kCFAllocatorDefault, values, 2, &kCFTypeArrayCallBacks);
1639 }
1640
1641 SecDbEventRef SecDbEventCreateWithComponents(CFTypeRef deleted, CFTypeRef inserted) {
1642 if (deleted && inserted)
1643 return SecDbEventCreateUpdate(deleted, inserted);
1644 else if (deleted)
1645 return SecDbEventCreateDelete(deleted);
1646 else if (inserted)
1647 return SecDbEventCreateInsert(inserted);
1648 else
1649 return NULL;
1650 }
1651
1652 void SecDbEventTranslateComponents(SecDbEventRef item, CFTypeRef* deleted, CFTypeRef* inserted) {
1653 if(CFGetTypeID(item) == CFArrayGetTypeID()) {
1654 // One item: deletion. Two: update.
1655 CFIndex arraySize = CFArrayGetCount(item);
1656 if(arraySize == 1) {
1657 if(deleted) { *deleted = CFArrayGetValueAtIndex(item, 0); }
1658 if(inserted) { *inserted = NULL; }
1659 } else if(arraySize == 2) {
1660 if(deleted) { *deleted = CFArrayGetValueAtIndex(item, 0); }
1661 if(inserted) { *inserted = CFArrayGetValueAtIndex(item, 1); }
1662 } else {
1663 if(deleted) { *deleted = NULL; }
1664 if(inserted) { *inserted = NULL; }
1665 }
1666 } else {
1667 if(deleted) { *deleted = NULL; }
1668 if(inserted) { *inserted = item; }
1669 }
1670
1671 }
1672
1673 bool SecDbEventGetComponents(SecDbEventRef event, CFTypeRef *deleted, CFTypeRef *inserted, CFErrorRef *error) {
1674 if (isArray(event)) {
1675 CFArrayRef array = event;
1676 switch (CFArrayGetCount(array)) {
1677 case 2:
1678 *deleted = CFArrayGetValueAtIndex(array, 0);
1679 *inserted = CFArrayGetValueAtIndex(array, 1);
1680 break;
1681 case 1:
1682 *deleted = CFArrayGetValueAtIndex(array, 0);
1683 *inserted = NULL;
1684 break;
1685 default:
1686 SecError(errSecParam, error, NULL, CFSTR("invalid entry in changes array: %@"), array);
1687 break;
1688 }
1689 } else {
1690 *deleted = NULL;
1691 *inserted = event;
1692 }
1693 return true;
1694 }