diff options
Diffstat (limited to 'crypto/heimdal/lib/hdb/hdb-sqlite.c')
-rw-r--r-- | crypto/heimdal/lib/hdb/hdb-sqlite.c | 879 |
1 files changed, 879 insertions, 0 deletions
diff --git a/crypto/heimdal/lib/hdb/hdb-sqlite.c b/crypto/heimdal/lib/hdb/hdb-sqlite.c new file mode 100644 index 0000000..e0635888 --- /dev/null +++ b/crypto/heimdal/lib/hdb/hdb-sqlite.c @@ -0,0 +1,879 @@ +/* + * Copyright (c) 2009 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hdb_locl.h" +#include "sqlite3.h" + +#define MAX_RETRIES 10 + +typedef struct hdb_sqlite_db { + double version; + sqlite3 *db; + char *db_file; + + sqlite3_stmt *get_version; + sqlite3_stmt *fetch; + sqlite3_stmt *get_ids; + sqlite3_stmt *add_entry; + sqlite3_stmt *add_principal; + sqlite3_stmt *add_alias; + sqlite3_stmt *delete_aliases; + sqlite3_stmt *update_entry; + sqlite3_stmt *remove; + sqlite3_stmt *get_all_entries; + +} hdb_sqlite_db; + +/* This should be used to mark updates which make the code incompatible + * with databases created with previous versions. Don't update it if + * compatibility is not broken. */ +#define HDBSQLITE_VERSION 0.1 + +#define _HDBSQLITE_STRINGIFY(x) #x +#define HDBSQLITE_STRINGIFY(x) _HDBSQLITE_STRINGIFY(x) + +#define HDBSQLITE_CREATE_TABLES \ + " BEGIN TRANSACTION;" \ + " CREATE TABLE Version (number REAL);" \ + " INSERT INTO Version (number)" \ + " VALUES (" HDBSQLITE_STRINGIFY(HDBSQLITE_VERSION) ");" \ + " CREATE TABLE Principal" \ + " (id INTEGER PRIMARY KEY," \ + " principal TEXT UNIQUE NOT NULL," \ + " canonical INTEGER," \ + " entry INTEGER);" \ + " CREATE TABLE Entry" \ + " (id INTEGER PRIMARY KEY," \ + " data BLOB);" \ + " COMMIT" +#define HDBSQLITE_CREATE_TRIGGERS \ + " CREATE TRIGGER remove_principals AFTER DELETE ON Entry" \ + " BEGIN" \ + " DELETE FROM Principal" \ + " WHERE entry = OLD.id;" \ + " END" +#define HDBSQLITE_GET_VERSION \ + " SELECT number FROM Version" +#define HDBSQLITE_FETCH \ + " SELECT Entry.data FROM Principal, Entry" \ + " WHERE Principal.principal = ? AND" \ + " Entry.id = Principal.entry" +#define HDBSQLITE_GET_IDS \ + " SELECT id, entry FROM Principal" \ + " WHERE principal = ?" +#define HDBSQLITE_ADD_ENTRY \ + " INSERT INTO Entry (data) VALUES (?)" +#define HDBSQLITE_ADD_PRINCIPAL \ + " INSERT INTO Principal (principal, entry, canonical)" \ + " VALUES (?, last_insert_rowid(), 1)" +#define HDBSQLITE_ADD_ALIAS \ + " INSERT INTO Principal (principal, entry, canonical)" \ + " VALUES(?, ?, 0)" +#define HDBSQLITE_DELETE_ALIASES \ + " DELETE FROM Principal" \ + " WHERE entry = ? AND canonical = 0" +#define HDBSQLITE_UPDATE_ENTRY \ + " UPDATE Entry SET data = ?" \ + " WHERE id = ?" +#define HDBSQLITE_REMOVE \ + " DELETE FROM ENTRY WHERE id = " \ + " (SELECT entry FROM Principal" \ + " WHERE principal = ?)" +#define HDBSQLITE_GET_ALL_ENTRIES \ + " SELECT data FROM Entry" + +/** + * Wrapper around sqlite3_prepare_v2. + * + * @param context The current krb5 context + * @param statement Where to store the pointer to the statement + * after preparing it + * @param str SQL code for the statement + * + * @return 0 if OK, an error code if not + */ +static krb5_error_code +hdb_sqlite_prepare_stmt(krb5_context context, + sqlite3 *db, + sqlite3_stmt **statement, + const char *str) +{ + int ret, tries = 0; + + ret = sqlite3_prepare_v2(db, str, -1, statement, NULL); + while((tries++ < MAX_RETRIES) && + ((ret == SQLITE_BUSY) || + (ret == SQLITE_IOERR_BLOCKED) || + (ret == SQLITE_LOCKED))) { + krb5_warnx(context, "hdb-sqlite: prepare busy"); + sleep(1); + ret = sqlite3_prepare_v2(db, str, -1, statement, NULL); + } + + if (ret != SQLITE_OK) { + krb5_set_error_message(context, EINVAL, + "Failed to prepare stmt %s: %s", + str, sqlite3_errmsg(db)); + return EINVAL; + } + + return 0; +} + +/** + * A wrapper around sqlite3_exec. + * + * @param context The current krb5 context + * @param database An open sqlite3 database handle + * @param statement SQL code to execute + * @param error_code What to return if the statement fails + * + * @return 0 if OK, else error_code + */ +static krb5_error_code +hdb_sqlite_exec_stmt(krb5_context context, + sqlite3 *database, + const char *statement, + krb5_error_code error_code) +{ + int ret; + + ret = sqlite3_exec(database, statement, NULL, NULL, NULL); + + while(((ret == SQLITE_BUSY) || + (ret == SQLITE_IOERR_BLOCKED) || + (ret == SQLITE_LOCKED))) { + krb5_warnx(context, "hdb-sqlite: exec busy: %d", (int)getpid()); + sleep(1); + ret = sqlite3_exec(database, statement, NULL, NULL, NULL); + } + + if (ret != SQLITE_OK && error_code) { + krb5_set_error_message(context, error_code, + "Execute %s: %s", statement, + sqlite3_errmsg(database)); + return error_code; + } + + return 0; +} + +/** + * Opens an sqlite3 database handle to a file, may create the + * database file depending on flags. + * + * @param context The current krb5 context + * @param db Heimdal database handle + * @param flags Controls whether or not the file may be created, + * may be 0 or SQLITE_OPEN_CREATE + */ +static krb5_error_code +hdb_sqlite_open_database(krb5_context context, HDB *db, int flags) +{ + int ret; + hdb_sqlite_db *hsdb = (hdb_sqlite_db*) db->hdb_db; + + ret = sqlite3_open_v2(hsdb->db_file, &hsdb->db, + SQLITE_OPEN_READWRITE | flags, NULL); + + if (ret) { + if (hsdb->db) { + ret = ENOENT; + krb5_set_error_message(context, ret, + "Error opening sqlite database %s: %s", + hsdb->db_file, sqlite3_errmsg(hsdb->db)); + sqlite3_close(hsdb->db); + hsdb->db = NULL; + } else + ret = krb5_enomem(context); + return ret; + } + + return 0; +} + +static int +hdb_sqlite_step(krb5_context context, sqlite3 *db, sqlite3_stmt *stmt) +{ + int ret; + + ret = sqlite3_step(stmt); + while(((ret == SQLITE_BUSY) || + (ret == SQLITE_IOERR_BLOCKED) || + (ret == SQLITE_LOCKED))) { + krb5_warnx(context, "hdb-sqlite: step busy: %d", (int)getpid()); + sleep(1); + ret = sqlite3_step(stmt); + } + return ret; +} + +/** + * Closes the database and frees memory allocated for statements. + * + * @param context The current krb5 context + * @param db Heimdal database handle + */ +static krb5_error_code +hdb_sqlite_close_database(krb5_context context, HDB *db) +{ + hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; + + sqlite3_finalize(hsdb->get_version); + sqlite3_finalize(hsdb->fetch); + sqlite3_finalize(hsdb->get_ids); + sqlite3_finalize(hsdb->add_entry); + sqlite3_finalize(hsdb->add_principal); + sqlite3_finalize(hsdb->add_alias); + sqlite3_finalize(hsdb->delete_aliases); + sqlite3_finalize(hsdb->update_entry); + sqlite3_finalize(hsdb->remove); + sqlite3_finalize(hsdb->get_all_entries); + + sqlite3_close(hsdb->db); + + return 0; +} + +/** + * Opens an sqlite database file and prepares it for use. + * If the file does not exist it will be created. + * + * @param context The current krb5_context + * @param db The heimdal database handle + * @param filename Where to store the database file + * + * @return 0 if everything worked, an error code if not + */ +static krb5_error_code +hdb_sqlite_make_database(krb5_context context, HDB *db, const char *filename) +{ + int ret; + int created_file = 0; + hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; + + hsdb->db_file = strdup(filename); + if(hsdb->db_file == NULL) + return ENOMEM; + + ret = hdb_sqlite_open_database(context, db, 0); + if (ret) { + ret = hdb_sqlite_open_database(context, db, SQLITE_OPEN_CREATE); + if (ret) goto out; + + created_file = 1; + + ret = hdb_sqlite_exec_stmt(context, hsdb->db, + HDBSQLITE_CREATE_TABLES, + EINVAL); + if (ret) goto out; + + ret = hdb_sqlite_exec_stmt(context, hsdb->db, + HDBSQLITE_CREATE_TRIGGERS, + EINVAL); + if (ret) goto out; + } + + ret = hdb_sqlite_prepare_stmt(context, hsdb->db, + &hsdb->get_version, + HDBSQLITE_GET_VERSION); + if (ret) goto out; + ret = hdb_sqlite_prepare_stmt(context, hsdb->db, + &hsdb->fetch, + HDBSQLITE_FETCH); + if (ret) goto out; + ret = hdb_sqlite_prepare_stmt(context, hsdb->db, + &hsdb->get_ids, + HDBSQLITE_GET_IDS); + if (ret) goto out; + ret = hdb_sqlite_prepare_stmt(context, hsdb->db, + &hsdb->add_entry, + HDBSQLITE_ADD_ENTRY); + if (ret) goto out; + ret = hdb_sqlite_prepare_stmt(context, hsdb->db, + &hsdb->add_principal, + HDBSQLITE_ADD_PRINCIPAL); + if (ret) goto out; + ret = hdb_sqlite_prepare_stmt(context, hsdb->db, + &hsdb->add_alias, + HDBSQLITE_ADD_ALIAS); + if (ret) goto out; + ret = hdb_sqlite_prepare_stmt(context, hsdb->db, + &hsdb->delete_aliases, + HDBSQLITE_DELETE_ALIASES); + if (ret) goto out; + ret = hdb_sqlite_prepare_stmt(context, hsdb->db, + &hsdb->update_entry, + HDBSQLITE_UPDATE_ENTRY); + if (ret) goto out; + ret = hdb_sqlite_prepare_stmt(context, hsdb->db, + &hsdb->remove, + HDBSQLITE_REMOVE); + if (ret) goto out; + ret = hdb_sqlite_prepare_stmt(context, hsdb->db, + &hsdb->get_all_entries, + HDBSQLITE_GET_ALL_ENTRIES); + if (ret) goto out; + + ret = hdb_sqlite_step(context, hsdb->db, hsdb->get_version); + if(ret == SQLITE_ROW) { + hsdb->version = sqlite3_column_double(hsdb->get_version, 0); + } + sqlite3_reset(hsdb->get_version); + ret = 0; + + if(hsdb->version != HDBSQLITE_VERSION) { + ret = EINVAL; + krb5_set_error_message(context, ret, "HDBSQLITE_VERSION mismatch"); + } + + if(ret) goto out; + + return 0; + + out: + if (hsdb->db) + sqlite3_close(hsdb->db); + if (created_file) + unlink(hsdb->db_file); + + return ret; +} + +/** + * Retrieves an entry by searching for the given + * principal in the Principal database table, both + * for canonical principals and aliases. + * + * @param context The current krb5_context + * @param db Heimdal database handle + * @param principal The principal whose entry to search for + * @param flags Currently only for HDB_F_DECRYPT + * @param kvno kvno to fetch is HDB_F_KVNO_SPECIFIED use used + * + * @return 0 if everything worked, an error code if not + */ +static krb5_error_code +hdb_sqlite_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal, + unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry) +{ + int sqlite_error; + krb5_error_code ret; + char *principal_string; + hdb_sqlite_db *hsdb = (hdb_sqlite_db*)(db->hdb_db); + sqlite3_stmt *fetch = hsdb->fetch; + krb5_data value; + + ret = krb5_unparse_name(context, principal, &principal_string); + if (ret) { + free(principal_string); + return ret; + } + + sqlite3_bind_text(fetch, 1, principal_string, -1, SQLITE_STATIC); + + sqlite_error = hdb_sqlite_step(context, hsdb->db, fetch); + if (sqlite_error != SQLITE_ROW) { + if(sqlite_error == SQLITE_DONE) { + ret = HDB_ERR_NOENTRY; + goto out; + } else { + ret = EINVAL; + krb5_set_error_message(context, ret, + "sqlite fetch failed: %d", + sqlite_error); + goto out; + } + } + + value.length = sqlite3_column_bytes(fetch, 0); + value.data = (void *) sqlite3_column_blob(fetch, 0); + + ret = hdb_value2entry(context, &value, &entry->entry); + if(ret) + goto out; + + if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { + ret = hdb_unseal_keys(context, db, &entry->entry); + if(ret) { + hdb_free_entry(context, entry); + goto out; + } + } + + ret = 0; + +out: + + sqlite3_clear_bindings(fetch); + sqlite3_reset(fetch); + + free(principal_string); + + return ret; +} + +/** + * Convenience function to step a prepared statement with no + * value once. + * + * @param context The current krb5_context + * @param statement A prepared sqlite3 statement + * + * @return 0 if everything worked, an error code if not + */ +static krb5_error_code +hdb_sqlite_step_once(krb5_context context, HDB *db, sqlite3_stmt *statement) +{ + int ret; + hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; + + ret = hdb_sqlite_step(context, hsdb->db, statement); + sqlite3_clear_bindings(statement); + sqlite3_reset(statement); + + return ret; +} + + +/** + * Stores an hdb_entry in the database. If flags contains HDB_F_REPLACE + * a previous entry may be replaced. + * + * @param context The current krb5_context + * @param db Heimdal database handle + * @param flags May currently only contain HDB_F_REPLACE + * @param entry The data to store + * + * @return 0 if everything worked, an error code if not + */ +static krb5_error_code +hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags, + hdb_entry_ex *entry) +{ + int ret; + int i; + sqlite_int64 entry_id; + char *principal_string = NULL; + char *alias_string; + const HDB_Ext_Aliases *aliases; + + hdb_sqlite_db *hsdb = (hdb_sqlite_db *)(db->hdb_db); + krb5_data value; + sqlite3_stmt *get_ids = hsdb->get_ids; + + ret = hdb_sqlite_exec_stmt(context, hsdb->db, + "BEGIN IMMEDIATE TRANSACTION", EINVAL); + if(ret != SQLITE_OK) { + ret = EINVAL; + krb5_set_error_message(context, ret, + "SQLite BEGIN TRANSACTION failed: %s", + sqlite3_errmsg(hsdb->db)); + goto rollback; + } + + ret = krb5_unparse_name(context, + entry->entry.principal, &principal_string); + if (ret) { + goto rollback; + } + + ret = hdb_seal_keys(context, db, &entry->entry); + if(ret) { + goto rollback; + } + + ret = hdb_entry2value(context, &entry->entry, &value); + if(ret) { + goto rollback; + } + + sqlite3_bind_text(get_ids, 1, principal_string, -1, SQLITE_STATIC); + ret = hdb_sqlite_step(context, hsdb->db, get_ids); + + if(ret == SQLITE_DONE) { /* No such principal */ + + sqlite3_bind_blob(hsdb->add_entry, 1, + value.data, value.length, SQLITE_STATIC); + ret = hdb_sqlite_step(context, hsdb->db, hsdb->add_entry); + sqlite3_clear_bindings(hsdb->add_entry); + sqlite3_reset(hsdb->add_entry); + if(ret != SQLITE_DONE) + goto rollback; + + sqlite3_bind_text(hsdb->add_principal, 1, + principal_string, -1, SQLITE_STATIC); + ret = hdb_sqlite_step(context, hsdb->db, hsdb->add_principal); + sqlite3_clear_bindings(hsdb->add_principal); + sqlite3_reset(hsdb->add_principal); + if(ret != SQLITE_DONE) + goto rollback; + + entry_id = sqlite3_column_int64(get_ids, 1); + + } else if(ret == SQLITE_ROW) { /* Found a principal */ + + if(! (flags & HDB_F_REPLACE)) /* Not allowed to replace it */ + goto rollback; + + entry_id = sqlite3_column_int64(get_ids, 1); + + sqlite3_bind_int64(hsdb->delete_aliases, 1, entry_id); + ret = hdb_sqlite_step_once(context, db, hsdb->delete_aliases); + if(ret != SQLITE_DONE) + goto rollback; + + sqlite3_bind_blob(hsdb->update_entry, 1, + value.data, value.length, SQLITE_STATIC); + sqlite3_bind_int64(hsdb->update_entry, 2, entry_id); + ret = hdb_sqlite_step_once(context, db, hsdb->update_entry); + if(ret != SQLITE_DONE) + goto rollback; + + } else { + /* Error! */ + goto rollback; + } + + ret = hdb_entry_get_aliases(&entry->entry, &aliases); + if(ret || aliases == NULL) + goto commit; + + for(i = 0; i < aliases->aliases.len; i++) { + + ret = krb5_unparse_name(context, &aliases->aliases.val[i], + &alias_string); + if (ret) { + free(alias_string); + goto rollback; + } + + sqlite3_bind_text(hsdb->add_alias, 1, alias_string, + -1, SQLITE_STATIC); + sqlite3_bind_int64(hsdb->add_alias, 2, entry_id); + ret = hdb_sqlite_step_once(context, db, hsdb->add_alias); + + free(alias_string); + + if(ret != SQLITE_DONE) + goto rollback; + } + + ret = 0; + +commit: + + free(principal_string); + + krb5_data_free(&value); + + sqlite3_clear_bindings(get_ids); + sqlite3_reset(get_ids); + + ret = hdb_sqlite_exec_stmt(context, hsdb->db, "COMMIT", EINVAL); + if(ret != SQLITE_OK) + krb5_warnx(context, "hdb-sqlite: COMMIT problem: %d: %s", + ret, sqlite3_errmsg(hsdb->db)); + + return ret; + +rollback: + + krb5_warnx(context, "hdb-sqlite: store rollback problem: %d: %s", + ret, sqlite3_errmsg(hsdb->db)); + + free(principal_string); + + ret = hdb_sqlite_exec_stmt(context, hsdb->db, + "ROLLBACK", EINVAL); + return ret; +} + +/** + * This may be called often by other code, since the BDB backends + * can not have several open connections. SQLite can handle + * many processes with open handles to the database file + * and closing/opening the handle is an expensive operation. + * Hence, this function does nothing. + * + * @param context The current krb5 context + * @param db Heimdal database handle + * + * @return Always returns 0 + */ +static krb5_error_code +hdb_sqlite_close(krb5_context context, HDB *db) +{ + return 0; +} + +/** + * The opposite of hdb_sqlite_close. Since SQLite accepts + * many open handles to the database file the handle does not + * need to be closed, or reopened. + * + * @param context The current krb5 context + * @param db Heimdal database handle + * @param flags + * @param mode_t + * + * @return Always returns 0 + */ +static krb5_error_code +hdb_sqlite_open(krb5_context context, HDB *db, int flags, mode_t mode) +{ + return 0; +} + +/** + * Closes the databse and frees all resources. + * + * @param context The current krb5 context + * @param db Heimdal database handle + * + * @return 0 on success, an error code if not + */ +static krb5_error_code +hdb_sqlite_destroy(krb5_context context, HDB *db) +{ + int ret; + hdb_sqlite_db *hsdb; + + ret = hdb_clear_master_key(context, db); + + hdb_sqlite_close_database(context, db); + + hsdb = (hdb_sqlite_db*)(db->hdb_db); + + free(hsdb->db_file); + free(db->hdb_db); + free(db); + + return ret; +} + +/* + * Not sure if this is needed. + */ +static krb5_error_code +hdb_sqlite_lock(krb5_context context, HDB *db, int operation) +{ + krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB, + "lock not implemented"); + return HDB_ERR_CANT_LOCK_DB; +} + +/* + * Not sure if this is needed. + */ +static krb5_error_code +hdb_sqlite_unlock(krb5_context context, HDB *db) +{ + krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB, + "unlock not implemented"); + return HDB_ERR_CANT_LOCK_DB; +} + +/* + * Should get the next entry, to allow iteration over all entries. + */ +static krb5_error_code +hdb_sqlite_nextkey(krb5_context context, HDB *db, unsigned flags, + hdb_entry_ex *entry) +{ + krb5_error_code ret = 0; + int sqlite_error; + krb5_data value; + + hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; + + sqlite_error = hdb_sqlite_step(context, hsdb->db, hsdb->get_all_entries); + if(sqlite_error == SQLITE_ROW) { + /* Found an entry */ + value.length = sqlite3_column_bytes(hsdb->get_all_entries, 0); + value.data = (void *) sqlite3_column_blob(hsdb->get_all_entries, 0); + memset(entry, 0, sizeof(*entry)); + ret = hdb_value2entry(context, &value, &entry->entry); + } + else if(sqlite_error == SQLITE_DONE) { + /* No more entries */ + ret = HDB_ERR_NOENTRY; + sqlite3_reset(hsdb->get_all_entries); + } + else { + /* XXX SQLite error. Should be handled in some way. */ + ret = EINVAL; + } + + return ret; +} + +/* + * Should get the first entry in the database. + * What is flags used for? + */ +static krb5_error_code +hdb_sqlite_firstkey(krb5_context context, HDB *db, unsigned flags, + hdb_entry_ex *entry) +{ + hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; + krb5_error_code ret; + + sqlite3_reset(hsdb->get_all_entries); + + ret = hdb_sqlite_nextkey(context, db, flags, entry); + if(ret) + return ret; + + return 0; +} + +/* + * Renames the database file. + */ +static krb5_error_code +hdb_sqlite_rename(krb5_context context, HDB *db, const char *new_name) +{ + hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; + int ret; + + krb5_warnx(context, "hdb_sqlite_rename"); + + if (strncasecmp(new_name, "sqlite:", 7) == 0) + new_name += 7; + + hdb_sqlite_close_database(context, db); + + ret = rename(hsdb->db_file, new_name); + free(hsdb->db_file); + + hdb_sqlite_make_database(context, db, new_name); + + return ret; +} + +/* + * Removes a principal, including aliases and associated entry. + */ +static krb5_error_code +hdb_sqlite_remove(krb5_context context, HDB *db, + krb5_const_principal principal) +{ + krb5_error_code ret; + char *principal_string; + hdb_sqlite_db *hsdb = (hdb_sqlite_db*)(db->hdb_db); + sqlite3_stmt *remove = hsdb->remove; + + ret = krb5_unparse_name(context, principal, &principal_string); + if (ret) { + free(principal_string); + return ret; + } + + sqlite3_bind_text(remove, 1, principal_string, -1, SQLITE_STATIC); + + ret = hdb_sqlite_step(context, hsdb->db, remove); + if (ret != SQLITE_DONE) { + ret = EINVAL; + krb5_set_error_message(context, ret, + "sqlite remove failed: %d", + ret); + } else + ret = 0; + + sqlite3_clear_bindings(remove); + sqlite3_reset(remove); + + return ret; +} + +/** + * Create SQLITE object, and creates the on disk database if its doesn't exists. + * + * @param context A Kerberos 5 context. + * @param db a returned database handle. + * @param argument filename + * + * @return 0 on success, an error code if not + */ + +krb5_error_code +hdb_sqlite_create(krb5_context context, HDB **db, const char *argument) +{ + krb5_error_code ret; + hdb_sqlite_db *hsdb; + + *db = calloc(1, sizeof (**db)); + if (*db == NULL) + return krb5_enomem(context); + + hsdb = (hdb_sqlite_db*) calloc(1, sizeof (*hsdb)); + if (hsdb == NULL) { + free(*db); + *db = NULL; + return krb5_enomem(context); + } + + (*db)->hdb_db = hsdb; + + /* XXX make_database should make sure everything else is freed on error */ + ret = hdb_sqlite_make_database(context, *db, argument); + if (ret) { + free((*db)->hdb_db); + free(*db); + + return ret; + } + + (*db)->hdb_master_key_set = 0; + (*db)->hdb_openp = 0; + (*db)->hdb_capability_flags = 0; + + (*db)->hdb_open = hdb_sqlite_open; + (*db)->hdb_close = hdb_sqlite_close; + + (*db)->hdb_lock = hdb_sqlite_lock; + (*db)->hdb_unlock = hdb_sqlite_unlock; + (*db)->hdb_firstkey = hdb_sqlite_firstkey; + (*db)->hdb_nextkey = hdb_sqlite_nextkey; + (*db)->hdb_fetch_kvno = hdb_sqlite_fetch_kvno; + (*db)->hdb_store = hdb_sqlite_store; + (*db)->hdb_remove = hdb_sqlite_remove; + (*db)->hdb_destroy = hdb_sqlite_destroy; + (*db)->hdb_rename = hdb_sqlite_rename; + (*db)->hdb__get = NULL; + (*db)->hdb__put = NULL; + (*db)->hdb__del = NULL; + + return 0; +} |