]> git.saurik.com Git - apple/security.git/blob - OSX/authd/authdb.c
Security-57337.50.23.tar.gz
[apple/security.git] / OSX / authd / authdb.c
1 /* Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. */
2
3 #include "authdb.h"
4 #include "mechanism.h"
5 #include "rule.h"
6 #include "debugging.h"
7 #include "authitems.h"
8 #include "server.h"
9
10 #include <sqlite3.h>
11 #include <sqlite3_private.h>
12 #include <CoreFoundation/CoreFoundation.h>
13 #include "rule.h"
14 #include "authutilities.h"
15 #include <libgen.h>
16 #include <sys/stat.h>
17
18 #define AUTHDB "/var/db/auth.db"
19 #define AUTHDB_DATA "/System/Library/Security/authorization.plist"
20
21 #define AUTH_STR(x) #x
22 #define AUTH_STRINGIFY(x) AUTH_STR(x)
23
24 #define AUTHDB_VERSION 1
25 #define AUTHDB_VERSION_STRING AUTH_STRINGIFY(AUTHDB_VERSION)
26
27 #define AUTHDB_BUSY_DELAY 1
28 #define AUTHDB_MAX_HANDLES 3
29
30 struct _authdb_connection_s {
31 __AUTH_BASE_STRUCT_HEADER__;
32
33 authdb_t db;
34 sqlite3 * handle;
35 };
36
37 struct _authdb_s {
38 __AUTH_BASE_STRUCT_HEADER__;
39
40 char * db_path;
41 dispatch_queue_t queue;
42 CFMutableArrayRef connections;
43 };
44
45 static const char * const authdb_upgrade_sql[] = {
46 /* 0 */
47 /* current scheme */
48 "CREATE TABLE delegates_map ("
49 "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
50 "d_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
51 "ord INTEGER NOT NULL"
52 ");"
53 "CREATE INDEX d_map_d_id ON delegates_map(d_id);"
54 "CREATE INDEX d_map_r_id ON delegates_map(r_id);"
55 "CREATE INDEX d_map_r_id_ord ON delegates_map (r_id, ord);"
56 "CREATE TABLE mechanisms ("
57 "id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,"
58 "plugin TEXT NOT NULL,"
59 "param TEXT NOT NULL,"
60 "privileged INTEGER CHECK (privileged = 0 OR privileged = 1) NOT NULL DEFAULT (0)"
61 ");"
62 "CREATE UNIQUE INDEX mechanisms_lookup ON mechanisms (plugin,param,privileged);"
63 "CREATE TABLE mechanisms_map ("
64 "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
65 "m_id INTEGER NOT NULL REFERENCES mechanisms(id) ON DELETE CASCADE,"
66 "ord INTEGER NOT NULL"
67 ");"
68 "CREATE INDEX m_map_m_id ON mechanisms_map (m_id);"
69 "CREATE INDEX m_map_r_id ON mechanisms_map (r_id);"
70 "CREATE INDEX m_map_r_id_ord ON mechanisms_map (r_id, ord);"
71 "CREATE TABLE rules ("
72 "id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,"
73 "name TEXT NOT NULL UNIQUE,"
74 "type INTEGER CHECK (type = 1 OR type = 2) NOT NULL,"
75 "class INTEGER CHECK (class > 0),"
76 "'group' TEXT,"
77 "kofn INTEGER,"
78 "timeout INTEGER,"
79 "flags INTEGER,"
80 "tries INTEGER,"
81 "version INTEGER NOT NULL DEFAULT (0),"
82 "created REAL NOT NULL DEFAULT (0),"
83 "modified REAL NOT NULL DEFAULT (0),"
84 "hash BLOB,"
85 "identifier TEXT,"
86 "requirement BLOB,"
87 "comment TEXT"
88 ");"
89 "CREATE INDEX a_type ON rules (type);"
90 "CREATE TABLE config ("
91 "'key' TEXT PRIMARY KEY NOT NULL UNIQUE,"
92 "value"
93 ");"
94 "CREATE TABLE prompts ("
95 "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
96 "lang TEXT NOT NULL,"
97 "value TEXT NOT NULL"
98 ");"
99 "CREATE INDEX p_r_id ON prompts(r_id);"
100 "CREATE TABLE buttons ("
101 "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
102 "lang TEXT NOT NULL,"
103 "value TEXT NOT NULL"
104 ");"
105 "CREATE INDEX b_r_id ON buttons(r_id);"
106 "INSERT INTO config VALUES('version', "AUTHDB_VERSION_STRING");"
107 };
108
109 static int32_t
110 _sqlite3_exec(sqlite3 * handle, const char * query)
111 {
112 int32_t rc = SQLITE_ERROR;
113 require(query != NULL, done);
114
115 char * errmsg = NULL;
116 rc = sqlite3_exec(handle, query, NULL, NULL, &errmsg);
117 if (errmsg) {
118 LOGE("authdb: exec, (%i) %s", rc, errmsg);
119 sqlite3_free(errmsg);
120 }
121
122 done:
123 return rc;
124 }
125
126 struct _db_upgrade_stages {
127 int pre;
128 int main;
129 int post;
130 };
131
132 static struct _db_upgrade_stages auth_upgrade_script[] = {
133 { .pre = -1, .main = 0, .post = -1 } // Create version AUTHDB_VERSION databse.
134 };
135
136 static int32_t _db_run_script(authdb_connection_t dbconn, int number)
137 {
138 int32_t s3e;
139
140 /* Script -1 == skip this step. */
141 if (number < 0)
142 return SQLITE_OK;
143
144 /* If we are attempting to run a script we don't have, fail. */
145 if ((size_t)number >= sizeof(authdb_upgrade_sql) / sizeof(char*))
146 return SQLITE_CORRUPT;
147
148 s3e = _sqlite3_exec(dbconn->handle, authdb_upgrade_sql[number]);
149
150 return s3e;
151 }
152
153 static int32_t _db_upgrade_from_version(authdb_connection_t dbconn, int32_t version)
154 {
155 int32_t s3e;
156
157 /* If we are attempting to upgrade to a version greater than what we have
158 an upgrade script for, fail. */
159 if (version < 0 ||
160 (size_t)version >= sizeof(auth_upgrade_script) / sizeof(struct _db_upgrade_stages))
161 return SQLITE_CORRUPT;
162
163 struct _db_upgrade_stages *script = &auth_upgrade_script[version];
164 s3e = _db_run_script(dbconn, script->pre);
165 if (s3e == SQLITE_OK)
166 s3e = _db_run_script(dbconn, script->main);
167 if (s3e == SQLITE_OK)
168 s3e = _db_run_script(dbconn, script->post);
169
170 return s3e;
171 }
172
173 static void _printCFError(const char * errmsg, CFErrorRef err)
174 {
175 if (err) {
176 CFStringRef errString = NULL;
177 errString = CFErrorCopyDescription(err);
178 char *tmp = _copy_cf_string(errString, NULL);
179 LOGV("%s, %s", errmsg, tmp);
180 free_safe(tmp);
181 CFReleaseSafe(errString);
182 } else {
183 LOGV("%s", errmsg);
184 }
185 }
186
187 static void _db_load_data(authdb_connection_t dbconn, auth_items_t config)
188 {
189 CFURLRef authURL = NULL;
190 CFPropertyListRef plist = NULL;
191 CFDataRef data = NULL;
192 int32_t rc = 0;
193 CFErrorRef err = NULL;
194 CFTypeRef value = NULL;
195 CFAbsoluteTime ts = 0;
196 CFAbsoluteTime old_ts = 0;
197 Boolean ok;
198
199 authURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR(AUTHDB_DATA), kCFURLPOSIXPathStyle, false);
200 require_action(authURL != NULL, done, LOGE("authdb: file not found %s", AUTHDB_DATA));
201
202 ok = CFURLCopyResourcePropertyForKey(authURL, kCFURLContentModificationDateKey, &value, &err);
203 require_action(ok && value != NULL, done, _printCFError("authdb: failed to get modification date", err));
204
205 if (CFGetTypeID(value) == CFDateGetTypeID()) {
206 ts = CFDateGetAbsoluteTime(value);
207 }
208
209 old_ts = auth_items_get_double(config, "data_ts");
210
211 // <rdar://problem/17484375> SEED: BUG: Fast User Switching Not Working
212 // After Mavericks => Yosemite upgrade install, the new Yosemite rule "system.login.fus" was missing.
213 // Somehow (probably during install) ts < old_ts, even though that should never happen.
214 // Solution: always import plist and update db when time stamps don't match.
215 // After a successful import, old_ts = ts below.
216 if (ts != old_ts) {
217 LOGV("authdb: %s modified old=%f, new=%f", AUTHDB_DATA, old_ts, ts);
218 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, authURL, &data, NULL, NULL, (SInt32*)&rc);
219 require_noerr_action(rc, done, LOGE("authdb: failed to load %s", AUTHDB_DATA));
220
221 plist = CFPropertyListCreateWithData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL, &err);
222 require_action(err == NULL, done, _printCFError("authdb: failed to read plist", err));
223
224 if (authdb_import_plist(dbconn, plist, true)) {
225 LOGD("authdb: updating data_ts");
226 auth_items_t update = auth_items_create();
227 auth_items_set_double(update, "data_ts", ts);
228 authdb_set_key_value(dbconn, "config", update);
229 CFReleaseSafe(update);
230 }
231 }
232
233 done:
234 CFReleaseSafe(value);
235 CFReleaseSafe(authURL);
236 CFReleaseSafe(plist);
237 CFReleaseSafe(err);
238 CFReleaseSafe(data);
239 }
240
241 static bool _truncate_db(authdb_connection_t dbconn)
242 {
243 int32_t rc = SQLITE_ERROR;
244 int32_t flags = SQLITE_TRUNCATE_JOURNALMODE_WAL | SQLITE_TRUNCATE_AUTOVACUUM_FULL;
245 rc = sqlite3_file_control(dbconn->handle, NULL, SQLITE_TRUNCATE_DATABASE, &flags);
246 if (rc != SQLITE_OK) {
247 LOGV("Failed to delete db handle! SQLite error %i.\n", rc);
248 if (rc == SQLITE_IOERR) {
249 // Unable to recover successfully if we can't truncate
250 abort();
251 }
252 }
253
254 return rc == SQLITE_OK;
255 }
256
257 static void _handle_corrupt_db(authdb_connection_t dbconn)
258 {
259 int32_t rc = SQLITE_ERROR;
260 char buf[PATH_MAX+1];
261 sqlite3 *corrupt_db = NULL;
262
263 snprintf(buf, sizeof(buf), "%s-corrupt", dbconn->db->db_path);
264 if (sqlite3_open(buf, &corrupt_db) == SQLITE_OK) {
265
266 int on = 1;
267 sqlite3_file_control(corrupt_db, 0, SQLITE_FCNTL_PERSIST_WAL, &on);
268
269 rc = sqlite3_file_control(corrupt_db, NULL, SQLITE_REPLACE_DATABASE, (void *)dbconn->handle);
270 if (SQLITE_OK == rc) {
271 LOGE("Database at path %s is corrupt. Copying it to %s for further investigation.", dbconn->db->db_path, buf);
272 } else {
273 LOGE("Tried to copy corrupt database at path %s, but we failed with SQLite error %i.", dbconn->db->db_path, rc);
274 }
275 sqlite3_close(corrupt_db);
276 }
277
278 _truncate_db(dbconn);
279 }
280
281 static int32_t _db_maintenance(authdb_connection_t dbconn)
282 {
283 __block int32_t s3e = SQLITE_OK;
284 __block auth_items_t config = NULL;
285
286 authdb_transaction(dbconn, AuthDBTransactionNormal, ^bool(void) {
287
288 authdb_get_key_value(dbconn, "config", &config);
289
290 // We don't have a config table
291 if (NULL == config) {
292 LOGV("authdb: initializing database");
293 s3e = _db_upgrade_from_version(dbconn, 0);
294 require_noerr_action(s3e, done, LOGE("authdb: failed to initialize database %i", s3e));
295
296 s3e = authdb_get_key_value(dbconn, "config", &config);
297 require_noerr_action(s3e, done, LOGE("authdb: failed to get config %i", s3e));
298 }
299
300 int64_t currentVersion = auth_items_get_int64(config, "version");
301 LOGV("authdb: current db ver=%lli", currentVersion);
302 if (currentVersion < AUTHDB_VERSION) {
303 LOGV("authdb: upgrading schema");
304 s3e = _db_upgrade_from_version(dbconn, (int32_t)currentVersion);
305
306 auth_items_set_int64(config, "version", AUTHDB_VERSION);
307 authdb_set_key_value(dbconn, "config", config);
308 }
309
310 done:
311 return true;
312 });
313
314 CFReleaseSafe(config);
315 return s3e;
316 }
317
318 //static void unlock_notify_cb(void **apArg, int nArg AUTH_UNUSED){
319 // dispatch_semaphore_t semaphore = (dispatch_semaphore_t)apArg[0];
320 // dispatch_semaphore_signal(semaphore);
321 //}
322 //
323 //static int32_t _wait_for_unlock_notify(authdb_connection_t dbconn, sqlite3_stmt * stmt)
324 //{
325 // int32_t rc;
326 // dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
327 //
328 // rc = sqlite3_unlock_notify(dbconn->handle, unlock_notify_cb, semaphore);
329 // require(!rc, done);
330 //
331 // if (dispatch_semaphore_wait(semaphore, 5*NSEC_PER_SEC) != 0) {
332 // LOGV("authdb: timeout occurred!");
333 // sqlite3_unlock_notify(dbconn->handle, NULL, NULL);
334 // rc = SQLITE_LOCKED;
335 // } else if (stmt){
336 // sqlite3_reset(stmt);
337 // }
338 //
339 //done:
340 // dispatch_release(semaphore);
341 // return rc;
342 //}
343
344 static bool _is_busy(int32_t rc)
345 {
346 return SQLITE_BUSY == rc || SQLITE_LOCKED == rc;
347 }
348
349 static void _checkResult(authdb_connection_t dbconn, int32_t rc, const char * fn_name, sqlite3_stmt * stmt)
350 {
351 bool isCorrupt = (SQLITE_CORRUPT == rc) || (SQLITE_NOTADB == rc) || (SQLITE_IOERR == rc);
352
353 if (isCorrupt) {
354 _handle_corrupt_db(dbconn);
355 authdb_maintenance(dbconn);
356 } else if (SQLITE_CONSTRAINT == rc || SQLITE_READONLY == rc) {
357 if (stmt) {
358 LOGV("authdb: %s %s for %s", fn_name, sqlite3_errmsg(dbconn->handle), sqlite3_sql(stmt));
359 } else {
360 LOGV("authdb: %s %s", fn_name, sqlite3_errmsg(dbconn->handle));
361 }
362 }
363 }
364
365 char * authdb_copy_sql_string(sqlite3_stmt * sql,int32_t col)
366 {
367 char * result = NULL;
368 const char * sql_str = (const char *)sqlite3_column_text(sql, col);
369 if (sql_str) {
370 size_t len = strlen(sql_str) + 1;
371 result = (char*)calloc(1u, len);
372 check(result != NULL);
373
374 strlcpy(result, sql_str, len);
375 }
376 return result;
377 }
378
379 #pragma mark -
380 #pragma mark authdb_t
381
382 static void
383 _authdb_finalize(CFTypeRef value)
384 {
385 authdb_t db = (authdb_t)value;
386
387 CFReleaseSafe(db->connections);
388 dispatch_release(db->queue);
389 free_safe(db->db_path);
390 }
391
392 AUTH_TYPE_INSTANCE(authdb,
393 .init = NULL,
394 .copy = NULL,
395 .finalize = _authdb_finalize,
396 .equal = NULL,
397 .hash = NULL,
398 .copyFormattingDesc = NULL,
399 .copyDebugDesc = NULL
400 );
401
402 static CFTypeID authdb_get_type_id() {
403 static CFTypeID type_id = _kCFRuntimeNotATypeID;
404 static dispatch_once_t onceToken;
405
406 dispatch_once(&onceToken, ^{
407 type_id = _CFRuntimeRegisterClass(&_auth_type_authdb);
408 });
409
410 return type_id;
411 }
412
413 authdb_t
414 authdb_create()
415 {
416 authdb_t db = NULL;
417
418 db = (authdb_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, authdb_get_type_id(), AUTH_CLASS_SIZE(authdb), NULL);
419 require(db != NULL, done);
420
421 db->queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
422 db->connections = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
423
424 if (getenv("__OSINSTALL_ENVIRONMENT") != NULL) {
425 LOGV("authdb: running from installer");
426 db->db_path = _copy_string("file::memory:?cache=shared");
427 } else {
428 db->db_path = _copy_string(AUTHDB);
429 }
430
431 done:
432 return db;
433 }
434
435 authdb_connection_t authdb_connection_acquire(authdb_t db)
436 {
437 __block authdb_connection_t dbconn = NULL;
438 #if DEBUG
439 static int32_t total = 0;
440 #endif
441 dispatch_sync(db->queue, ^{
442 CFIndex count = CFArrayGetCount(db->connections);
443 if (count) {
444 dbconn = (authdb_connection_t)CFArrayGetValueAtIndex(db->connections, 0);
445 CFArrayRemoveValueAtIndex(db->connections, 0);
446 } else {
447 dbconn = authdb_connection_create(db);
448 #if DEBUG
449 total++;
450 LOGV("authdb: no handles available total: %i", total);
451 #endif
452 }
453 });
454
455 return dbconn;
456 }
457
458 void authdb_connection_release(authdb_connection_t * dbconn)
459 {
460 if (!dbconn || !(*dbconn))
461 return;
462
463 authdb_connection_t tmp = *dbconn;
464 *dbconn = NULL;
465
466 dispatch_async(tmp->db->queue, ^{
467 CFIndex count = CFArrayGetCount(tmp->db->connections);
468 if (count <= AUTHDB_MAX_HANDLES) {
469 CFArrayAppendValue(tmp->db->connections, tmp);
470 } else {
471 LOGD("authdb: freeing extra connection");
472 CFRelease(tmp);
473 }
474 });
475 }
476
477 static bool _db_check_corrupted(authdb_connection_t dbconn)
478 {
479 bool isCorrupted = true;
480 sqlite3_stmt *stmt = NULL;
481 int32_t rc;
482
483 rc = sqlite3_prepare_v2(dbconn->handle, "PRAGMA integrity_check;", -1, &stmt, NULL);
484 if (rc == SQLITE_LOCKED || rc == SQLITE_BUSY) {
485 LOGV("authdb: warning error %i when running integrity check", rc);
486 isCorrupted = false;
487
488 } else if (rc == SQLITE_OK) {
489 rc = sqlite3_step(stmt);
490
491 if (rc == SQLITE_LOCKED || rc == SQLITE_BUSY) {
492 LOGV("authdb: warning error %i when running integrity check", rc);
493 isCorrupted = false;
494 } else if (rc == SQLITE_ROW) {
495 const char * result = (const char*)sqlite3_column_text(stmt, 0);
496
497 if (result && strncasecmp(result, "ok", 3) == 0) {
498 isCorrupted = false;
499 }
500 }
501 }
502
503 sqlite3_finalize(stmt);
504 return isCorrupted;
505 }
506
507 bool authdb_maintenance(authdb_connection_t dbconn)
508 {
509 LOGD("authdb: starting maintenance");
510 int32_t rc = SQLITE_ERROR;
511 auth_items_t config = NULL;
512
513 bool isCorrupted = _db_check_corrupted(dbconn);
514 LOGD("authdb: integrity check=%s", isCorrupted ? "fail" : "pass");
515
516 if (isCorrupted) {
517 _handle_corrupt_db(dbconn);
518 }
519
520 _db_maintenance(dbconn);
521
522 rc = authdb_get_key_value(dbconn, "config", &config);
523 require_noerr_action(rc, done, LOGV("authdb: maintenance failed %i", rc));
524
525 _db_load_data(dbconn, config);
526
527 done:
528 CFReleaseSafe(config);
529 LOGD("authdb: finished maintenance");
530 return rc == SQLITE_OK;
531 }
532
533 int32_t
534 authdb_exec(authdb_connection_t dbconn, const char * query)
535 {
536 int32_t rc = SQLITE_ERROR;
537 require(query != NULL, done);
538
539 rc = _sqlite3_exec(dbconn->handle, query);
540 _checkResult(dbconn, rc, __FUNCTION__, NULL);
541
542 done:
543 return rc;
544 }
545
546 static int32_t _prepare(authdb_connection_t dbconn, const char * sql, sqlite3_stmt ** out_stmt)
547 {
548 int32_t rc;
549 sqlite3_stmt * stmt = NULL;
550
551 require_action(sql != NULL, done, rc = SQLITE_ERROR);
552 require_action(out_stmt != NULL, done, rc = SQLITE_ERROR);
553
554 rc = sqlite3_prepare_v2(dbconn->handle, sql, -1, &stmt, NULL);
555 require_noerr_action(rc, done, LOGV("authdb: prepare (%i) %s", rc, sqlite3_errmsg(dbconn->handle)));
556
557 *out_stmt = stmt;
558
559 done:
560 _checkResult(dbconn, rc, __FUNCTION__, stmt);
561 return rc;
562 }
563
564 static void _parseItemsAtIndex(sqlite3_stmt * stmt, int32_t col, auth_items_t items, const char * key)
565 {
566 switch (sqlite3_column_type(stmt, col)) {
567 case SQLITE_FLOAT:
568 auth_items_set_double(items, key, sqlite3_column_double(stmt, col));
569 break;
570 case SQLITE_INTEGER:
571 auth_items_set_int64(items, key, sqlite3_column_int64(stmt, col));
572 break;
573 case SQLITE_BLOB:
574 auth_items_set_data(items,
575 key,
576 sqlite3_column_blob(stmt, col),
577 (size_t)sqlite3_column_bytes(stmt, col));
578 break;
579 case SQLITE_NULL:
580 break;
581 case SQLITE_TEXT:
582 default:
583 auth_items_set_string(items, key, (const char *)sqlite3_column_text(stmt, col));
584 break;
585 }
586
587 // LOGD("authdb: col=%s, val=%s, type=%i", sqlite3_column_name(stmt, col), sqlite3_column_text(stmt, col), sqlite3_column_type(stmt,col));
588 }
589
590 static int32_t _bindItemsAtIndex(sqlite3_stmt * stmt, int col, auth_items_t items, const char * key)
591 {
592 int32_t rc;
593 switch (auth_items_get_type(items, key)) {
594 case AI_TYPE_INT:
595 rc = sqlite3_bind_int64(stmt, col, auth_items_get_int(items, key));
596 break;
597 case AI_TYPE_UINT:
598 rc = sqlite3_bind_int64(stmt, col, auth_items_get_uint(items, key));
599 break;
600 case AI_TYPE_INT64:
601 rc = sqlite3_bind_int64(stmt, col, auth_items_get_int64(items, key));
602 break;
603 case AI_TYPE_UINT64:
604 rc = sqlite3_bind_int64(stmt, col, (int64_t)auth_items_get_uint64(items, key));
605 break;
606 case AI_TYPE_DOUBLE:
607 rc = sqlite3_bind_double(stmt, col, auth_items_get_double(items, key));
608 break;
609 case AI_TYPE_BOOL:
610 rc = sqlite3_bind_int64(stmt, col, auth_items_get_bool(items, key));
611 break;
612 case AI_TYPE_DATA:
613 {
614 size_t blobLen = 0;
615 const void * blob = auth_items_get_data(items, key, &blobLen);
616 rc = sqlite3_bind_blob(stmt, col, blob, (int32_t)blobLen, NULL);
617 }
618 break;
619 case AI_TYPE_STRING:
620 rc = sqlite3_bind_text(stmt, col, auth_items_get_string(items, key), -1, NULL);
621 break;
622 default:
623 rc = sqlite3_bind_null(stmt, col);
624 break;
625 }
626 if (rc != SQLITE_OK) {
627 LOGV("authdb: auth_items bind failed (%i)", rc);
628 }
629 return rc;
630 }
631
632 int32_t authdb_get_key_value(authdb_connection_t dbconn, const char * table, auth_items_t * out_items)
633 {
634 int32_t rc = SQLITE_ERROR;
635 char * query = NULL;
636 sqlite3_stmt * stmt = NULL;
637 auth_items_t items = NULL;
638
639 require(table != NULL, done);
640 require(out_items != NULL, done);
641
642 asprintf(&query, "SELECT * FROM %s", table);
643
644 rc = _prepare(dbconn, query, &stmt);
645 require_noerr(rc, done);
646
647 items = auth_items_create();
648 while ((rc = sqlite3_step(stmt)) != SQLITE_DONE) {
649 switch (rc) {
650 case SQLITE_ROW:
651 _parseItemsAtIndex(stmt, 1, items, (const char*)sqlite3_column_text(stmt, 0));
652 break;
653 default:
654 _checkResult(dbconn, rc, __FUNCTION__, stmt);
655 if (_is_busy(rc)) {
656 sleep(AUTHDB_BUSY_DELAY);
657 } else {
658 require_noerr_action(rc, done, LOGV("authdb: get_key_value (%i) %s", rc, sqlite3_errmsg(dbconn->handle)));
659 }
660 break;
661 }
662 }
663
664 rc = SQLITE_OK;
665 CFRetain(items);
666 *out_items = items;
667
668 done:
669 CFReleaseSafe(items);
670 free_safe(query);
671 sqlite3_finalize(stmt);
672 return rc;
673 }
674
675 int32_t authdb_set_key_value(authdb_connection_t dbconn, const char * table, auth_items_t items)
676 {
677 __block int32_t rc = SQLITE_ERROR;
678 char * query = NULL;
679 sqlite3_stmt * stmt = NULL;
680
681 require(table != NULL, done);
682 require(items != NULL, done);
683
684 asprintf(&query, "INSERT OR REPLACE INTO %s VALUES (?,?)", table);
685
686 rc = _prepare(dbconn, query, &stmt);
687 require_noerr(rc, done);
688
689 auth_items_iterate(items, ^bool(const char *key) {
690 sqlite3_reset(stmt);
691 _checkResult(dbconn, rc, __FUNCTION__, stmt);
692
693 sqlite3_bind_text(stmt, 1, key, -1, NULL);
694 _bindItemsAtIndex(stmt, 2, items, key);
695
696 rc = sqlite3_step(stmt);
697 if (rc != SQLITE_DONE) {
698 _checkResult(dbconn, rc, __FUNCTION__, stmt);
699 LOGV("authdb: set_key_value, step (%i) %s", rc, sqlite3_errmsg(dbconn->handle));
700 }
701
702 return true;
703 });
704
705 done:
706 free_safe(query);
707 sqlite3_finalize(stmt);
708 return rc;
709 }
710
711 static int32_t _begin_transaction_type(authdb_connection_t dbconn, AuthDBTransactionType type)
712 {
713 int32_t result = SQLITE_ERROR;
714
715 const char * query = NULL;
716 switch (type) {
717 case AuthDBTransactionImmediate:
718 query = "BEGIN IMMEDATE;";
719 break;
720 case AuthDBTransactionExclusive:
721 query = "BEGIN EXCLUSIVE;";
722 break;
723 case AuthDBTransactionNormal:
724 query = "BEGIN;";
725 break;
726 default:
727 break;
728 }
729
730 result = SQLITE_OK;
731
732 if (query != NULL && sqlite3_get_autocommit(dbconn->handle) != 0) {
733 result = _sqlite3_exec(dbconn->handle, query);
734 }
735
736 return result;
737 }
738
739 static int32_t _end_transaction(authdb_connection_t dbconn, bool commit)
740 {
741 if (commit) {
742 return _sqlite3_exec(dbconn->handle, "END;");
743 } else {
744 return _sqlite3_exec(dbconn->handle, "ROLLBACK;");
745 }
746 }
747
748 bool authdb_transaction(authdb_connection_t dbconn, AuthDBTransactionType type, bool (^t)(void))
749 {
750 int32_t result = SQLITE_ERROR;
751 bool commit = false;
752
753 result = _begin_transaction_type(dbconn, type);
754 require_action(result == SQLITE_OK, done, LOGV("authdb: transaction begin failed %i", result));
755
756 commit = t();
757
758 result = _end_transaction(dbconn, commit);
759 require_action(result == SQLITE_OK, done, commit = false; LOGV("authdb: transaction end failed %i", result));
760
761 done:
762 return commit;
763 }
764
765 bool authdb_step(authdb_connection_t dbconn, const char * sql, void (^bind_stmt)(sqlite3_stmt*), authdb_iterator_t iter)
766 {
767 bool result = false;
768 sqlite3_stmt * stmt = NULL;
769 int32_t rc = SQLITE_ERROR;
770
771 require_action(sql != NULL, done, rc = SQLITE_ERROR);
772
773 rc = _prepare(dbconn, sql, &stmt);
774 require_noerr(rc, done);
775
776 if (bind_stmt) {
777 bind_stmt(stmt);
778 }
779
780 int32_t count = sqlite3_column_count(stmt);
781
782 auth_items_t items = NULL;
783 while ((rc = sqlite3_step(stmt)) != SQLITE_DONE) {
784 switch (rc) {
785 case SQLITE_ROW:
786 {
787 if (iter) {
788 items = auth_items_create();
789 for (int i = 0; i < count; i++) {
790 _parseItemsAtIndex(stmt, i, items, sqlite3_column_name(stmt, i));
791 }
792 result = iter(items);
793 CFReleaseNull(items);
794 if (!result) {
795 goto done;
796 }
797 }
798 }
799 break;
800 default:
801 if (_is_busy(rc)) {
802 LOGV("authdb: %s", sqlite3_errmsg(dbconn->handle));
803 sleep(AUTHDB_BUSY_DELAY);
804 sqlite3_reset(stmt);
805 } else {
806 require_noerr_action(rc, done, LOGV("authdb: step (%i) %s", rc, sqlite3_errmsg(dbconn->handle)));
807 }
808 break;
809 }
810 }
811
812 done:
813 _checkResult(dbconn, rc, __FUNCTION__, stmt);
814 sqlite3_finalize(stmt);
815 return rc == SQLITE_DONE;
816 }
817
818 void authdb_checkpoint(authdb_connection_t dbconn)
819 {
820 int32_t rc = sqlite3_wal_checkpoint(dbconn->handle, NULL);
821 if (rc != SQLITE_OK) {
822 LOGV("authdb: checkpoit failed %i", rc);
823 }
824 }
825
826 static CFMutableArrayRef
827 _copy_rules_dict(RuleType type, CFDictionaryRef plist, authdb_connection_t dbconn)
828 {
829 CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
830 require(result != NULL, done);
831
832 _cf_dictionary_iterate(plist, ^bool(CFTypeRef key, CFTypeRef value) {
833 if (CFGetTypeID(key) != CFStringGetTypeID()) {
834 return true;
835 }
836
837 if (CFGetTypeID(value) != CFDictionaryGetTypeID()) {
838 return true;
839 }
840
841 rule_t rule = rule_create_with_plist(type, key, value, dbconn);
842 if (rule) {
843 CFArrayAppendValue(result, rule);
844 CFReleaseSafe(rule);
845 }
846
847 return true;
848 });
849
850 done:
851 return result;
852 }
853
854 static void
855 _import_rules(authdb_connection_t dbconn, CFMutableArrayRef rules, bool version_check, CFAbsoluteTime now)
856 {
857 CFMutableArrayRef notcommited = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
858 CFIndex count = CFArrayGetCount(rules);
859
860 for (CFIndex i = 0; i < count; i++) {
861 rule_t rule = (rule_t)CFArrayGetValueAtIndex(rules, i);
862
863 bool update = false;
864 if (version_check) {
865 if (rule_get_id(rule) != 0) { // rule already exists see if we need to update
866 rule_t current = rule_create_with_string(rule_get_name(rule), dbconn);
867 if (rule_get_version(rule) > rule_get_version(current)) {
868 update = true;
869 }
870 CFReleaseSafe(current);
871
872 if (!update) {
873 continue;
874 }
875 }
876 }
877
878 __block bool delayCommit = false;
879
880 switch (rule_get_type(rule)) {
881 case RT_RULE:
882 rule_delegates_iterator(rule, ^bool(rule_t delegate) {
883 if (rule_get_id(delegate) == 0) {
884 // fetch the rule from the database if it was previously committed
885 rule_sql_fetch(delegate, dbconn);
886 }
887 if (rule_get_id(delegate) == 0) {
888 LOGD("authdb: delaying %s waiting for delegate %s", rule_get_name(rule), rule_get_name(delegate));
889 delayCommit = true;
890 return false;
891 }
892 return true;
893 });
894 break;
895 default:
896 break;
897 }
898
899 if (!delayCommit) {
900 bool success = rule_sql_commit(rule, dbconn, now, NULL);
901 LOGV("authdb: %s %s %s %s",
902 update ? "updating" : "importing",
903 rule_get_type(rule) == RT_RULE ? "rule" : "right",
904 rule_get_name(rule), success ? "success" : "FAIL");
905 if (!success) {
906 CFArrayAppendValue(notcommited, rule);
907 }
908 } else {
909 CFArrayAppendValue(notcommited, rule);
910 }
911 }
912 CFArrayRemoveAllValues(rules);
913 CFArrayAppendArray(rules, notcommited, CFRangeMake(0, CFArrayGetCount(notcommited)));
914 CFReleaseSafe(notcommited);
915 }
916
917 bool
918 authdb_import_plist(authdb_connection_t dbconn, CFDictionaryRef plist, bool version_check)
919 {
920 bool result = false;
921
922 LOGV("authdb: starting import");
923
924 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
925 CFMutableArrayRef rights = NULL;
926 CFMutableArrayRef rules = NULL;
927 require(plist != NULL, done);
928
929 CFTypeRef rightsDict = CFDictionaryGetValue(plist, CFSTR("rights"));
930 if (rightsDict && CFGetTypeID(rightsDict) == CFDictionaryGetTypeID()) {
931 rights = _copy_rules_dict(RT_RIGHT, rightsDict, dbconn);
932 }
933
934 CFTypeRef rulesDict = CFDictionaryGetValue(plist, CFSTR("rules"));
935 if (rulesDict && CFGetTypeID(rulesDict) == CFDictionaryGetTypeID()) {
936 rules = _copy_rules_dict(RT_RULE, rulesDict, dbconn);
937 }
938
939 LOGV("authdb: rights = %li", CFArrayGetCount(rights));
940 LOGV("authdb: rules = %li", CFArrayGetCount(rules));
941
942 CFIndex count;
943 // first pass import base rules without delegations
944 // remaining import rules that delegate to other rules
945 // loop upto 3 times to commit dependent rules first
946 for (int32_t j = 0; j < 3; j++) {
947 count = CFArrayGetCount(rules);
948 if (!count)
949 break;
950
951 _import_rules(dbconn, rules, version_check, now);
952 }
953
954 _import_rules(dbconn, rights, version_check, now);
955
956 if (CFArrayGetCount(rights) == 0) {
957 result = true;
958 }
959
960 authdb_checkpoint(dbconn);
961
962 done:
963 CFReleaseSafe(rights);
964 CFReleaseSafe(rules);
965
966 LOGV("authdb: finished import, %s", result ? "succeeded" : "failed");
967
968 return result;
969 }
970
971 #pragma mark -
972 #pragma mark authdb_connection_t
973
974 static bool _sql_profile_enabled(void)
975 {
976 static bool profile_enabled = false;
977
978 #if DEBUG
979 static dispatch_once_t onceToken;
980
981 //sudo defaults write /Library/Preferences/com.apple.security.auth profile -bool true
982 dispatch_once(&onceToken, ^{
983 CFTypeRef profile = (CFNumberRef)CFPreferencesCopyValue(CFSTR("profile"), CFSTR(SECURITY_AUTH_NAME), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
984
985 if (profile && CFGetTypeID(profile) == CFBooleanGetTypeID()) {
986 profile_enabled = CFBooleanGetValue((CFBooleanRef)profile);
987 }
988
989 LOGV("authdb: sql profile: %s", profile_enabled ? "enabled" : "disabled");
990
991 CFReleaseSafe(profile);
992 });
993 #endif
994
995 return profile_enabled;
996 }
997
998 static void _profile(void *context AUTH_UNUSED, const char *sql, sqlite3_uint64 ns) {
999 LOGV("==\nauthdb: %s\nTime: %llu ms\n", sql, ns >> 20);
1000 }
1001
1002 static sqlite3 * _create_handle(authdb_t db)
1003 {
1004 bool dbcreated = false;
1005 sqlite3 * handle = NULL;
1006 int32_t rc = sqlite3_open_v2(db->db_path, &handle, SQLITE_OPEN_READWRITE, NULL);
1007
1008 if (rc != SQLITE_OK) {
1009 char * tmp = dirname(db->db_path);
1010 if (tmp) {
1011 mkpath_np(tmp, 0700);
1012 }
1013 rc = sqlite3_open_v2(db->db_path, &handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
1014 dbcreated = true;
1015 }
1016 require_noerr_action(rc, done, LOGE("authdb: open %s (%i) %s", db->db_path, rc, sqlite3_errmsg(handle)));
1017
1018 if (_sql_profile_enabled()) {
1019 sqlite3_profile(handle, _profile, NULL);
1020 }
1021
1022 _sqlite3_exec(handle, "PRAGMA foreign_keys = ON");
1023 _sqlite3_exec(handle, "PRAGMA temp_store = MEMORY");
1024
1025 if (dbcreated) {
1026 _sqlite3_exec(handle, "PRAGMA auto_vacuum = FULL");
1027 _sqlite3_exec(handle, "PRAGMA journal_mode = WAL");
1028
1029 int on = 1;
1030 sqlite3_file_control(handle, 0, SQLITE_FCNTL_PERSIST_WAL, &on);
1031
1032 chmod(db->db_path, S_IRUSR | S_IWUSR);
1033 }
1034
1035 done:
1036 return handle;
1037 }
1038
1039 static void
1040 _authdb_connection_finalize(CFTypeRef value)
1041 {
1042 authdb_connection_t dbconn = (authdb_connection_t)value;
1043
1044 if (dbconn->handle) {
1045 sqlite3_close(dbconn->handle);
1046 }
1047 CFReleaseSafe(dbconn->db);
1048 }
1049
1050 AUTH_TYPE_INSTANCE(authdb_connection,
1051 .init = NULL,
1052 .copy = NULL,
1053 .finalize = _authdb_connection_finalize,
1054 .equal = NULL,
1055 .hash = NULL,
1056 .copyFormattingDesc = NULL,
1057 .copyDebugDesc = NULL
1058 );
1059
1060 static CFTypeID authdb_connection_get_type_id() {
1061 static CFTypeID type_id = _kCFRuntimeNotATypeID;
1062 static dispatch_once_t onceToken;
1063
1064 dispatch_once(&onceToken, ^{
1065 type_id = _CFRuntimeRegisterClass(&_auth_type_authdb_connection);
1066 });
1067
1068 return type_id;
1069 }
1070
1071 authdb_connection_t
1072 authdb_connection_create(authdb_t db)
1073 {
1074 authdb_connection_t dbconn = NULL;
1075
1076 dbconn = (authdb_connection_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, authdb_connection_get_type_id(), AUTH_CLASS_SIZE(authdb_connection), NULL);
1077 require(dbconn != NULL, done);
1078
1079 dbconn->db = (authdb_t)CFRetain(db);
1080 dbconn->handle = _create_handle(dbconn->db);
1081
1082 done:
1083 return dbconn;
1084 }