]> git.saurik.com Git - apple/security.git/blob - OSX/authd/authdb.c
Security-59754.41.1.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: handles count: %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(AUTHD_LOG, "authdb: %{public}s (will try to recover)", 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 int64_t currVer = rule_get_version(current);
947 int64_t newVer = rule_get_version(rule);
948
949 if (newVer > currVer) {
950 update = true;
951 }
952 CFReleaseSafe(current);
953
954 if (!update) {
955 continue;
956 } else {
957 os_log(AUTHD_LOG, "authdb: right %{public}s new version %lld vs existing version %lld, will update", rule_get_name(rule), newVer, currVer);
958 }
959 }
960 }
961
962 __block bool delayCommit = false;
963
964 switch (rule_get_type(rule)) {
965 case RT_RULE:
966 rule_delegates_iterator(rule, ^bool(rule_t delegate) {
967 if (rule_get_id(delegate) == 0) {
968 // fetch the rule from the database if it was previously committed
969 rule_sql_fetch(delegate, dbconn);
970 }
971 if (rule_get_id(delegate) == 0) {
972 os_log_debug(AUTHD_LOG, "authdb: delaying %{public}s waiting for delegate %{public}s", rule_get_name(rule), rule_get_name(delegate));
973 delayCommit = true;
974 return false;
975 }
976 return true;
977 });
978 break;
979 default:
980 break;
981 }
982
983 if (!delayCommit) {
984 bool success = rule_sql_commit(rule, dbconn, now, NULL);
985 os_log(AUTHD_LOG, "authdb: %{public}s %{public}s %{public}s %{public}s",
986 update ? "updating" : "importing",
987 rule_get_type(rule) == RT_RULE ? "rule" : "right",
988 rule_get_name(rule), success ? "success" : "FAIL");
989 if (!success) {
990 CFArrayAppendValue(notcommited, rule);
991 }
992 } else {
993 CFArrayAppendValue(notcommited, rule);
994 }
995 }
996 CFArrayRemoveAllValues(rules);
997 CFArrayAppendArray(rules, notcommited, CFRangeMake(0, CFArrayGetCount(notcommited)));
998 CFReleaseSafe(notcommited);
999 }
1000
1001 bool
1002 authdb_import_plist(authdb_connection_t dbconn, CFDictionaryRef plist, bool version_check, const char *name)
1003 {
1004 bool result = false;
1005
1006 os_log_debug(AUTHD_LOG, "authdb: starting import");
1007
1008 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
1009 CFMutableArrayRef rights = NULL;
1010 CFMutableArrayRef rules = NULL;
1011 require(plist != NULL, done);
1012
1013 CFTypeRef rightsDict = CFDictionaryGetValue(plist, CFSTR("rights"));
1014 if (rightsDict && CFGetTypeID(rightsDict) == CFDictionaryGetTypeID()) {
1015 rights = _copy_rules_dict(RT_RIGHT, rightsDict, dbconn);
1016 }
1017
1018 CFTypeRef rulesDict = CFDictionaryGetValue(plist, CFSTR("rules"));
1019 if (rulesDict && CFGetTypeID(rulesDict) == CFDictionaryGetTypeID()) {
1020 rules = _copy_rules_dict(RT_RULE, rulesDict, dbconn);
1021 }
1022
1023 os_log_debug(AUTHD_LOG, "authdb: rights = %li", CFArrayGetCount(rights));
1024 os_log_debug(AUTHD_LOG, "authdb: rules = %li", CFArrayGetCount(rules));
1025
1026 CFIndex count;
1027 // first pass import base rules without delegations
1028 // remaining import rules that delegate to other rules
1029 // loop upto 3 times to commit dependent rules first
1030 for (int32_t j = 0; j < 3; j++) {
1031 count = CFArrayGetCount(rules);
1032 if (!count)
1033 break;
1034
1035 _import_rules(dbconn, rules, version_check, now, name);
1036 }
1037
1038 _import_rules(dbconn, rights, version_check, now, name);
1039
1040 if (CFArrayGetCount(rights) == 0) {
1041 result = true;
1042 }
1043
1044 authdb_checkpoint(dbconn);
1045
1046 done:
1047 CFReleaseSafe(rights);
1048 CFReleaseSafe(rules);
1049
1050 os_log_debug(AUTHD_LOG, "authdb: finished import, %{public}s", result ? "succeeded" : "failed");
1051
1052 return result;
1053 }
1054
1055 #pragma mark -
1056 #pragma mark authdb_connection_t
1057
1058 static bool _sql_profile_enabled(void)
1059 {
1060 static bool profile_enabled = false;
1061
1062 #if DEBUG
1063 static dispatch_once_t onceToken;
1064
1065 //sudo defaults write /Library/Preferences/com.apple.security.auth profile -bool true
1066 dispatch_once(&onceToken, ^{
1067 CFTypeRef profile = (CFNumberRef)CFPreferencesCopyValue(CFSTR("profile"), CFSTR(SECURITY_AUTH_NAME), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
1068
1069 if (profile && CFGetTypeID(profile) == CFBooleanGetTypeID()) {
1070 profile_enabled = CFBooleanGetValue((CFBooleanRef)profile);
1071 }
1072
1073 os_log_debug(AUTHD_LOG, "authdb: sql profile: %{public}s", profile_enabled ? "enabled" : "disabled");
1074
1075 CFReleaseSafe(profile);
1076 });
1077 #endif
1078
1079 return profile_enabled;
1080 }
1081
1082 static void _profile(void *context AUTH_UNUSED, const char *sql, sqlite3_uint64 ns) {
1083 os_log_debug(AUTHD_LOG, "==\nauthdb: %{private}s\nTime: %llu ms\n", sql, ns >> 20);
1084 }
1085
1086 static sqlite3 * _create_handle(authdb_t db)
1087 {
1088 bool dbcreated = false;
1089 sqlite3 * handle = NULL;
1090 int32_t rc = sqlite3_open_v2(db->db_path, &handle, SQLITE_OPEN_READWRITE, NULL);
1091
1092 if (rc != SQLITE_OK) {
1093 os_log_error(AUTHD_LOG, "authdb: open %{public}s (%i) %{public}s", db->db_path, rc, handle ? sqlite3_errmsg(handle) : "no memory for handle");
1094 if (handle) {
1095 sqlite3_close(handle);
1096 }
1097 char * tmp = dirname(db->db_path);
1098 if (tmp) {
1099 mkpath_np(tmp, 0700);
1100 }
1101 rc = sqlite3_open_v2(db->db_path, &handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
1102 dbcreated = true;
1103
1104 if (rc != SQLITE_OK) {
1105 os_log_error(AUTHD_LOG, "authdb: create %{public}s (%i) %{public}s", db->db_path, rc, handle ? sqlite3_errmsg(handle) : "no memory for handle");
1106 if (handle) {
1107 sqlite3_close(handle);
1108 handle = NULL;
1109 }
1110 goto done;
1111 }
1112 }
1113
1114 if (_sql_profile_enabled()) {
1115 sqlite3_profile(handle, _profile, NULL);
1116 }
1117
1118 _sqlite3_exec(handle, "PRAGMA foreign_keys = ON");
1119 _sqlite3_exec(handle, "PRAGMA temp_store = MEMORY");
1120
1121 if (dbcreated) {
1122 _sqlite3_exec(handle, "PRAGMA auto_vacuum = FULL");
1123 _sqlite3_exec(handle, "PRAGMA journal_mode = WAL");
1124
1125 int on = 1;
1126 sqlite3_file_control(handle, 0, SQLITE_FCNTL_PERSIST_WAL, &on);
1127
1128 chmod(db->db_path, S_IRUSR | S_IWUSR);
1129 }
1130
1131 // Let SQLite handle timeouts.
1132 sqlite3_busy_timeout(handle, 5*1000);
1133 done:
1134 return handle;
1135 }
1136
1137 static void
1138 _authdb_connection_finalize(CFTypeRef value)
1139 {
1140 authdb_connection_t dbconn = (authdb_connection_t)value;
1141
1142 if (dbconn->handle) {
1143 sqlite3_close(dbconn->handle);
1144 }
1145 CFReleaseNull(dbconn->db);
1146 }
1147
1148 AUTH_TYPE_INSTANCE(authdb_connection,
1149 .init = NULL,
1150 .copy = NULL,
1151 .finalize = _authdb_connection_finalize,
1152 .equal = NULL,
1153 .hash = NULL,
1154 .copyFormattingDesc = NULL,
1155 .copyDebugDesc = NULL
1156 );
1157
1158 static CFTypeID authdb_connection_get_type_id() {
1159 static CFTypeID type_id = _kCFRuntimeNotATypeID;
1160 static dispatch_once_t onceToken;
1161
1162 dispatch_once(&onceToken, ^{
1163 type_id = _CFRuntimeRegisterClass(&_auth_type_authdb_connection);
1164 });
1165
1166 return type_id;
1167 }
1168
1169 authdb_connection_t
1170 authdb_connection_create(authdb_t db)
1171 {
1172 authdb_connection_t dbconn = NULL;
1173
1174 dbconn = (authdb_connection_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, authdb_connection_get_type_id(), AUTH_CLASS_SIZE(authdb_connection), NULL);
1175 require(dbconn != NULL, done);
1176
1177 dbconn->db = (authdb_t)CFRetain(db);
1178 dbconn->handle = _create_handle(dbconn->db);
1179
1180 done:
1181 return dbconn;
1182 }