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