1 /* Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. */
11 #include <sqlite3_private.h>
12 #include <CoreFoundation/CoreFoundation.h>
14 #include "authutilities.h"
20 #define AUTHDB "/var/db/auth.db"
21 #define AUTHDB_DATA "/System/Library/Security/authorization.plist"
23 #define AUTH_STR(x) #x
24 #define AUTH_STRINGIFY(x) AUTH_STR(x)
26 #define AUTHDB_VERSION 2
27 #define AUTHDB_VERSION_STRING AUTH_STRINGIFY(AUTHDB_VERSION)
29 #define AUTHDB_BUSY_DELAY 1
30 #define AUTHDB_MAX_HANDLES 3
32 struct _authdb_connection_s
{
33 __AUTH_BASE_STRUCT_HEADER__
;
40 __AUTH_BASE_STRUCT_HEADER__
;
43 dispatch_queue_t queue
;
44 CFMutableArrayRef connections
;
47 static const char * const authdb_upgrade_sql
[] = {
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"
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)"
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"
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),"
83 "version INTEGER NOT NULL DEFAULT (0),"
84 "created REAL NOT NULL DEFAULT (0),"
85 "modified REAL NOT NULL DEFAULT (0),"
91 "CREATE INDEX a_type ON rules (type);"
92 "CREATE TABLE config ("
93 "'key' TEXT PRIMARY KEY NOT NULL UNIQUE,"
96 "CREATE TABLE prompts ("
97 "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
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"
107 "CREATE INDEX b_r_id ON buttons(r_id);"
108 "INSERT INTO config VALUES('version', '1');" ,
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"
120 static sqlite3
* _create_handle(authdb_t db
);
123 _sqlite3_exec(sqlite3
* handle
, const char * query
)
125 int32_t rc
= SQLITE_ERROR
;
126 require(query
!= NULL
, done
);
128 char * errmsg
= NULL
;
129 rc
= sqlite3_exec(handle
, query
, NULL
, NULL
, &errmsg
);
131 os_log_error(AUTHD_LOG
, "authdb: exec, (%i) %{public}s", rc
, errmsg
);
132 sqlite3_free(errmsg
);
139 struct _db_upgrade_stages
{
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}
150 static int32_t _db_run_script(authdb_connection_t dbconn
, int number
)
154 /* Script -1 == skip this step. */
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
;
162 s3e
= _sqlite3_exec(dbconn
->handle
, authdb_upgrade_sql
[number
]);
167 static int32_t _db_upgrade_from_version(authdb_connection_t dbconn
, int32_t version
)
171 /* If we are attempting to upgrade to a version greater than what we have
172 an upgrade script for, fail. */
174 (size_t)version
>= sizeof(auth_upgrade_script
) / sizeof(struct _db_upgrade_stages
))
175 return SQLITE_CORRUPT
;
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
);
187 static CFDictionaryRef
_copy_plist(auth_items_t config
, CFAbsoluteTime
*outTs
)
189 CFURLRef authURL
= NULL
;
190 CFPropertyListRef plist
= NULL
;
191 CFDataRef data
= NULL
;
193 CFErrorRef err
= NULL
;
194 CFTypeRef value
= NULL
;
195 CFAbsoluteTime ts
= 0;
196 CFAbsoluteTime old_ts
= 0;
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
));
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
));
204 if (CFGetTypeID(value
) == CFDateGetTypeID()) {
205 ts
= CFDateGetAbsoluteTime(value
);
212 old_ts
= auth_items_get_double(config
, "data_ts");
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
));
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
));
230 CFReleaseSafe(authURL
);
231 CFReleaseSafe(value
);
238 static void _repair_broken_kofn_right(authdb_connection_t dbconn
, const char *right
, CFDictionaryRef plist
)
240 if (!right
|| !dbconn
) {
244 CFDictionaryRef localPlist
= plist
;
246 localPlist
= _copy_plist(NULL
, NULL
);
249 // import the broken right
250 os_log(AUTHD_LOG
, "Repairing broken right %{public}s", right
);
251 authdb_import_plist(dbconn
, localPlist
, FALSE
, right
);
253 if (!plist
&& localPlist
) {
254 CFRelease(localPlist
);
258 static void _repair_all_kofns(authdb_connection_t dbconn
, auth_items_t config
)
260 // we want plist to be returned always and not only when never
261 CFDictionaryRef plist
= _copy_plist(NULL
, NULL
);
264 os_log_error(AUTHD_LOG
, "authdb: unable to repair kofns");
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");
272 _repair_broken_kofn_right(dbconn
, name
, plist
);
280 static void _db_load_data(authdb_connection_t dbconn
, auth_items_t config
)
282 CFAbsoluteTime ts
= 0;
283 CFDictionaryRef plist
= _copy_plist(config
, &ts
);
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
);
294 CFReleaseSafe(plist
);
297 static bool _truncate_db(authdb_connection_t dbconn
)
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
310 return rc
== SQLITE_OK
;
313 static void _handle_corrupt_db(authdb_connection_t dbconn
)
315 int32_t rc
= SQLITE_ERROR
;
316 char buf
[PATH_MAX
+1];
317 sqlite3
*corrupt_db
= NULL
;
319 snprintf(buf
, sizeof(buf
), "%s-corrupt", dbconn
->db
->db_path
);
320 if (sqlite3_open(buf
, &corrupt_db
) == SQLITE_OK
) {
323 sqlite3_file_control(corrupt_db
, 0, SQLITE_FCNTL_PERSIST_WAL
, &on
);
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
);
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
);
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.
336 sqlite3_close(corrupt_db
);
338 _truncate_db(dbconn
);
341 static int32_t _db_maintenance(authdb_connection_t dbconn
)
343 __block
int32_t s3e
= SQLITE_OK
;
344 __block auth_items_t config
= NULL
;
346 authdb_transaction(dbconn
, AuthDBTransactionNormal
, ^bool(void) {
348 authdb_get_key_value(dbconn
, "config", true, &config
);
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
));
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
));
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
);
366 auth_items_set_int64(config
, "version", AUTHDB_VERSION
);
367 authdb_set_key_value(dbconn
, "config", config
);
374 CFReleaseSafe(config
);
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);
383 //static int32_t _wait_for_unlock_notify(authdb_connection_t dbconn, sqlite3_stmt * stmt)
386 // dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
388 // rc = sqlite3_unlock_notify(dbconn->handle, unlock_notify_cb, semaphore);
389 // require(!rc, done);
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;
396 // sqlite3_reset(stmt);
400 // dispatch_release(semaphore);
404 static bool _is_busy(int32_t rc
)
406 return SQLITE_BUSY
== rc
|| SQLITE_LOCKED
== rc
;
409 static void _checkResult(authdb_connection_t dbconn
, int32_t rc
, const char * fn_name
, sqlite3_stmt
* stmt
, const bool skip_maintenance
)
411 bool isCorrupt
= (SQLITE_CORRUPT
== rc
) || (SQLITE_NOTADB
== rc
) || (SQLITE_IOERR
== rc
);
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
));
417 _handle_corrupt_db(dbconn
);
418 authdb_maintenance(dbconn
);
420 } else if (SQLITE_CONSTRAINT
== rc
|| SQLITE_READONLY
== rc
) {
422 os_log_debug(AUTHD_LOG
, "authdb: %{public}s %{public}s for %{public}s", fn_name
, sqlite3_errmsg(dbconn
->handle
), sqlite3_sql(stmt
));
424 os_log_debug(AUTHD_LOG
, "authdb: %{public}s %{public}s", fn_name
, sqlite3_errmsg(dbconn
->handle
));
429 char * authdb_copy_sql_string(sqlite3_stmt
* sql
,int32_t col
)
431 char * result
= NULL
;
432 const char * sql_str
= (const char *)sqlite3_column_text(sql
, col
);
434 size_t len
= strlen(sql_str
) + 1;
435 result
= (char*)calloc(1u, len
);
436 check(result
!= NULL
);
438 strlcpy(result
, sql_str
, len
);
444 #pragma mark authdb_t
447 _authdb_finalize(CFTypeRef value
)
449 authdb_t db
= (authdb_t
)value
;
451 CFReleaseNull(db
->connections
);
452 dispatch_release(db
->queue
);
453 free_safe(db
->db_path
);
456 AUTH_TYPE_INSTANCE(authdb
,
459 .finalize
= _authdb_finalize
,
462 .copyFormattingDesc
= NULL
,
463 .copyDebugDesc
= NULL
466 static CFTypeID
authdb_get_type_id() {
467 static CFTypeID type_id
= _kCFRuntimeNotATypeID
;
468 static dispatch_once_t onceToken
;
470 dispatch_once(&onceToken
, ^{
471 type_id
= _CFRuntimeRegisterClass(&_auth_type_authdb
);
482 db
= (authdb_t
)_CFRuntimeCreateInstance(kCFAllocatorDefault
, authdb_get_type_id(), AUTH_CLASS_SIZE(authdb
), NULL
);
483 require(db
!= NULL
, done
);
485 db
->queue
= dispatch_queue_create(NULL
, DISPATCH_QUEUE_SERIAL
);
486 db
->connections
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, NULL
);
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");
492 db
->db_path
= _copy_string(AUTHDB
);
499 authdb_connection_t
authdb_connection_acquire(authdb_t db
)
501 __block authdb_connection_t dbconn
= NULL
;
503 static int32_t total
= 0;
505 dispatch_sync(db
->queue
, ^{
506 CFIndex count
= CFArrayGetCount(db
->connections
);
508 dbconn
= (authdb_connection_t
)CFArrayGetValueAtIndex(db
->connections
, 0);
509 CFArrayRemoveValueAtIndex(db
->connections
, 0);
511 dbconn
= authdb_connection_create(db
);
514 os_log_debug(AUTHD_LOG
, "authdb: no handles available total: %i", total
);
522 void authdb_connection_release(authdb_connection_t
* dbconn
)
527 authdb_connection_t tmp
= *dbconn
;
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
);
535 os_log_debug(AUTHD_LOG
, "authdb: freeing extra connection");
541 static bool _db_check_corrupted(authdb_connection_t dbconn
)
543 bool isCorrupted
= true;
544 sqlite3_stmt
*stmt
= NULL
;
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
);
555 } else if (rc
== SQLITE_OK
) {
556 rc
= sqlite3_step(stmt
);
558 if (rc
== SQLITE_LOCKED
|| rc
== SQLITE_BUSY
) {
559 os_log_debug(AUTHD_LOG
, "authdb: warning error %i when running integrity check", rc
);
561 } else if (rc
== SQLITE_ROW
) {
562 const char * result
= (const char*)sqlite3_column_text(stmt
, 0);
564 if (result
&& strncasecmp(result
, "ok", 3) == 0) {
570 sqlite3_finalize(stmt
);
574 bool authdb_maintenance(authdb_connection_t dbconn
)
576 os_log_debug(AUTHD_LOG
, "authdb: starting maintenance");
577 int32_t rc
= SQLITE_ERROR
;
578 auth_items_t config
= NULL
;
580 bool isCorrupted
= _db_check_corrupted(dbconn
);
581 os_log_debug(AUTHD_LOG
, "authdb: integrity check=%{public}s", isCorrupted
? "fail" : "pass");
584 _handle_corrupt_db(dbconn
);
587 if (dbconn
->handle
== NULL
) {
588 dbconn
->handle
= _create_handle(dbconn
->db
);
591 require_action(dbconn
->handle
, done
, os_log_error(AUTHD_LOG
, "authdb: maintenance cannot open database"));
593 _db_maintenance(dbconn
);
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
));
598 _db_load_data(dbconn
, config
);
600 _repair_all_kofns(dbconn
, config
);
602 CFReleaseSafe(config
);
603 os_log_debug(AUTHD_LOG
, "authdb: finished maintenance");
604 return rc
== SQLITE_OK
;
608 authdb_exec(authdb_connection_t dbconn
, const char * query
)
610 int32_t rc
= SQLITE_ERROR
;
611 require(query
!= NULL
, done
);
613 rc
= _sqlite3_exec(dbconn
->handle
, query
);
614 _checkResult(dbconn
, rc
, __FUNCTION__
, NULL
, false);
620 static int32_t _prepare(authdb_connection_t dbconn
, const char * sql
, const bool skip_maintenance
, sqlite3_stmt
** out_stmt
)
623 sqlite3_stmt
* stmt
= NULL
;
625 require_action(sql
!= NULL
, done
, rc
= SQLITE_ERROR
);
626 require_action(out_stmt
!= NULL
, done
, rc
= SQLITE_ERROR
);
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
)));
634 _checkResult(dbconn
, rc
, __FUNCTION__
, stmt
, skip_maintenance
);
638 static void _parseItemsAtIndex(sqlite3_stmt
* stmt
, int32_t col
, auth_items_t items
, const char * key
)
640 switch (sqlite3_column_type(stmt
, col
)) {
642 auth_items_set_double(items
, key
, sqlite3_column_double(stmt
, col
));
645 auth_items_set_int64(items
, key
, sqlite3_column_int64(stmt
, col
));
648 auth_items_set_data(items
,
650 sqlite3_column_blob(stmt
, col
),
651 (size_t)sqlite3_column_bytes(stmt
, col
));
657 auth_items_set_string(items
, key
, (const char *)sqlite3_column_text(stmt
, col
));
661 // LOGD("authdb: col=%s, val=%s, type=%i", sqlite3_column_name(stmt, col), sqlite3_column_text(stmt, col), sqlite3_column_type(stmt,col));
664 static int32_t _bindItemsAtIndex(sqlite3_stmt
* stmt
, int col
, auth_items_t items
, const char * key
)
667 switch (auth_items_get_type(items
, key
)) {
669 rc
= sqlite3_bind_int64(stmt
, col
, auth_items_get_int(items
, key
));
672 rc
= sqlite3_bind_int64(stmt
, col
, auth_items_get_uint(items
, key
));
675 rc
= sqlite3_bind_int64(stmt
, col
, auth_items_get_int64(items
, key
));
678 rc
= sqlite3_bind_int64(stmt
, col
, (int64_t)auth_items_get_uint64(items
, key
));
681 rc
= sqlite3_bind_double(stmt
, col
, auth_items_get_double(items
, key
));
684 rc
= sqlite3_bind_int64(stmt
, col
, auth_items_get_bool(items
, key
));
689 const void * blob
= auth_items_get_data(items
, key
, &blobLen
);
690 rc
= sqlite3_bind_blob(stmt
, col
, blob
, (int32_t)blobLen
, NULL
);
694 rc
= sqlite3_bind_text(stmt
, col
, auth_items_get_string(items
, key
), -1, NULL
);
697 rc
= sqlite3_bind_null(stmt
, col
);
700 if (rc
!= SQLITE_OK
) {
701 os_log_debug(AUTHD_LOG
, "authdb: auth_items bind failed (%i)", rc
);
706 int32_t authdb_get_key_value(authdb_connection_t dbconn
, const char * table
, const bool skip_maintenance
, auth_items_t
* out_items
)
708 int32_t rc
= SQLITE_ERROR
;
710 sqlite3_stmt
* stmt
= NULL
;
711 auth_items_t items
= NULL
;
713 require(table
!= NULL
, done
);
714 require(out_items
!= NULL
, done
);
716 asprintf(&query
, "SELECT * FROM %s", table
);
718 rc
= _prepare(dbconn
, query
, skip_maintenance
, &stmt
);
719 require_noerr(rc
, done
);
721 items
= auth_items_create();
722 while ((rc
= sqlite3_step(stmt
)) != SQLITE_DONE
) {
725 _parseItemsAtIndex(stmt
, 1, items
, (const char*)sqlite3_column_text(stmt
, 0));
728 _checkResult(dbconn
, rc
, __FUNCTION__
, stmt
, skip_maintenance
);
730 sleep(AUTHDB_BUSY_DELAY
);
732 require_noerr_action(rc
, done
, os_log_debug(AUTHD_LOG
, "authdb: get_key_value (%i) %{public}s", rc
, sqlite3_errmsg(dbconn
->handle
)));
743 CFReleaseSafe(items
);
745 sqlite3_finalize(stmt
);
749 int32_t authdb_set_key_value(authdb_connection_t dbconn
, const char * table
, auth_items_t items
)
751 __block
int32_t rc
= SQLITE_ERROR
;
753 sqlite3_stmt
* stmt
= NULL
;
755 require(table
!= NULL
, done
);
756 require(items
!= NULL
, done
);
758 asprintf(&query
, "INSERT OR REPLACE INTO %s VALUES (?,?)", table
);
760 rc
= _prepare(dbconn
, query
, false, &stmt
);
761 require_noerr(rc
, done
);
763 auth_items_iterate(items
, ^bool(const char *key
) {
765 _checkResult(dbconn
, rc
, __FUNCTION__
, stmt
, false);
767 sqlite3_bind_text(stmt
, 1, key
, -1, NULL
);
768 _bindItemsAtIndex(stmt
, 2, items
, key
);
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
));
781 sqlite3_finalize(stmt
);
785 static int32_t _begin_transaction_type(authdb_connection_t dbconn
, AuthDBTransactionType type
)
787 int32_t result
= SQLITE_ERROR
;
789 const char * query
= NULL
;
791 case AuthDBTransactionImmediate
:
792 query
= "BEGIN IMMEDIATE;";
794 case AuthDBTransactionExclusive
:
795 query
= "BEGIN EXCLUSIVE;";
797 case AuthDBTransactionNormal
:
806 if (query
!= NULL
&& sqlite3_get_autocommit(dbconn
->handle
) != 0) {
807 result
= _sqlite3_exec(dbconn
->handle
, query
);
813 static int32_t _end_transaction(authdb_connection_t dbconn
, bool commit
)
816 return _sqlite3_exec(dbconn
->handle
, "END;");
818 return _sqlite3_exec(dbconn
->handle
, "ROLLBACK;");
822 bool authdb_transaction(authdb_connection_t dbconn
, AuthDBTransactionType type
, bool (^t
)(void))
824 int32_t result
= SQLITE_ERROR
;
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
));
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
));
839 bool authdb_step(authdb_connection_t dbconn
, const char * sql
, void (^bind_stmt
)(sqlite3_stmt
*), authdb_iterator_t iter
)
842 sqlite3_stmt
* stmt
= NULL
;
843 int32_t rc
= SQLITE_ERROR
;
845 require_action(sql
!= NULL
, done
, rc
= SQLITE_ERROR
);
847 rc
= _prepare(dbconn
, sql
, false, &stmt
);
848 require_noerr(rc
, done
);
854 int32_t count
= sqlite3_column_count(stmt
);
856 auth_items_t items
= NULL
;
857 while ((rc
= sqlite3_step(stmt
)) != SQLITE_DONE
) {
862 items
= auth_items_create();
863 for (int i
= 0; i
< count
; i
++) {
864 _parseItemsAtIndex(stmt
, i
, items
, sqlite3_column_name(stmt
, i
));
866 result
= iter(items
);
867 CFReleaseNull(items
);
876 os_log_debug(AUTHD_LOG
, "authdb: %{public}s", sqlite3_errmsg(dbconn
->handle
));
877 sleep(AUTHDB_BUSY_DELAY
);
880 require_noerr_action(rc
, done
, os_log_debug(AUTHD_LOG
, "authdb: step (%i) %{public}s", rc
, sqlite3_errmsg(dbconn
->handle
)));
887 _checkResult(dbconn
, rc
, __FUNCTION__
, stmt
, false);
888 sqlite3_finalize(stmt
);
889 return rc
== SQLITE_DONE
;
892 void authdb_checkpoint(authdb_connection_t dbconn
)
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
);
900 static CFMutableArrayRef
901 _copy_rules_dict(RuleType type
, CFDictionaryRef plist
, authdb_connection_t dbconn
)
903 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
904 require(result
!= NULL
, done
);
906 _cf_dictionary_iterate(plist
, ^bool(CFTypeRef key
, CFTypeRef value
) {
907 if (CFGetTypeID(key
) != CFStringGetTypeID()) {
911 if (CFGetTypeID(value
) != CFDictionaryGetTypeID()) {
915 rule_t rule
= rule_create_with_plist(type
, key
, value
, dbconn
);
917 CFArrayAppendValue(result
, rule
);
929 _import_rules(authdb_connection_t dbconn
, CFMutableArrayRef rules
, bool version_check
, CFAbsoluteTime now
, const char *nameFilter
)
931 CFMutableArrayRef notcommited
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
932 CFIndex count
= CFArrayGetCount(rules
);
934 for (CFIndex i
= 0; i
< count
; i
++) {
935 rule_t rule
= (rule_t
)CFArrayGetValueAtIndex(rules
, i
);
937 if (nameFilter
&& (strcmp(nameFilter
, rule_get_name(rule
)) != 0)) {
938 // current rule name does not match the requested filter
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
)) {
949 CFReleaseSafe(current
);
957 __block
bool delayCommit
= false;
959 switch (rule_get_type(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
);
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
));
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");
985 CFArrayAppendValue(notcommited
, rule
);
988 CFArrayAppendValue(notcommited
, rule
);
991 CFArrayRemoveAllValues(rules
);
992 CFArrayAppendArray(rules
, notcommited
, CFRangeMake(0, CFArrayGetCount(notcommited
)));
993 CFReleaseSafe(notcommited
);
997 authdb_import_plist(authdb_connection_t dbconn
, CFDictionaryRef plist
, bool version_check
, const char *name
)
1001 os_log_debug(AUTHD_LOG
, "authdb: starting import");
1003 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
1004 CFMutableArrayRef rights
= NULL
;
1005 CFMutableArrayRef rules
= NULL
;
1006 require(plist
!= NULL
, done
);
1008 CFTypeRef rightsDict
= CFDictionaryGetValue(plist
, CFSTR("rights"));
1009 if (rightsDict
&& CFGetTypeID(rightsDict
) == CFDictionaryGetTypeID()) {
1010 rights
= _copy_rules_dict(RT_RIGHT
, rightsDict
, dbconn
);
1013 CFTypeRef rulesDict
= CFDictionaryGetValue(plist
, CFSTR("rules"));
1014 if (rulesDict
&& CFGetTypeID(rulesDict
) == CFDictionaryGetTypeID()) {
1015 rules
= _copy_rules_dict(RT_RULE
, rulesDict
, dbconn
);
1018 os_log_debug(AUTHD_LOG
, "authdb: rights = %li", CFArrayGetCount(rights
));
1019 os_log_debug(AUTHD_LOG
, "authdb: rules = %li", CFArrayGetCount(rules
));
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
);
1030 _import_rules(dbconn
, rules
, version_check
, now
, name
);
1033 _import_rules(dbconn
, rights
, version_check
, now
, name
);
1035 if (CFArrayGetCount(rights
) == 0) {
1039 authdb_checkpoint(dbconn
);
1042 CFReleaseSafe(rights
);
1043 CFReleaseSafe(rules
);
1045 os_log_debug(AUTHD_LOG
, "authdb: finished import, %{public}s", result
? "succeeded" : "failed");
1051 #pragma mark authdb_connection_t
1053 static bool _sql_profile_enabled(void)
1055 static bool profile_enabled
= false;
1058 static dispatch_once_t onceToken
;
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
);
1064 if (profile
&& CFGetTypeID(profile
) == CFBooleanGetTypeID()) {
1065 profile_enabled
= CFBooleanGetValue((CFBooleanRef
)profile
);
1068 os_log_debug(AUTHD_LOG
, "authdb: sql profile: %{public}s", profile_enabled
? "enabled" : "disabled");
1070 CFReleaseSafe(profile
);
1074 return profile_enabled
;
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);
1081 static sqlite3
* _create_handle(authdb_t db
)
1083 bool dbcreated
= false;
1084 sqlite3
* handle
= NULL
;
1085 int32_t rc
= sqlite3_open_v2(db
->db_path
, &handle
, SQLITE_OPEN_READWRITE
, NULL
);
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");
1090 sqlite3_close(handle
);
1092 char * tmp
= dirname(db
->db_path
);
1094 mkpath_np(tmp
, 0700);
1096 rc
= sqlite3_open_v2(db
->db_path
, &handle
, SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE
, NULL
);
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");
1102 sqlite3_close(handle
);
1109 if (_sql_profile_enabled()) {
1110 sqlite3_profile(handle
, _profile
, NULL
);
1113 _sqlite3_exec(handle
, "PRAGMA foreign_keys = ON");
1114 _sqlite3_exec(handle
, "PRAGMA temp_store = MEMORY");
1117 _sqlite3_exec(handle
, "PRAGMA auto_vacuum = FULL");
1118 _sqlite3_exec(handle
, "PRAGMA journal_mode = WAL");
1121 sqlite3_file_control(handle
, 0, SQLITE_FCNTL_PERSIST_WAL
, &on
);
1123 chmod(db
->db_path
, S_IRUSR
| S_IWUSR
);
1131 _authdb_connection_finalize(CFTypeRef value
)
1133 authdb_connection_t dbconn
= (authdb_connection_t
)value
;
1135 if (dbconn
->handle
) {
1136 sqlite3_close(dbconn
->handle
);
1138 CFReleaseNull(dbconn
->db
);
1141 AUTH_TYPE_INSTANCE(authdb_connection
,
1144 .finalize
= _authdb_connection_finalize
,
1147 .copyFormattingDesc
= NULL
,
1148 .copyDebugDesc
= NULL
1151 static CFTypeID
authdb_connection_get_type_id() {
1152 static CFTypeID type_id
= _kCFRuntimeNotATypeID
;
1153 static dispatch_once_t onceToken
;
1155 dispatch_once(&onceToken
, ^{
1156 type_id
= _CFRuntimeRegisterClass(&_auth_type_authdb_connection
);
1163 authdb_connection_create(authdb_t db
)
1165 authdb_connection_t dbconn
= NULL
;
1167 dbconn
= (authdb_connection_t
)_CFRuntimeCreateInstance(kCFAllocatorDefault
, authdb_connection_get_type_id(), AUTH_CLASS_SIZE(authdb_connection
), NULL
);
1168 require(dbconn
!= NULL
, done
);
1170 dbconn
->db
= (authdb_t
)CFRetain(db
);
1171 dbconn
->handle
= _create_handle(dbconn
->db
);