1 /* Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. */
11 #include <sqlite3_private.h>
12 #include <CoreFoundation/CoreFoundation.h>
14 #include "authutilities.h"
18 #define AUTHDB "/var/db/auth.db"
19 #define AUTHDB_DATA "/System/Library/Security/authorization.plist"
21 #define AUTH_STR(x) #x
22 #define AUTH_STRINGIFY(x) AUTH_STR(x)
24 #define AUTHDB_VERSION 1
25 #define AUTHDB_VERSION_STRING AUTH_STRINGIFY(AUTHDB_VERSION)
27 #define AUTHDB_BUSY_DELAY 1
28 #define AUTHDB_MAX_HANDLES 3
30 struct _authdb_connection_s
{
31 __AUTH_BASE_STRUCT_HEADER__
;
38 __AUTH_BASE_STRUCT_HEADER__
;
41 dispatch_queue_t queue
;
42 CFMutableArrayRef connections
;
45 static const char * const authdb_upgrade_sql
[] = {
48 "CREATE TABLE delegates_map ("
49 "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
50 "d_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
51 "ord INTEGER NOT NULL"
53 "CREATE INDEX d_map_d_id ON delegates_map(d_id);"
54 "CREATE INDEX d_map_r_id ON delegates_map(r_id);"
55 "CREATE INDEX d_map_r_id_ord ON delegates_map (r_id, ord);"
56 "CREATE TABLE mechanisms ("
57 "id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,"
58 "plugin TEXT NOT NULL,"
59 "param TEXT NOT NULL,"
60 "privileged INTEGER CHECK (privileged = 0 OR privileged = 1) NOT NULL DEFAULT (0)"
62 "CREATE UNIQUE INDEX mechanisms_lookup ON mechanisms (plugin,param,privileged);"
63 "CREATE TABLE mechanisms_map ("
64 "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
65 "m_id INTEGER NOT NULL REFERENCES mechanisms(id) ON DELETE CASCADE,"
66 "ord INTEGER NOT NULL"
68 "CREATE INDEX m_map_m_id ON mechanisms_map (m_id);"
69 "CREATE INDEX m_map_r_id ON mechanisms_map (r_id);"
70 "CREATE INDEX m_map_r_id_ord ON mechanisms_map (r_id, ord);"
71 "CREATE TABLE rules ("
72 "id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,"
73 "name TEXT NOT NULL UNIQUE,"
74 "type INTEGER CHECK (type = 1 OR type = 2) NOT NULL,"
75 "class INTEGER CHECK (class > 0),"
81 "version INTEGER NOT NULL DEFAULT (0),"
82 "created REAL NOT NULL DEFAULT (0),"
83 "modified REAL NOT NULL DEFAULT (0),"
89 "CREATE INDEX a_type ON rules (type);"
90 "CREATE TABLE config ("
91 "'key' TEXT PRIMARY KEY NOT NULL UNIQUE,"
94 "CREATE TABLE prompts ("
95 "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
99 "CREATE INDEX p_r_id ON prompts(r_id);"
100 "CREATE TABLE buttons ("
101 "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
102 "lang TEXT NOT NULL,"
103 "value TEXT NOT NULL"
105 "CREATE INDEX b_r_id ON buttons(r_id);"
106 "INSERT INTO config VALUES('version', "AUTHDB_VERSION_STRING
");"
110 _sqlite3_exec(sqlite3
* handle
, const char * query
)
112 int32_t rc
= SQLITE_ERROR
;
113 require(query
!= NULL
, done
);
115 char * errmsg
= NULL
;
116 rc
= sqlite3_exec(handle
, query
, NULL
, NULL
, &errmsg
);
118 LOGE("authdb: exec, (%i) %s", rc
, errmsg
);
119 sqlite3_free(errmsg
);
126 struct _db_upgrade_stages
{
132 static struct _db_upgrade_stages auth_upgrade_script
[] = {
133 { .pre
= -1, .main
= 0, .post
= -1 } // Create version AUTHDB_VERSION databse.
136 static int32_t _db_run_script(authdb_connection_t dbconn
, int number
)
140 /* Script -1 == skip this step. */
144 /* If we are attempting to run a script we don't have, fail. */
145 if ((size_t)number
>= sizeof(authdb_upgrade_sql
) / sizeof(char*))
146 return SQLITE_CORRUPT
;
148 s3e
= _sqlite3_exec(dbconn
->handle
, authdb_upgrade_sql
[number
]);
153 static int32_t _db_upgrade_from_version(authdb_connection_t dbconn
, int32_t version
)
157 /* If we are attempting to upgrade to a version greater than what we have
158 an upgrade script for, fail. */
160 (size_t)version
>= sizeof(auth_upgrade_script
) / sizeof(struct _db_upgrade_stages
))
161 return SQLITE_CORRUPT
;
163 struct _db_upgrade_stages
*script
= &auth_upgrade_script
[version
];
164 s3e
= _db_run_script(dbconn
, script
->pre
);
165 if (s3e
== SQLITE_OK
)
166 s3e
= _db_run_script(dbconn
, script
->main
);
167 if (s3e
== SQLITE_OK
)
168 s3e
= _db_run_script(dbconn
, script
->post
);
173 static void _printCFError(const char * errmsg
, CFErrorRef err
)
175 CFStringRef errString
= NULL
;
176 errString
= CFErrorCopyDescription(err
);
177 char * tmp
= _copy_cf_string(errString
, NULL
);
178 LOGV("%s, %s", errmsg
, tmp
);
180 CFReleaseSafe(errString
);
183 static void _db_load_data(authdb_connection_t dbconn
, auth_items_t config
)
185 CFURLRef authURL
= NULL
;
186 CFPropertyListRef plist
= NULL
;
187 CFDataRef data
= NULL
;
189 CFErrorRef err
= NULL
;
190 CFTypeRef value
= NULL
;
191 CFAbsoluteTime ts
= 0;
192 CFAbsoluteTime old_ts
= 0;
194 authURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, CFSTR(AUTHDB_DATA
), kCFURLPOSIXPathStyle
, false);
195 require_action(authURL
!= NULL
, done
, LOGE("authdb: file not found %s", AUTHDB_DATA
));
197 CFURLCopyResourcePropertyForKey(authURL
, kCFURLContentModificationDateKey
, &value
, &err
);
198 require_action(err
== NULL
, done
, _printCFError("authdb: failed to get modification date", err
));
200 if (CFGetTypeID(value
) == CFDateGetTypeID()) {
201 ts
= CFDateGetAbsoluteTime(value
);
204 old_ts
= auth_items_get_double(config
, "data_ts");
206 // <rdar://problem/17484375> SEED: BUG: Fast User Switching Not Working
207 // After Mavericks => Yosemite upgrade install, the new Yosemite rule "system.login.fus" was missing.
208 // Somehow (probably during install) ts < old_ts, even though that should never happen.
209 // Solution: always import plist and update db when time stamps don't match.
210 // After a successful import, old_ts = ts below.
212 LOGV("authdb: %s modified old=%f, new=%f", AUTHDB_DATA
, old_ts
, ts
);
213 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault
, authURL
, &data
, NULL
, NULL
, (SInt32
*)&rc
);
214 require_noerr_action(rc
, done
, LOGE("authdb: failed to load %s", AUTHDB_DATA
));
216 plist
= CFPropertyListCreateWithData(kCFAllocatorDefault
, data
, kCFPropertyListImmutable
, NULL
, &err
);
217 require_action(err
== NULL
, done
, _printCFError("authdb: failed to read plist", err
));
219 if (authdb_import_plist(dbconn
, plist
, true)) {
220 LOGD("authdb: updating data_ts");
221 auth_items_t update
= auth_items_create();
222 auth_items_set_double(update
, "data_ts", ts
);
223 authdb_set_key_value(dbconn
, "config", update
);
224 CFReleaseSafe(update
);
229 CFReleaseSafe(value
);
230 CFReleaseSafe(authURL
);
231 CFReleaseSafe(plist
);
236 static bool _truncate_db(authdb_connection_t dbconn
)
238 int32_t rc
= SQLITE_ERROR
;
239 int32_t flags
= SQLITE_TRUNCATE_JOURNALMODE_WAL
| SQLITE_TRUNCATE_AUTOVACUUM_FULL
;
240 rc
= sqlite3_file_control(dbconn
->handle
, NULL
, SQLITE_TRUNCATE_DATABASE
, &flags
);
241 if (rc
!= SQLITE_OK
) {
242 LOGV("Failed to delete db handle! SQLite error %i.\n", rc
);
243 if (rc
== SQLITE_IOERR
) {
244 // Unable to recover successfully if we can't truncate
249 return rc
== SQLITE_OK
;
252 static void _handle_corrupt_db(authdb_connection_t dbconn
)
254 int32_t rc
= SQLITE_ERROR
;
255 char buf
[PATH_MAX
+1];
256 sqlite3
*corrupt_db
= NULL
;
258 snprintf(buf
, sizeof(buf
), "%s-corrupt", dbconn
->db
->db_path
);
259 if (sqlite3_open(buf
, &corrupt_db
) == SQLITE_OK
) {
262 sqlite3_file_control(corrupt_db
, 0, SQLITE_FCNTL_PERSIST_WAL
, &on
);
264 rc
= sqlite3_file_control(corrupt_db
, NULL
, SQLITE_REPLACE_DATABASE
, (void *)dbconn
->handle
);
265 if (SQLITE_OK
== rc
) {
266 LOGE("Database at path %s is corrupt. Copying it to %s for further investigation.", dbconn
->db
->db_path
, buf
);
268 LOGE("Tried to copy corrupt database at path %s, but we failed with SQLite error %i.", dbconn
->db
->db_path
, rc
);
270 sqlite3_close(corrupt_db
);
273 _truncate_db(dbconn
);
276 static int32_t _db_maintenance(authdb_connection_t dbconn
)
278 __block
int32_t s3e
= SQLITE_OK
;
279 __block auth_items_t config
= NULL
;
281 authdb_transaction(dbconn
, AuthDBTransactionNormal
, ^bool(void) {
283 authdb_get_key_value(dbconn
, "config", &config
);
285 // We don't have a config table
286 if (NULL
== config
) {
287 LOGV("authdb: initializing database");
288 s3e
= _db_upgrade_from_version(dbconn
, 0);
289 require_noerr_action(s3e
, done
, LOGE("authdb: failed to initialize database %i", s3e
));
291 s3e
= authdb_get_key_value(dbconn
, "config", &config
);
292 require_noerr_action(s3e
, done
, LOGE("authdb: failed to get config %i", s3e
));
295 int64_t currentVersion
= auth_items_get_int64(config
, "version");
296 LOGV("authdb: current db ver=%lli", currentVersion
);
297 if (currentVersion
< AUTHDB_VERSION
) {
298 LOGV("authdb: upgrading schema");
299 s3e
= _db_upgrade_from_version(dbconn
, (int32_t)currentVersion
);
301 auth_items_set_int64(config
, "version", AUTHDB_VERSION
);
302 authdb_set_key_value(dbconn
, "config", config
);
309 CFReleaseSafe(config
);
313 //static void unlock_notify_cb(void **apArg, int nArg AUTH_UNUSED){
314 // dispatch_semaphore_t semaphore = (dispatch_semaphore_t)apArg[0];
315 // dispatch_semaphore_signal(semaphore);
318 //static int32_t _wait_for_unlock_notify(authdb_connection_t dbconn, sqlite3_stmt * stmt)
321 // dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
323 // rc = sqlite3_unlock_notify(dbconn->handle, unlock_notify_cb, semaphore);
324 // require(!rc, done);
326 // if (dispatch_semaphore_wait(semaphore, 5*NSEC_PER_SEC) != 0) {
327 // LOGV("authdb: timeout occurred!");
328 // sqlite3_unlock_notify(dbconn->handle, NULL, NULL);
329 // rc = SQLITE_LOCKED;
331 // sqlite3_reset(stmt);
335 // dispatch_release(semaphore);
339 static bool _is_busy(int32_t rc
)
341 return SQLITE_BUSY
== rc
|| SQLITE_LOCKED
== rc
;
344 static void _checkResult(authdb_connection_t dbconn
, int32_t rc
, const char * fn_name
, sqlite3_stmt
* stmt
)
346 bool isCorrupt
= (SQLITE_CORRUPT
== rc
) || (SQLITE_NOTADB
== rc
) || (SQLITE_IOERR
== rc
);
349 _handle_corrupt_db(dbconn
);
350 authdb_maintenance(dbconn
);
351 } else if (SQLITE_CONSTRAINT
== rc
|| SQLITE_READONLY
== rc
) {
353 LOGV("authdb: %s %s for %s", fn_name
, sqlite3_errmsg(dbconn
->handle
), sqlite3_sql(stmt
));
355 LOGV("authdb: %s %s", fn_name
, sqlite3_errmsg(dbconn
->handle
));
360 char * authdb_copy_sql_string(sqlite3_stmt
* sql
,int32_t col
)
362 char * result
= NULL
;
363 const char * sql_str
= (const char *)sqlite3_column_text(sql
, col
);
365 size_t len
= strlen(sql_str
) + 1;
366 result
= (char*)calloc(1u, len
);
367 check(result
!= NULL
);
369 strlcpy(result
, sql_str
, len
);
375 #pragma mark authdb_t
378 _authdb_finalize(CFTypeRef value
)
380 authdb_t db
= (authdb_t
)value
;
382 CFReleaseSafe(db
->connections
);
383 dispatch_release(db
->queue
);
384 free_safe(db
->db_path
);
387 AUTH_TYPE_INSTANCE(authdb
,
390 .finalize
= _authdb_finalize
,
393 .copyFormattingDesc
= NULL
,
394 .copyDebugDesc
= NULL
397 static CFTypeID
authdb_get_type_id() {
398 static CFTypeID type_id
= _kCFRuntimeNotATypeID
;
399 static dispatch_once_t onceToken
;
401 dispatch_once(&onceToken
, ^{
402 type_id
= _CFRuntimeRegisterClass(&_auth_type_authdb
);
413 db
= (authdb_t
)_CFRuntimeCreateInstance(kCFAllocatorDefault
, authdb_get_type_id(), AUTH_CLASS_SIZE(authdb
), NULL
);
414 require(db
!= NULL
, done
);
416 db
->queue
= dispatch_queue_create(NULL
, DISPATCH_QUEUE_SERIAL
);
417 db
->connections
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, NULL
);
419 if (getenv("__OSINSTALL_ENVIRONMENT") != NULL
) {
420 LOGV("authdb: running from installer");
421 db
->db_path
= _copy_string("file::memory:?cache=shared");
423 db
->db_path
= _copy_string(AUTHDB
);
430 authdb_connection_t
authdb_connection_acquire(authdb_t db
)
432 __block authdb_connection_t dbconn
= NULL
;
434 static int32_t total
= 0;
436 dispatch_sync(db
->queue
, ^{
437 CFIndex count
= CFArrayGetCount(db
->connections
);
439 dbconn
= (authdb_connection_t
)CFArrayGetValueAtIndex(db
->connections
, 0);
440 CFArrayRemoveValueAtIndex(db
->connections
, 0);
442 dbconn
= authdb_connection_create(db
);
445 LOGV("authdb: no handles available total: %i", total
);
453 void authdb_connection_release(authdb_connection_t
* dbconn
)
455 if (!dbconn
|| !(*dbconn
))
458 authdb_connection_t tmp
= *dbconn
;
461 dispatch_async(tmp
->db
->queue
, ^{
462 CFIndex count
= CFArrayGetCount(tmp
->db
->connections
);
463 if (count
<= AUTHDB_MAX_HANDLES
) {
464 CFArrayAppendValue(tmp
->db
->connections
, tmp
);
466 LOGD("authdb: freeing extra connection");
472 static bool _db_check_corrupted(authdb_connection_t dbconn
)
474 bool isCorrupted
= true;
475 sqlite3_stmt
*stmt
= NULL
;
478 rc
= sqlite3_prepare_v2(dbconn
->handle
, "PRAGMA integrity_check;", -1, &stmt
, NULL
);
479 if (rc
== SQLITE_LOCKED
|| rc
== SQLITE_BUSY
) {
480 LOGV("authdb: warning error %i when running integrity check", rc
);
483 } else if (rc
== SQLITE_OK
) {
484 rc
= sqlite3_step(stmt
);
486 if (rc
== SQLITE_LOCKED
|| rc
== SQLITE_BUSY
) {
487 LOGV("authdb: warning error %i when running integrity check", rc
);
489 } else if (rc
== SQLITE_ROW
) {
490 const char * result
= (const char*)sqlite3_column_text(stmt
, 0);
492 if (result
&& strncasecmp(result
, "ok", 3) == 0) {
498 sqlite3_finalize(stmt
);
502 bool authdb_maintenance(authdb_connection_t dbconn
)
504 LOGD("authdb: starting maintenance");
505 int32_t rc
= SQLITE_ERROR
;
506 auth_items_t config
= NULL
;
508 bool isCorrupted
= _db_check_corrupted(dbconn
);
509 LOGD("authdb: integrity check=%s", isCorrupted
? "fail" : "pass");
512 _handle_corrupt_db(dbconn
);
515 _db_maintenance(dbconn
);
517 rc
= authdb_get_key_value(dbconn
, "config", &config
);
518 require_noerr_action(rc
, done
, LOGV("authdb: maintenance failed %i", rc
));
520 _db_load_data(dbconn
, config
);
523 CFReleaseSafe(config
);
524 LOGD("authdb: finished maintenance");
525 return rc
== SQLITE_OK
;
529 authdb_exec(authdb_connection_t dbconn
, const char * query
)
531 int32_t rc
= SQLITE_ERROR
;
532 require(query
!= NULL
, done
);
534 rc
= _sqlite3_exec(dbconn
->handle
, query
);
535 _checkResult(dbconn
, rc
, __FUNCTION__
, NULL
);
541 static int32_t _prepare(authdb_connection_t dbconn
, const char * sql
, sqlite3_stmt
** out_stmt
)
544 sqlite3_stmt
* stmt
= NULL
;
546 require_action(sql
!= NULL
, done
, rc
= SQLITE_ERROR
);
547 require_action(out_stmt
!= NULL
, done
, rc
= SQLITE_ERROR
);
549 rc
= sqlite3_prepare_v2(dbconn
->handle
, sql
, -1, &stmt
, NULL
);
550 require_noerr_action(rc
, done
, LOGV("authdb: prepare (%i) %s", rc
, sqlite3_errmsg(dbconn
->handle
)));
555 _checkResult(dbconn
, rc
, __FUNCTION__
, stmt
);
559 static void _parseItemsAtIndex(sqlite3_stmt
* stmt
, int32_t col
, auth_items_t items
, const char * key
)
561 switch (sqlite3_column_type(stmt
, col
)) {
563 auth_items_set_double(items
, key
, sqlite3_column_double(stmt
, col
));
566 auth_items_set_int64(items
, key
, sqlite3_column_int64(stmt
, col
));
569 auth_items_set_data(items
,
571 sqlite3_column_blob(stmt
, col
),
572 (size_t)sqlite3_column_bytes(stmt
, col
));
578 auth_items_set_string(items
, key
, (const char *)sqlite3_column_text(stmt
, col
));
582 // LOGD("authdb: col=%s, val=%s, type=%i", sqlite3_column_name(stmt, col), sqlite3_column_text(stmt, col), sqlite3_column_type(stmt,col));
585 static int32_t _bindItemsAtIndex(sqlite3_stmt
* stmt
, int col
, auth_items_t items
, const char * key
)
588 switch (auth_items_get_type(items
, key
)) {
590 rc
= sqlite3_bind_int64(stmt
, col
, auth_items_get_int(items
, key
));
593 rc
= sqlite3_bind_int64(stmt
, col
, auth_items_get_uint(items
, key
));
596 rc
= sqlite3_bind_int64(stmt
, col
, auth_items_get_int64(items
, key
));
599 rc
= sqlite3_bind_int64(stmt
, col
, (int64_t)auth_items_get_uint64(items
, key
));
602 rc
= sqlite3_bind_double(stmt
, col
, auth_items_get_double(items
, key
));
605 rc
= sqlite3_bind_int64(stmt
, col
, auth_items_get_bool(items
, key
));
610 const void * blob
= auth_items_get_data(items
, key
, &blobLen
);
611 rc
= sqlite3_bind_blob(stmt
, col
, blob
, (int32_t)blobLen
, NULL
);
615 rc
= sqlite3_bind_text(stmt
, col
, auth_items_get_string(items
, key
), -1, NULL
);
618 rc
= sqlite3_bind_null(stmt
, col
);
621 if (rc
!= SQLITE_OK
) {
622 LOGV("authdb: auth_items bind failed (%i)", rc
);
627 int32_t authdb_get_key_value(authdb_connection_t dbconn
, const char * table
, auth_items_t
* out_items
)
629 int32_t rc
= SQLITE_ERROR
;
631 sqlite3_stmt
* stmt
= NULL
;
632 auth_items_t items
= NULL
;
634 require(table
!= NULL
, done
);
635 require(out_items
!= NULL
, done
);
637 asprintf(&query
, "SELECT * FROM %s", table
);
639 rc
= _prepare(dbconn
, query
, &stmt
);
640 require_noerr(rc
, done
);
642 items
= auth_items_create();
643 while ((rc
= sqlite3_step(stmt
)) != SQLITE_DONE
) {
646 _parseItemsAtIndex(stmt
, 1, items
, (const char*)sqlite3_column_text(stmt
, 0));
649 _checkResult(dbconn
, rc
, __FUNCTION__
, stmt
);
651 sleep(AUTHDB_BUSY_DELAY
);
653 require_noerr_action(rc
, done
, LOGV("authdb: get_key_value (%i) %s", rc
, sqlite3_errmsg(dbconn
->handle
)));
664 CFReleaseSafe(items
);
666 sqlite3_finalize(stmt
);
670 int32_t authdb_set_key_value(authdb_connection_t dbconn
, const char * table
, auth_items_t items
)
672 __block
int32_t rc
= SQLITE_ERROR
;
674 sqlite3_stmt
* stmt
= NULL
;
676 require(table
!= NULL
, done
);
677 require(items
!= NULL
, done
);
679 asprintf(&query
, "INSERT OR REPLACE INTO %s VALUES (?,?)", table
);
681 rc
= _prepare(dbconn
, query
, &stmt
);
682 require_noerr(rc
, done
);
684 auth_items_iterate(items
, ^bool(const char *key
) {
686 _checkResult(dbconn
, rc
, __FUNCTION__
, stmt
);
688 sqlite3_bind_text(stmt
, 1, key
, -1, NULL
);
689 _bindItemsAtIndex(stmt
, 2, items
, key
);
691 rc
= sqlite3_step(stmt
);
692 if (rc
!= SQLITE_DONE
) {
693 _checkResult(dbconn
, rc
, __FUNCTION__
, stmt
);
694 LOGV("authdb: set_key_value, step (%i) %s", rc
, sqlite3_errmsg(dbconn
->handle
));
702 sqlite3_finalize(stmt
);
706 static int32_t _begin_transaction_type(authdb_connection_t dbconn
, AuthDBTransactionType type
)
708 int32_t result
= SQLITE_ERROR
;
710 const char * query
= NULL
;
712 case AuthDBTransactionImmediate
:
713 query
= "BEGIN IMMEDATE;";
715 case AuthDBTransactionExclusive
:
716 query
= "BEGIN EXCLUSIVE;";
718 case AuthDBTransactionNormal
:
727 if (query
!= NULL
&& sqlite3_get_autocommit(dbconn
->handle
) != 0) {
728 result
= _sqlite3_exec(dbconn
->handle
, query
);
734 static int32_t _end_transaction(authdb_connection_t dbconn
, bool commit
)
737 return _sqlite3_exec(dbconn
->handle
, "END;");
739 return _sqlite3_exec(dbconn
->handle
, "ROLLBACK;");
743 bool authdb_transaction(authdb_connection_t dbconn
, AuthDBTransactionType type
, bool (^t
)(void))
745 int32_t result
= SQLITE_ERROR
;
748 result
= _begin_transaction_type(dbconn
, type
);
749 require_action(result
== SQLITE_OK
, done
, LOGV("authdb: transaction begin failed %i", result
));
753 result
= _end_transaction(dbconn
, commit
);
754 require_action(result
== SQLITE_OK
, done
, commit
= false; LOGV("authdb: transaction end failed %i", result
));
760 bool authdb_step(authdb_connection_t dbconn
, const char * sql
, void (^bind_stmt
)(sqlite3_stmt
*), authdb_iterator_t iter
)
763 sqlite3_stmt
* stmt
= NULL
;
764 int32_t rc
= SQLITE_ERROR
;
766 require_action(sql
!= NULL
, done
, rc
= SQLITE_ERROR
);
768 rc
= _prepare(dbconn
, sql
, &stmt
);
769 require_noerr(rc
, done
);
775 int32_t count
= sqlite3_column_count(stmt
);
777 auth_items_t items
= NULL
;
778 while ((rc
= sqlite3_step(stmt
)) != SQLITE_DONE
) {
783 items
= auth_items_create();
784 for (int i
= 0; i
< count
; i
++) {
785 _parseItemsAtIndex(stmt
, i
, items
, sqlite3_column_name(stmt
, i
));
787 result
= iter(items
);
788 CFReleaseNull(items
);
797 LOGV("authdb: %s", sqlite3_errmsg(dbconn
->handle
));
798 sleep(AUTHDB_BUSY_DELAY
);
801 require_noerr_action(rc
, done
, LOGV("authdb: step (%i) %s", rc
, sqlite3_errmsg(dbconn
->handle
)));
808 _checkResult(dbconn
, rc
, __FUNCTION__
, stmt
);
809 sqlite3_finalize(stmt
);
810 return rc
== SQLITE_DONE
;
813 void authdb_checkpoint(authdb_connection_t dbconn
)
815 int32_t rc
= sqlite3_wal_checkpoint(dbconn
->handle
, NULL
);
816 if (rc
!= SQLITE_OK
) {
817 LOGV("authdb: checkpoit failed %i", rc
);
821 static CFMutableArrayRef
822 _copy_rules_dict(RuleType type
, CFDictionaryRef plist
, authdb_connection_t dbconn
)
824 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
825 require(result
!= NULL
, done
);
827 _cf_dictionary_iterate(plist
, ^bool(CFTypeRef key
, CFTypeRef value
) {
828 if (CFGetTypeID(key
) != CFStringGetTypeID()) {
832 if (CFGetTypeID(value
) != CFDictionaryGetTypeID()) {
836 rule_t rule
= rule_create_with_plist(type
, key
, value
, dbconn
);
838 CFArrayAppendValue(result
, rule
);
850 _import_rules(authdb_connection_t dbconn
, CFMutableArrayRef rules
, bool version_check
, CFAbsoluteTime now
)
852 CFMutableArrayRef notcommited
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
853 CFIndex count
= CFArrayGetCount(rules
);
855 for (CFIndex i
= 0; i
< count
; i
++) {
856 rule_t rule
= (rule_t
)CFArrayGetValueAtIndex(rules
, i
);
860 if (rule_get_id(rule
) != 0) { // rule already exists see if we need to update
861 rule_t current
= rule_create_with_string(rule_get_name(rule
), dbconn
);
862 if (rule_get_version(rule
) > rule_get_version(current
)) {
865 CFReleaseSafe(current
);
873 __block
bool delayCommit
= false;
875 switch (rule_get_type(rule
)) {
877 rule_delegates_iterator(rule
, ^bool(rule_t delegate
) {
878 if (rule_get_id(delegate
) == 0) {
879 // fetch the rule from the database if it was previously committed
880 rule_sql_fetch(delegate
, dbconn
);
882 if (rule_get_id(delegate
) == 0) {
883 LOGD("authdb: delaying %s waiting for delegate %s", rule_get_name(rule
), rule_get_name(delegate
));
895 bool success
= rule_sql_commit(rule
, dbconn
, now
, NULL
);
896 LOGV("authdb: %s %s %s %s",
897 update
? "updating" : "importing",
898 rule_get_type(rule
) == RT_RULE
? "rule" : "right",
899 rule_get_name(rule
), success
? "success" : "FAIL");
901 CFArrayAppendValue(notcommited
, rule
);
904 CFArrayAppendValue(notcommited
, rule
);
907 CFArrayRemoveAllValues(rules
);
908 CFArrayAppendArray(rules
, notcommited
, CFRangeMake(0, CFArrayGetCount(notcommited
)));
909 CFReleaseSafe(notcommited
);
913 authdb_import_plist(authdb_connection_t dbconn
, CFDictionaryRef plist
, bool version_check
)
917 LOGV("authdb: starting import");
919 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
920 CFMutableArrayRef rights
= NULL
;
921 CFMutableArrayRef rules
= NULL
;
922 require(plist
!= NULL
, done
);
924 CFTypeRef rightsDict
= CFDictionaryGetValue(plist
, CFSTR("rights"));
925 if (rightsDict
&& CFGetTypeID(rightsDict
) == CFDictionaryGetTypeID()) {
926 rights
= _copy_rules_dict(RT_RIGHT
, rightsDict
, dbconn
);
929 CFTypeRef rulesDict
= CFDictionaryGetValue(plist
, CFSTR("rules"));
930 if (rulesDict
&& CFGetTypeID(rulesDict
) == CFDictionaryGetTypeID()) {
931 rules
= _copy_rules_dict(RT_RULE
, rulesDict
, dbconn
);
934 LOGV("authdb: rights = %li", CFArrayGetCount(rights
));
935 LOGV("authdb: rules = %li", CFArrayGetCount(rules
));
938 // first pass import base rules without delegations
939 // remaining import rules that delegate to other rules
940 // loop upto 3 times to commit dependent rules first
941 for (int32_t j
= 0; j
< 3; j
++) {
942 count
= CFArrayGetCount(rules
);
946 _import_rules(dbconn
, rules
, version_check
, now
);
949 _import_rules(dbconn
, rights
, version_check
, now
);
951 if (CFArrayGetCount(rights
) == 0) {
955 authdb_checkpoint(dbconn
);
958 CFReleaseSafe(rights
);
959 CFReleaseSafe(rules
);
961 LOGV("authdb: finished import, %s", result
? "succeeded" : "failed");
967 #pragma mark authdb_connection_t
969 static bool _sql_profile_enabled(void)
971 static bool profile_enabled
= false;
974 static dispatch_once_t onceToken
;
976 //sudo defaults write /Library/Preferences/com.apple.security.auth profile -bool true
977 dispatch_once(&onceToken
, ^{
978 CFTypeRef profile
= (CFNumberRef
)CFPreferencesCopyValue(CFSTR("profile"), CFSTR(SECURITY_AUTH_NAME
), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
980 if (profile
&& CFGetTypeID(profile
) == CFBooleanGetTypeID()) {
981 profile_enabled
= CFBooleanGetValue((CFBooleanRef
)profile
);
984 LOGV("authdb: sql profile: %s", profile_enabled
? "enabled" : "disabled");
986 CFReleaseSafe(profile
);
990 return profile_enabled
;
993 static void _profile(void *context AUTH_UNUSED
, const char *sql
, sqlite3_uint64 ns
) {
994 LOGV("==\nauthdb: %s\nTime: %llu ms\n", sql
, ns
>> 20);
997 static sqlite3
* _create_handle(authdb_t db
)
999 bool dbcreated
= false;
1000 sqlite3
* handle
= NULL
;
1001 int32_t rc
= sqlite3_open_v2(db
->db_path
, &handle
, SQLITE_OPEN_READWRITE
, NULL
);
1003 if (rc
!= SQLITE_OK
) {
1004 char * tmp
= dirname(db
->db_path
);
1006 mkpath_np(tmp
, 0700);
1008 rc
= sqlite3_open_v2(db
->db_path
, &handle
, SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE
, NULL
);
1011 require_noerr_action(rc
, done
, LOGE("authdb: open %s (%i) %s", db
->db_path
, rc
, sqlite3_errmsg(handle
)));
1013 if (_sql_profile_enabled()) {
1014 sqlite3_profile(handle
, _profile
, NULL
);
1017 _sqlite3_exec(handle
, "PRAGMA foreign_keys = ON");
1018 _sqlite3_exec(handle
, "PRAGMA temp_store = MEMORY");
1021 _sqlite3_exec(handle
, "PRAGMA auto_vacuum = FULL");
1022 _sqlite3_exec(handle
, "PRAGMA journal_mode = WAL");
1025 sqlite3_file_control(handle
, 0, SQLITE_FCNTL_PERSIST_WAL
, &on
);
1027 chmod(db
->db_path
, S_IRUSR
| S_IWUSR
);
1035 _authdb_connection_finalize(CFTypeRef value
)
1037 authdb_connection_t dbconn
= (authdb_connection_t
)value
;
1039 if (dbconn
->handle
) {
1040 sqlite3_close(dbconn
->handle
);
1042 CFReleaseSafe(dbconn
->db
);
1045 AUTH_TYPE_INSTANCE(authdb_connection
,
1048 .finalize
= _authdb_connection_finalize
,
1051 .copyFormattingDesc
= NULL
,
1052 .copyDebugDesc
= NULL
1055 static CFTypeID
authdb_connection_get_type_id() {
1056 static CFTypeID type_id
= _kCFRuntimeNotATypeID
;
1057 static dispatch_once_t onceToken
;
1059 dispatch_once(&onceToken
, ^{
1060 type_id
= _CFRuntimeRegisterClass(&_auth_type_authdb_connection
);
1067 authdb_connection_create(authdb_t db
)
1069 authdb_connection_t dbconn
= NULL
;
1071 dbconn
= (authdb_connection_t
)_CFRuntimeCreateInstance(kCFAllocatorDefault
, authdb_connection_get_type_id(), AUTH_CLASS_SIZE(authdb_connection
), NULL
);
1072 require(dbconn
!= NULL
, done
);
1074 dbconn
->db
= (authdb_t
)CFRetain(db
);
1075 dbconn
->handle
= _create_handle(dbconn
->db
);