]> git.saurik.com Git - apple/security.git/blobdiff - authd/authdb.c
Security-57031.1.35.tar.gz
[apple/security.git] / authd / authdb.c
diff --git a/authd/authdb.c b/authd/authdb.c
deleted file mode 100644 (file)
index d6952d0..0000000
+++ /dev/null
@@ -1,1073 +0,0 @@
-/* Copyright (c) 2012 Apple Inc. All rights reserved. */
-
-#include "authdb.h"
-#include "mechanism.h"
-#include "rule.h"
-#include "debugging.h"
-#include "authitems.h"
-#include "server.h"
-
-#include <sqlite3.h>
-#include <sqlite3_private.h>
-#include <CoreFoundation/CoreFoundation.h>
-#include "rule.h"
-#include "authutilities.h"
-#include <libgen.h>
-#include <sys/stat.h>
-
-#define AUTHDB "/var/db/auth.db"
-#define AUTHDB_DATA "/System/Library/Security/authorization.plist"
-
-#define AUTH_STR(x) #x
-#define AUTH_STRINGIFY(x) AUTH_STR(x)
-
-#define AUTHDB_VERSION 1
-#define AUTHDB_VERSION_STRING AUTH_STRINGIFY(AUTHDB_VERSION)
-
-#define AUTHDB_BUSY_DELAY 1
-#define AUTHDB_MAX_HANDLES 3
-
-struct _authdb_connection_s {
-    __AUTH_BASE_STRUCT_HEADER__;
-    
-    authdb_t db;
-    sqlite3 * handle;
-};
-
-struct _authdb_s {
-    __AUTH_BASE_STRUCT_HEADER__;
-    
-    char * db_path;
-    dispatch_queue_t queue;
-    CFMutableArrayRef connections;
-};
-
-static const char * const authdb_upgrade_sql[] = {
-    /* 0 */
-    /* current scheme */
-    "CREATE TABLE delegates_map ("
-        "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
-        "d_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
-        "ord INTEGER NOT NULL"
-        ");"
-    "CREATE INDEX d_map_d_id ON delegates_map(d_id);"
-    "CREATE INDEX d_map_r_id ON delegates_map(r_id);"
-    "CREATE INDEX d_map_r_id_ord ON delegates_map (r_id, ord);"
-    "CREATE TABLE mechanisms ("
-        "id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,"
-        "plugin TEXT NOT NULL,"
-        "param TEXT NOT NULL,"
-        "privileged INTEGER CHECK (privileged = 0 OR privileged = 1) NOT NULL DEFAULT (0)"
-        ");"
-    "CREATE UNIQUE INDEX mechanisms_lookup ON mechanisms (plugin,param,privileged);"
-    "CREATE TABLE mechanisms_map ("
-        "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
-        "m_id INTEGER NOT NULL REFERENCES mechanisms(id) ON DELETE CASCADE,"
-        "ord INTEGER NOT NULL"
-        ");"
-    "CREATE INDEX m_map_m_id ON mechanisms_map (m_id);"
-    "CREATE INDEX m_map_r_id ON mechanisms_map (r_id);"
-    "CREATE INDEX m_map_r_id_ord ON mechanisms_map (r_id, ord);"
-    "CREATE TABLE rules ("
-        "id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,"
-        "name TEXT NOT NULL UNIQUE,"
-        "type INTEGER CHECK (type = 1 OR type = 2) NOT NULL,"
-        "class INTEGER CHECK (class > 0),"
-        "'group' TEXT,"
-        "kofn INTEGER,"
-        "timeout INTEGER,"
-        "flags INTEGER,"
-        "tries INTEGER,"
-        "version INTEGER NOT NULL DEFAULT (0),"
-        "created REAL NOT NULL DEFAULT (0),"
-        "modified REAL NOT NULL DEFAULT (0),"
-        "hash BLOB,"
-        "identifier TEXT,"
-        "requirement BLOB,"
-        "comment TEXT"
-        ");"
-    "CREATE INDEX a_type ON rules (type);"
-    "CREATE TABLE config ("
-        "'key' TEXT PRIMARY KEY NOT NULL UNIQUE,"
-        "value"
-        ");"
-    "CREATE TABLE prompts ("
-        "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
-        "lang TEXT NOT NULL,"
-        "value TEXT NOT NULL"
-        ");"
-    "CREATE INDEX p_r_id ON prompts(r_id);"
-    "CREATE TABLE buttons ("
-        "r_id INTEGER NOT NULL REFERENCES rules(id) ON DELETE CASCADE,"
-        "lang TEXT NOT NULL,"
-        "value TEXT NOT NULL"
-        ");"
-    "CREATE INDEX b_r_id ON buttons(r_id);"
-    "INSERT INTO config VALUES('version', "AUTHDB_VERSION_STRING");"
-};
-
-static int32_t
-_sqlite3_exec(sqlite3 * handle, const char * query)
-{
-    int32_t rc = SQLITE_ERROR;
-    require(query != NULL, done);
-    
-    char * errmsg = NULL;
-    rc = sqlite3_exec(handle, query, NULL, NULL, &errmsg);
-    if (errmsg) {
-        LOGE("authdb: exec, (%i) %s", rc, errmsg);
-        sqlite3_free(errmsg);
-    }
-    
-done:
-    return rc;
-}
-
-struct _db_upgrade_stages {
-    int pre;
-    int main;
-    int post;
-};
-
-static struct _db_upgrade_stages auth_upgrade_script[] = {
-    { .pre = -1, .main = 0, .post = -1 } // Create version AUTHDB_VERSION databse.
-};
-
-static int32_t _db_run_script(authdb_connection_t dbconn, int number)
-{
-    int32_t s3e;
-    
-    /* Script -1 == skip this step. */
-    if (number < 0)
-        return SQLITE_OK;
-    
-    /* If we are attempting to run a script we don't have, fail. */
-    if ((size_t)number >= sizeof(authdb_upgrade_sql) / sizeof(char*))
-        return SQLITE_CORRUPT;
-    
-    s3e = _sqlite3_exec(dbconn->handle, authdb_upgrade_sql[number]);
-    
-    return s3e;
-}
-
-static int32_t _db_upgrade_from_version(authdb_connection_t dbconn, int32_t version)
-{
-    int32_t s3e;
-    
-    /* If we are attempting to upgrade to a version greater than what we have
-     an upgrade script for, fail. */
-    if (version < 0 ||
-        (size_t)version >= sizeof(auth_upgrade_script) / sizeof(struct _db_upgrade_stages))
-        return SQLITE_CORRUPT;
-    
-    struct _db_upgrade_stages *script = &auth_upgrade_script[version];
-    s3e = _db_run_script(dbconn, script->pre);
-    if (s3e == SQLITE_OK)
-        s3e = _db_run_script(dbconn, script->main);
-    if (s3e == SQLITE_OK)
-        s3e = _db_run_script(dbconn, script->post);
-    
-    return s3e;
-}
-
-static void _printCFError(const char * errmsg, CFErrorRef err)
-{
-    CFStringRef errString = NULL;
-    errString = CFErrorCopyDescription(err);
-    char * tmp = _copy_cf_string(errString, NULL);
-    LOGV("%s, %s", errmsg, tmp);
-    free_safe(tmp);
-    CFReleaseSafe(errString);
-}
-
-static void _db_load_data(authdb_connection_t dbconn, auth_items_t config)
-{
-    CFURLRef authURL = NULL;
-    CFPropertyListRef plist = NULL;
-    CFDataRef data = NULL;
-    int32_t rc = 0;
-    CFErrorRef err = NULL;
-    CFTypeRef value = NULL;
-    CFAbsoluteTime ts = 0;
-    CFAbsoluteTime old_ts = 0;
-    
-    authURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR(AUTHDB_DATA), kCFURLPOSIXPathStyle, false);
-    require_action(authURL != NULL, done, LOGE("authdb: file not found %s", AUTHDB_DATA));
-    
-    CFURLCopyResourcePropertyForKey(authURL, kCFURLContentModificationDateKey, &value, &err);
-    require_action(err == NULL, done, _printCFError("authdb: failed to get modification date", err));
-    
-    if (CFGetTypeID(value) == CFDateGetTypeID()) {
-        ts = CFDateGetAbsoluteTime(value);
-    }
-    
-    old_ts = auth_items_get_double(config, "data_ts");
-    if (ts > old_ts) {
-        LOGV("authdb: %s modified old=%f, new=%f", AUTHDB_DATA, old_ts, ts);
-        CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, authURL, &data, NULL, NULL, (SInt32*)&rc);
-        require_noerr_action(rc, done, LOGE("authdb: failed to load %s", AUTHDB_DATA));
-
-        plist = CFPropertyListCreateWithData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL, &err);
-        require_action(err == NULL, done, _printCFError("authdb: failed to read plist", err));
-        
-        if (authdb_import_plist(dbconn, plist, true)) {
-            LOGD("authdb: updating data_ts");
-            auth_items_t update = auth_items_create();
-            auth_items_set_double(update, "data_ts", ts);
-            authdb_set_key_value(dbconn, "config", update);
-            CFReleaseSafe(update);
-        }
-    }
-    
-done:
-    CFReleaseSafe(value);
-    CFReleaseSafe(authURL);
-    CFReleaseSafe(plist);
-    CFReleaseSafe(err);
-    CFReleaseSafe(data);
-}
-
-static bool _truncate_db(authdb_connection_t dbconn)
-{
-    int32_t rc = SQLITE_ERROR;
-    int32_t flags = SQLITE_TRUNCATE_JOURNALMODE_WAL | SQLITE_TRUNCATE_AUTOVACUUM_FULL;
-    rc = sqlite3_file_control(dbconn->handle, NULL, SQLITE_TRUNCATE_DATABASE, &flags);
-    if (rc != SQLITE_OK) {
-        LOGV("Failed to delete db handle!  SQLite error %i.\n", rc);
-        if (rc == SQLITE_IOERR) {
-            // Unable to recover successfully if we can't truncate
-            abort();
-        }
-    }
-    
-    return rc == SQLITE_OK;
-}
-
-static void _handle_corrupt_db(authdb_connection_t dbconn)
-{
-    int32_t rc = SQLITE_ERROR;
-    char buf[PATH_MAX+1];
-    sqlite3 *corrupt_db = NULL;
-    
-    snprintf(buf, sizeof(buf), "%s-corrupt", dbconn->db->db_path);
-    if (sqlite3_open(buf, &corrupt_db) == SQLITE_OK) {
-        
-        int on = 1;
-        sqlite3_file_control(corrupt_db, 0, SQLITE_FCNTL_PERSIST_WAL, &on);
-        
-        rc = sqlite3_file_control(corrupt_db, NULL, SQLITE_REPLACE_DATABASE, (void *)dbconn->handle);
-        if (SQLITE_OK == rc) {
-            LOGE("Database at path %s is corrupt. Copying it to %s for further investigation.", dbconn->db->db_path, buf);
-        } else {
-            LOGE("Tried to copy corrupt database at path %s, but we failed with SQLite error %i.", dbconn->db->db_path, rc);
-        }
-        sqlite3_close(corrupt_db);
-    }
-    
-    _truncate_db(dbconn);
-}
-
-static int32_t _db_maintenance(authdb_connection_t dbconn)
-{
-    __block int32_t s3e = SQLITE_OK;
-    __block auth_items_t config = NULL;
-    
-    authdb_transaction(dbconn, AuthDBTransactionNormal, ^bool(void) {
-        
-        authdb_get_key_value(dbconn, "config", &config);
-        
-        // We don't have a config table
-        if (NULL == config) {
-            LOGV("authdb: initializing database");
-            s3e = _db_upgrade_from_version(dbconn, 0);
-            require_noerr_action(s3e, done, LOGE("authdb: failed to initialize database %i", s3e));
-            
-            s3e = authdb_get_key_value(dbconn, "config", &config);
-            require_noerr_action(s3e, done, LOGE("authdb: failed to get config %i", s3e));
-        }
-        
-        int64_t currentVersion = auth_items_get_int64(config, "version");
-        LOGV("authdb: current db ver=%lli", currentVersion);
-        if (currentVersion < AUTHDB_VERSION) {
-            LOGV("authdb: upgrading schema");
-            s3e = _db_upgrade_from_version(dbconn, (int32_t)currentVersion);
-            
-            auth_items_set_int64(config, "version", AUTHDB_VERSION);
-            authdb_set_key_value(dbconn, "config", config);
-        }
-
-    done:
-        return true;
-    });
-    
-    CFReleaseSafe(config);
-    return s3e;
-}
-
-//static void unlock_notify_cb(void **apArg, int nArg AUTH_UNUSED){
-//    dispatch_semaphore_t semaphore = (dispatch_semaphore_t)apArg[0];
-//    dispatch_semaphore_signal(semaphore);
-//}
-//
-//static int32_t _wait_for_unlock_notify(authdb_connection_t dbconn, sqlite3_stmt * stmt)
-//{
-//    int32_t rc;
-//    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
-//    
-//    rc = sqlite3_unlock_notify(dbconn->handle, unlock_notify_cb, semaphore);
-//    require(!rc, done);
-//    
-//    if (dispatch_semaphore_wait(semaphore, 5*NSEC_PER_SEC) != 0) {
-//        LOGV("authdb: timeout occurred!");
-//        sqlite3_unlock_notify(dbconn->handle, NULL, NULL);
-//        rc = SQLITE_LOCKED;
-//    } else if (stmt){
-//        sqlite3_reset(stmt);
-//    }
-//
-//done:
-//    dispatch_release(semaphore);
-//    return rc;
-//}
-
-static bool _is_busy(int32_t rc)
-{
-    return SQLITE_BUSY == rc || SQLITE_LOCKED == rc;
-}
-
-static void _checkResult(authdb_connection_t dbconn, int32_t rc, const char * fn_name, sqlite3_stmt * stmt)
-{
-    bool isCorrupt = (SQLITE_CORRUPT == rc) || (SQLITE_NOTADB == rc) || (SQLITE_IOERR == rc);
-    
-    if (isCorrupt) {
-        _handle_corrupt_db(dbconn);
-        authdb_maintenance(dbconn);
-    } else if (SQLITE_CONSTRAINT == rc || SQLITE_READONLY == rc) {
-        if (stmt) {
-            LOGV("authdb: %s %s for %s", fn_name, sqlite3_errmsg(dbconn->handle), sqlite3_sql(stmt));
-        } else {
-            LOGV("authdb: %s %s", fn_name, sqlite3_errmsg(dbconn->handle));
-        }
-    }
-}
-
-char * authdb_copy_sql_string(sqlite3_stmt * sql,int32_t col)
-{
-    char * result = NULL;
-    const char * sql_str = (const char *)sqlite3_column_text(sql, col);
-    if (sql_str) {
-        size_t len = strlen(sql_str) + 1;
-        result = (char*)calloc(1u, len);
-        check(result != NULL);
-        
-        strlcpy(result, sql_str, len);
-    }
-    return result;
-}
-
-#pragma mark -
-#pragma mark authdb_t
-
-static void
-_authdb_finalize(CFTypeRef value)
-{
-    authdb_t db = (authdb_t)value;
-    
-    CFReleaseSafe(db->connections);
-    dispatch_release(db->queue);
-    free_safe(db->db_path);
-}
-
-AUTH_TYPE_INSTANCE(authdb,
-                   .init = NULL,
-                   .copy = NULL,
-                   .finalize = _authdb_finalize,
-                   .equal = NULL,
-                   .hash = NULL,
-                   .copyFormattingDesc = NULL,
-                   .copyDebugDesc = NULL
-                   );
-
-static CFTypeID authdb_get_type_id() {
-    static CFTypeID type_id = _kCFRuntimeNotATypeID;
-    static dispatch_once_t onceToken;
-    
-    dispatch_once(&onceToken, ^{
-        type_id = _CFRuntimeRegisterClass(&_auth_type_authdb);
-    });
-    
-    return type_id;
-}
-
-authdb_t
-authdb_create()
-{
-    authdb_t db = NULL;
-    
-    db = (authdb_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, authdb_get_type_id(), AUTH_CLASS_SIZE(authdb), NULL);
-    require(db != NULL, done);
-    
-    db->queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
-    db->connections = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
-    
-    if (getenv("__OSINSTALL_ENVIRONMENT") != NULL) {
-        LOGV("authdb: running from installer");
-        db->db_path = _copy_string("file::memory:?cache=shared");
-    } else {
-        db->db_path = _copy_string(AUTHDB);
-    }
-    
-done:
-    return db;
-}
-
-authdb_connection_t authdb_connection_acquire(authdb_t db)
-{
-    __block authdb_connection_t dbconn = NULL;
-#if DEBUG
-    static int32_t total = 0;
-#endif
-    dispatch_sync(db->queue, ^{
-        CFIndex count = CFArrayGetCount(db->connections);
-        if (count) {
-            dbconn = (authdb_connection_t)CFArrayGetValueAtIndex(db->connections, 0);
-            CFArrayRemoveValueAtIndex(db->connections, 0);
-        } else {
-            dbconn = authdb_connection_create(db);
-#if DEBUG
-            total++;
-            LOGV("authdb: no handles available total: %i", total);
-#endif
-        }
-    });
-
-    return dbconn;
-}
-
-void authdb_connection_release(authdb_connection_t * dbconn)
-{
-    if (!dbconn || !(*dbconn))
-        return;
-
-    authdb_connection_t tmp = *dbconn;
-    *dbconn = NULL;
-    
-    dispatch_async(tmp->db->queue, ^{
-        CFIndex count = CFArrayGetCount(tmp->db->connections);
-        if (count <= AUTHDB_MAX_HANDLES) {
-            CFArrayAppendValue(tmp->db->connections, tmp);
-        } else {
-            LOGD("authdb: freeing extra connection");
-            CFRelease(tmp);
-        }
-    });
-}
-
-static bool _db_check_corrupted(authdb_connection_t dbconn)
-{
-    bool isCorrupted = true;
-    sqlite3_stmt *stmt = NULL;
-    int32_t rc;
-    
-    rc = sqlite3_prepare_v2(dbconn->handle, "PRAGMA integrity_check;", -1, &stmt, NULL);
-    if (rc == SQLITE_LOCKED || rc == SQLITE_BUSY) {
-        LOGV("authdb: warning error %i when running integrity check", rc);
-        isCorrupted = false;
-        
-    } else if (rc == SQLITE_OK) {
-        rc = sqlite3_step(stmt);
-        
-        if (rc == SQLITE_LOCKED || rc == SQLITE_BUSY) {
-            LOGV("authdb: warning error %i when running integrity check", rc);
-            isCorrupted = false;
-        } else if (rc == SQLITE_ROW) {
-            const char * result = (const char*)sqlite3_column_text(stmt, 0);
-            
-            if (result && strncasecmp(result, "ok", 3) == 0) {
-                isCorrupted = false;
-            }
-        }
-    }
-    
-    sqlite3_finalize(stmt);
-    return isCorrupted;
-}
-
-bool authdb_maintenance(authdb_connection_t dbconn)
-{
-    LOGD("authdb: starting maintenance");
-    int32_t rc = SQLITE_ERROR;
-    auth_items_t config = NULL;
-    
-    bool isCorrupted = _db_check_corrupted(dbconn);
-    LOGD("authdb: integrity check=%s", isCorrupted ? "fail" : "pass");
-    
-    if (isCorrupted) {
-        _handle_corrupt_db(dbconn);
-    }
-
-    _db_maintenance(dbconn);
-    
-    rc = authdb_get_key_value(dbconn, "config", &config);
-    require_noerr_action(rc, done, LOGV("authdb: maintenance failed %i", rc));
-    
-    _db_load_data(dbconn, config);
-    
-done:
-    CFReleaseSafe(config);
-    LOGD("authdb: finished maintenance");
-    return rc == SQLITE_OK;
-}
-
-int32_t
-authdb_exec(authdb_connection_t dbconn, const char * query)
-{
-    int32_t rc = SQLITE_ERROR;
-    require(query != NULL, done);
-    
-    rc = _sqlite3_exec(dbconn->handle, query);
-    _checkResult(dbconn, rc, __FUNCTION__, NULL);
-    
-done:
-    return rc;
-}
-
-static int32_t _prepare(authdb_connection_t dbconn, const char * sql, sqlite3_stmt ** out_stmt)
-{
-    int32_t rc;
-    sqlite3_stmt * stmt = NULL; 
-    
-    require_action(sql != NULL, done, rc = SQLITE_ERROR);
-    require_action(out_stmt != NULL, done, rc = SQLITE_ERROR);
-    
-    rc = sqlite3_prepare_v2(dbconn->handle, sql, -1, &stmt, NULL);
-    require_noerr_action(rc, done, LOGV("authdb: prepare (%i) %s", rc, sqlite3_errmsg(dbconn->handle)));
-    
-    *out_stmt = stmt;
-    
-done:
-    _checkResult(dbconn, rc, __FUNCTION__, stmt);
-    return rc;
-}
-
-static void _parseItemsAtIndex(sqlite3_stmt * stmt, int32_t col, auth_items_t items, const char * key)
-{
-    switch (sqlite3_column_type(stmt, col)) {
-        case SQLITE_FLOAT:
-            auth_items_set_double(items, key, sqlite3_column_double(stmt, col));
-            break;
-        case SQLITE_INTEGER:
-            auth_items_set_int64(items, key, sqlite3_column_int64(stmt, col));
-            break;
-        case SQLITE_BLOB:
-            auth_items_set_data(items,
-                                key,
-                                sqlite3_column_blob(stmt, col),
-                                (size_t)sqlite3_column_bytes(stmt, col));
-            break;
-        case SQLITE_NULL:
-            break;
-        case SQLITE_TEXT:
-        default:
-            auth_items_set_string(items, key, (const char *)sqlite3_column_text(stmt, col));
-            break;
-    }
-
-//    LOGD("authdb: col=%s, val=%s, type=%i", sqlite3_column_name(stmt, col), sqlite3_column_text(stmt, col), sqlite3_column_type(stmt,col));
-}
-
-static int32_t _bindItemsAtIndex(sqlite3_stmt * stmt, int col, auth_items_t items, const char * key)
-{
-    int32_t rc;
-    switch (auth_items_get_type(items, key)) {
-        case AI_TYPE_INT:
-            rc = sqlite3_bind_int64(stmt, col, auth_items_get_int(items, key));
-            break;
-        case AI_TYPE_UINT:
-            rc = sqlite3_bind_int64(stmt, col, auth_items_get_uint(items, key));
-            break;
-        case AI_TYPE_INT64:
-            rc = sqlite3_bind_int64(stmt, col, auth_items_get_int64(items, key));
-            break;
-        case AI_TYPE_UINT64:
-            rc = sqlite3_bind_int64(stmt, col, (int64_t)auth_items_get_uint64(items, key));
-            break;
-        case AI_TYPE_DOUBLE:
-            rc = sqlite3_bind_double(stmt, col, auth_items_get_double(items, key));
-            break;
-        case AI_TYPE_BOOL:
-            rc = sqlite3_bind_int64(stmt, col, auth_items_get_bool(items, key));
-            break;
-        case AI_TYPE_DATA:
-        {
-            size_t blobLen = 0;
-            const void * blob = auth_items_get_data(items, key, &blobLen);
-            rc = sqlite3_bind_blob(stmt, col, blob, (int32_t)blobLen, NULL);
-        }
-            break;
-        case AI_TYPE_STRING:
-            rc = sqlite3_bind_text(stmt, col, auth_items_get_string(items, key), -1, NULL);
-            break;
-        default:
-            rc = sqlite3_bind_null(stmt, col);
-            break;
-    }
-    if (rc != SQLITE_OK) {
-        LOGV("authdb: auth_items bind failed (%i)", rc);
-    }
-    return rc;
-}
-
-int32_t authdb_get_key_value(authdb_connection_t dbconn, const char * table, auth_items_t * out_items)
-{
-    int32_t rc = SQLITE_ERROR;
-    char * query = NULL;
-    sqlite3_stmt * stmt = NULL;
-    auth_items_t items = NULL;
-    
-    require(table != NULL, done);
-    require(out_items != NULL, done);
-    
-    asprintf(&query, "SELECT * FROM %s", table);
-    
-    rc = _prepare(dbconn, query, &stmt);
-    require_noerr(rc, done);
-    
-    items = auth_items_create();
-    while ((rc = sqlite3_step(stmt)) != SQLITE_DONE) {
-        switch (rc) {
-            case SQLITE_ROW:
-                _parseItemsAtIndex(stmt, 1, items, (const char*)sqlite3_column_text(stmt, 0));
-                break;
-            default:
-                _checkResult(dbconn, rc, __FUNCTION__, stmt);
-                if (_is_busy(rc)) {
-                    sleep(AUTHDB_BUSY_DELAY);
-                } else {
-                    require_noerr_action(rc, done, LOGV("authdb: get_key_value (%i) %s", rc, sqlite3_errmsg(dbconn->handle)));
-                }
-                break;
-        }
-    }
-    
-    rc = SQLITE_OK;
-    CFRetain(items);
-    *out_items = items;
-    
-done:
-    CFReleaseSafe(items);
-    free_safe(query);
-    sqlite3_finalize(stmt);
-    return rc;
-}
-
-int32_t authdb_set_key_value(authdb_connection_t dbconn, const char * table, auth_items_t items)
-{
-    __block int32_t rc = SQLITE_ERROR;
-    char * query = NULL;
-    sqlite3_stmt * stmt = NULL;
-    
-    require(table != NULL, done);
-    require(items != NULL, done);
-    
-    asprintf(&query, "INSERT OR REPLACE INTO %s VALUES (?,?)", table);
-    
-    rc = _prepare(dbconn, query, &stmt);
-    require_noerr(rc, done);
-    
-    auth_items_iterate(items, ^bool(const char *key) {
-        sqlite3_reset(stmt);
-        _checkResult(dbconn, rc, __FUNCTION__, stmt);
-        
-        sqlite3_bind_text(stmt, 1, key, -1, NULL);
-        _bindItemsAtIndex(stmt, 2, items, key);
-        
-        rc = sqlite3_step(stmt);
-        if (rc != SQLITE_DONE) {
-            _checkResult(dbconn, rc, __FUNCTION__, stmt);
-            LOGV("authdb: set_key_value, step (%i) %s", rc, sqlite3_errmsg(dbconn->handle));
-        }
-        
-        return true;
-    });
-    
-done:
-    free_safe(query);
-    sqlite3_finalize(stmt);
-    return rc;
-}
-
-static int32_t _begin_transaction_type(authdb_connection_t dbconn, AuthDBTransactionType type)
-{
-    int32_t result = SQLITE_ERROR;
-    
-    const char * query = NULL;
-    switch (type) {
-        case AuthDBTransactionImmediate:
-            query = "BEGIN IMMEDATE;";
-            break;
-        case AuthDBTransactionExclusive:
-            query = "BEGIN EXCLUSIVE;";
-            break;
-        case AuthDBTransactionNormal:
-            query = "BEGIN;";
-            break;
-        default:
-            break;
-    }
-    
-    result = SQLITE_OK;
-    
-    if (query != NULL && sqlite3_get_autocommit(dbconn->handle) != 0) {
-        result = _sqlite3_exec(dbconn->handle, query);
-    }
-    
-    return result;
-}
-
-static int32_t _end_transaction(authdb_connection_t dbconn, bool commit)
-{
-    if (commit) {
-        return _sqlite3_exec(dbconn->handle, "END;");
-    } else {
-        return _sqlite3_exec(dbconn->handle, "ROLLBACK;");
-    }
-}
-
-bool authdb_transaction(authdb_connection_t dbconn, AuthDBTransactionType type, bool (^t)(void))
-{
-    int32_t result = SQLITE_ERROR;
-    bool commit = false;
-
-    result = _begin_transaction_type(dbconn, type);
-    require_action(result == SQLITE_OK, done, LOGV("authdb: transaction begin failed %i", result));
-
-    commit = t();
-    
-    result = _end_transaction(dbconn, commit);
-    require_action(result == SQLITE_OK, done, commit = false; LOGV("authdb: transaction end failed %i", result));
-
-done:
-    return commit;
-}
-
-bool authdb_step(authdb_connection_t dbconn, const char * sql, void (^bind_stmt)(sqlite3_stmt*), authdb_iterator_t iter)
-{
-    bool result = false;
-    sqlite3_stmt * stmt = NULL;
-    int32_t rc = SQLITE_ERROR;
-    
-    require_action(sql != NULL, done, rc = SQLITE_ERROR);
-    
-    rc = _prepare(dbconn, sql, &stmt);
-    require_noerr(rc, done);
-    
-    if (bind_stmt) {
-        bind_stmt(stmt);
-    }
-    
-    int32_t count = sqlite3_column_count(stmt);
-    
-    auth_items_t items = NULL;
-    while ((rc = sqlite3_step(stmt)) != SQLITE_DONE) {
-        switch (rc) {
-            case SQLITE_ROW:
-                {
-                    if (iter) {
-                        items = auth_items_create();
-                        for (int i = 0; i < count; i++) {
-                            _parseItemsAtIndex(stmt, i, items, sqlite3_column_name(stmt, i));
-                        }
-                        result = iter(items);
-                        CFReleaseNull(items);
-                        if (!result) {
-                            goto done;
-                        }
-                    }
-                }
-                break;
-            default:
-                if (_is_busy(rc)) {
-                    LOGV("authdb: %s", sqlite3_errmsg(dbconn->handle));
-                    sleep(AUTHDB_BUSY_DELAY);
-                    sqlite3_reset(stmt);
-                } else {
-                    require_noerr_action(rc, done, LOGV("authdb: step (%i) %s", rc, sqlite3_errmsg(dbconn->handle)));
-                }
-                break;
-        }
-    }
-    
-done:
-    _checkResult(dbconn, rc, __FUNCTION__, stmt);
-    sqlite3_finalize(stmt);
-    return rc == SQLITE_DONE;
-}
-
-void authdb_checkpoint(authdb_connection_t dbconn)
-{
-    int32_t rc = sqlite3_wal_checkpoint(dbconn->handle, NULL);
-    if (rc != SQLITE_OK) {
-        LOGV("authdb: checkpoit failed %i", rc);
-    }
-}
-
-static CFMutableArrayRef
-_copy_rules_dict(RuleType type, CFDictionaryRef plist, authdb_connection_t dbconn)
-{
-    CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
-    require(result != NULL, done);
-
-    _cf_dictionary_iterate(plist, ^bool(CFTypeRef key, CFTypeRef value) {
-        if (CFGetTypeID(key) != CFStringGetTypeID()) {
-            return true;
-        }
-        
-        if (CFGetTypeID(value) != CFDictionaryGetTypeID()) {
-            return true;
-        }
-        
-        rule_t rule = rule_create_with_plist(type, key, value, dbconn);
-        if (rule) {
-            CFArrayAppendValue(result, rule);
-            CFReleaseSafe(rule);
-        }
-        
-        return true;
-    });
-    
-done:
-    return result;
-}
-
-static void
-_import_rules(authdb_connection_t dbconn, CFMutableArrayRef rules, bool version_check, CFAbsoluteTime now)
-{
-    CFMutableArrayRef notcommited = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
-    CFIndex count = CFArrayGetCount(rules);
-    
-    for (CFIndex i = 0; i < count; i++) {
-        rule_t rule = (rule_t)CFArrayGetValueAtIndex(rules, i);
-        
-        bool update = false;
-        if (version_check) {
-            if (rule_get_id(rule) != 0) { // rule already exists see if we need to update
-                rule_t current = rule_create_with_string(rule_get_name(rule), dbconn);
-                if (rule_get_version(rule) > rule_get_version(current)) {
-                    update = true;
-                }
-                CFReleaseSafe(current);
-                
-                if (!update) {
-                    continue;
-                }
-            }
-        }
-        
-        __block bool delayCommit = false;
-        
-        switch (rule_get_type(rule)) {
-            case RT_RULE:
-                rule_delegates_iterator(rule, ^bool(rule_t delegate) {
-                    if (rule_get_id(delegate) == 0) {
-                        // fetch the rule from the database if it was previously committed
-                        rule_sql_fetch(delegate, dbconn);
-                    }
-                    if (rule_get_id(delegate) == 0) {
-                        LOGD("authdb: delaying %s waiting for delegate %s", rule_get_name(rule), rule_get_name(delegate));
-                        delayCommit = true;
-                        return false;
-                    }
-                    return true;
-                });
-                break;
-            default:
-                break;
-        }
-        
-        if (!delayCommit) {
-            bool success = rule_sql_commit(rule, dbconn, now, NULL);
-            LOGV("authdb: %s %s %s %s",
-                 update ? "updating" : "importing",
-                 rule_get_type(rule) == RT_RULE ? "rule" : "right",
-                 rule_get_name(rule), success ? "success" : "FAIL");
-            if (!success) {
-                CFArrayAppendValue(notcommited, rule);
-            }
-        } else {
-            CFArrayAppendValue(notcommited, rule);
-        }
-    }
-    CFArrayRemoveAllValues(rules);
-    CFArrayAppendArray(rules, notcommited, CFRangeMake(0, CFArrayGetCount(notcommited)));
-    CFReleaseSafe(notcommited);
-}
-
-bool
-authdb_import_plist(authdb_connection_t dbconn, CFDictionaryRef plist, bool version_check)
-{
-    bool result = false;
-    
-    LOGV("authdb: starting import");
-    
-    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
-    CFMutableArrayRef rights = NULL;
-    CFMutableArrayRef rules = NULL;
-    require(plist != NULL, done);
-    
-    CFTypeRef rightsDict = CFDictionaryGetValue(plist, CFSTR("rights"));
-    if (rightsDict && CFGetTypeID(rightsDict) == CFDictionaryGetTypeID()) {
-        rights = _copy_rules_dict(RT_RIGHT, rightsDict, dbconn);
-    }
-    
-    CFTypeRef rulesDict = CFDictionaryGetValue(plist, CFSTR("rules"));
-    if (rulesDict && CFGetTypeID(rulesDict) == CFDictionaryGetTypeID()) {
-        rules = _copy_rules_dict(RT_RULE, rulesDict, dbconn);
-    }
-
-    LOGV("authdb: rights = %li", CFArrayGetCount(rights));
-    LOGV("authdb: rules = %li", CFArrayGetCount(rules));
-    
-    CFIndex count;
-    // first pass import base rules without delegations
-    // remaining import rules that delegate to other rules
-    // loop upto 3 times to commit dependent rules first
-    for (int32_t j = 0; j < 3; j++) {
-        count = CFArrayGetCount(rules);
-        if (!count)
-            break;
-
-        _import_rules(dbconn, rules, version_check, now);
-    }
-    
-    _import_rules(dbconn, rights, version_check, now);
-    
-    if (CFArrayGetCount(rights) == 0) {
-        result = true;
-    }
-    
-    authdb_checkpoint(dbconn);
-    
-done:
-    CFReleaseSafe(rights);
-    CFReleaseSafe(rules);
-    
-    LOGV("authdb: finished import, %s", result ? "succeeded" : "failed");
-    
-    return result;
-}
-
-#pragma mark -
-#pragma mark authdb_connection_t
-
-static bool _sql_profile_enabled(void)
-{
-    static bool profile_enabled = false;
-    
-#if DEBUG
-    static dispatch_once_t onceToken;
-
-    //sudo defaults write /Library/Preferences/com.apple.security.auth profile -bool true
-    dispatch_once(&onceToken, ^{
-               CFTypeRef profile = (CFNumberRef)CFPreferencesCopyValue(CFSTR("profile"), CFSTR(SECURITY_AUTH_NAME), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
-        
-        if (profile && CFGetTypeID(profile) == CFBooleanGetTypeID()) {
-            profile_enabled = CFBooleanGetValue((CFBooleanRef)profile);
-        }
-        
-        LOGV("authdb: sql profile: %s", profile_enabled ? "enabled" : "disabled");
-        
-        CFReleaseSafe(profile);
-    });
-#endif
-    
-    return profile_enabled;
-}
-
-static void _profile(void *context AUTH_UNUSED, const char *sql, sqlite3_uint64 ns) {
-    LOGV("==\nauthdb: %s\nTime: %llu ms\n", sql, ns >> 20);
-}
-
-static sqlite3 * _create_handle(authdb_t db)
-{
-    bool dbcreated = false;
-    sqlite3 * handle = NULL;
-    int32_t rc = sqlite3_open_v2(db->db_path, &handle, SQLITE_OPEN_READWRITE, NULL);
-    
-    if (rc != SQLITE_OK) {
-        char * tmp = dirname(db->db_path);
-        if (tmp) {
-            mkpath_np(tmp, 0700);
-        }
-        rc = sqlite3_open_v2(db->db_path, &handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
-        dbcreated = true;
-    }
-    require_noerr_action(rc, done, LOGE("authdb: open %s (%i) %s", db->db_path, rc, sqlite3_errmsg(handle)));
-    
-    if (_sql_profile_enabled()) {
-        sqlite3_profile(handle, _profile, NULL);
-    }
-    
-    _sqlite3_exec(handle, "PRAGMA foreign_keys = ON");
-    _sqlite3_exec(handle, "PRAGMA temp_store = MEMORY");
-    
-    if (dbcreated) {
-        _sqlite3_exec(handle, "PRAGMA auto_vacuum = FULL");
-        _sqlite3_exec(handle, "PRAGMA journal_mode = WAL");
-        
-        int on = 1;
-        sqlite3_file_control(handle, 0, SQLITE_FCNTL_PERSIST_WAL, &on);
-        
-        chmod(db->db_path, S_IRUSR | S_IWUSR);
-    }
-    
-done:
-    return handle;
-}
-
-static void
-_authdb_connection_finalize(CFTypeRef value)
-{
-    authdb_connection_t dbconn = (authdb_connection_t)value;
-    
-    if (dbconn->handle) {
-        sqlite3_close(dbconn->handle);
-    }
-    CFReleaseSafe(dbconn->db);
-}
-
-AUTH_TYPE_INSTANCE(authdb_connection,
-                   .init = NULL,
-                   .copy = NULL,
-                   .finalize = _authdb_connection_finalize,
-                   .equal = NULL,
-                   .hash = NULL,
-                   .copyFormattingDesc = NULL,
-                   .copyDebugDesc = NULL
-                   );
-
-static CFTypeID authdb_connection_get_type_id() {
-    static CFTypeID type_id = _kCFRuntimeNotATypeID;
-    static dispatch_once_t onceToken;
-    
-    dispatch_once(&onceToken, ^{
-        type_id = _CFRuntimeRegisterClass(&_auth_type_authdb_connection);
-    });
-    
-    return type_id;
-}
-
-authdb_connection_t
-authdb_connection_create(authdb_t db)
-{
-    authdb_connection_t dbconn = NULL;
-    
-    dbconn = (authdb_connection_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, authdb_connection_get_type_id(), AUTH_CLASS_SIZE(authdb_connection), NULL);
-    require(dbconn != NULL, done);
-    
-    dbconn->db = (authdb_t)CFRetain(db);
-    dbconn->handle = _create_handle(dbconn->db);
-
-done:
-    return dbconn;
-}