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