/* * Removes a principal, including aliases and associated entry. */ static krb5_error_code hdb_sqlite_remove(krb5_context context, HDB *db, unsigned flags, krb5_const_principal principal) { krb5_error_code ret; hdb_sqlite_db *hsdb = (hdb_sqlite_db*)(db->hdb_db); sqlite3_stmt *get_ids = hsdb->get_ids; sqlite3_stmt *rm = hsdb->remove; bind_principal(context, principal, rm, 1); ret = hdb_sqlite_exec_stmt(context, hsdb, "BEGIN IMMEDIATE TRANSACTION", HDB_ERR_UK_SERROR); if (ret != SQLITE_OK) { ret = HDB_ERR_UK_SERROR; (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0); krb5_set_error_message(context, ret, "SQLite BEGIN TRANSACTION failed: %s", sqlite3_errmsg(hsdb->db)); return ret; } if ((flags & HDB_F_PRECHECK)) { ret = bind_principal(context, principal, get_ids, 1); if (ret) return ret; ret = hdb_sqlite_step(context, hsdb->db, get_ids); sqlite3_clear_bindings(get_ids); sqlite3_reset(get_ids); if (ret == SQLITE_DONE) { (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0); return HDB_ERR_NOENTRY; } } ret = hdb_sqlite_step(context, hsdb->db, rm); sqlite3_clear_bindings(rm); sqlite3_reset(rm); if (ret != SQLITE_DONE) { (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0); ret = HDB_ERR_UK_SERROR; krb5_set_error_message(context, ret, "sqlite remove failed: %d", ret); return ret; } if ((flags & HDB_F_PRECHECK)) { (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0); return 0; } ret = hdb_sqlite_exec_stmt(context, hsdb, "COMMIT", HDB_ERR_UK_SERROR); if (ret != SQLITE_OK) krb5_warnx(context, "hdb-sqlite: COMMIT problem: %ld: %s", (long)HDB_ERR_UK_SERROR, sqlite3_errmsg(hsdb->db)); return 0; }
/* * 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) { krb5_set_error_string(context, "sqlite remove failed: %d", ret); } else ret = 0; sqlite3_clear_bindings(remove); sqlite3_reset(remove); return ret; }
/* * 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 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 { ret = HDB_ERR_UK_RERROR; krb5_set_error_message(context, HDB_ERR_UK_RERROR, "SELECT failed after returning one or " "more rows: %s", sqlite3_errmsg(hsdb->db)); } 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 * * @return 0 if everything worked, an error code if not */ static krb5_error_code hdb_sqlite_fetch(krb5_context context, HDB *db, krb5_const_principal principal, unsigned flags, 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 { krb5_set_error_string(context, "sqlite fetch failed: %d", sqlite_error); ret = EINVAL; 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; } } 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; ret = 0; out: sqlite3_clear_bindings(fetch); sqlite3_reset(fetch); free(principal_string); return ret; }
/** * 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, HDBSQLITE_CREATE_TABLES, HDB_ERR_UK_SERROR); if (ret) goto out; ret = hdb_sqlite_exec_stmt(context, hsdb, HDBSQLITE_CREATE_TRIGGERS, HDB_ERR_UK_SERROR); if (ret) goto out; } ret = prep_stmts(context, hsdb); 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 = HDB_ERR_UK_SERROR; 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); free(hsdb->db_file); hsdb->db_file = NULL; 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) { krb5_set_error_string(context, "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; }
/** * 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) { krb5_set_error_string(context, "HDBSQLITE_VERSION mismatch"); ret = EINVAL; } if(ret) goto out; return 0; out: if (hsdb->db) sqlite3_close(hsdb->db); if (created_file) unlink(hsdb->db_file); 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; 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; krb5_data_zero(&value); ret = hdb_sqlite_exec_stmt(context, hsdb, "BEGIN IMMEDIATE TRANSACTION", HDB_ERR_UK_SERROR); if(ret != SQLITE_OK) { ret = HDB_ERR_UK_SERROR; krb5_set_error_message(context, ret, "SQLite BEGIN TRANSACTION failed: %s", sqlite3_errmsg(hsdb->db)); 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; } ret = bind_principal(context, entry->entry.principal, get_ids, 1); if (ret) goto rollback; 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 && ret != SQLITE_CONSTRAINT) { ret = HDB_ERR_UK_SERROR; goto rollback; } if (ret == SQLITE_CONSTRAINT) { ret = HDB_ERR_EXISTS; goto rollback; } ret = 0; ret = bind_principal(context, entry->entry.principal, hsdb->add_principal, 1); if (ret) goto rollback; 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 && ret != SQLITE_CONSTRAINT) { ret = HDB_ERR_UK_SERROR; goto rollback; } if (ret == SQLITE_CONSTRAINT) { ret = HDB_ERR_EXISTS; goto rollback; } /* Now let's learn what Entry ID we got for the new principal */ sqlite3_reset(get_ids); ret = hdb_sqlite_step(context, hsdb->db, get_ids); if (ret != SQLITE_ROW) { ret = HDB_ERR_UK_SERROR; goto rollback; } entry_id = sqlite3_column_int64(get_ids, 1); ret = 0; } 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) { ret = HDB_ERR_UK_SERROR; 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) { ret = HDB_ERR_UK_SERROR; goto rollback; } } else { /* Error! */ ret = HDB_ERR_UK_SERROR; 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 = bind_principal(context, &aliases->aliases.val[i], hsdb->add_alias, 1); if (ret) goto rollback; sqlite3_bind_int64(hsdb->add_alias, 2, entry_id); ret = hdb_sqlite_step_once(context, db, hsdb->add_alias); if (ret == SQLITE_CONSTRAINT) { ret = HDB_ERR_EXISTS; goto rollback; } if (ret != SQLITE_DONE) { ret = HDB_ERR_UK_SERROR; goto rollback; } } commit: krb5_data_free(&value); sqlite3_clear_bindings(get_ids); sqlite3_reset(get_ids); if ((flags & HDB_F_PRECHECK)) { (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0); return 0; } ret = hdb_sqlite_exec_stmt(context, hsdb, "COMMIT", HDB_ERR_UK_SERROR); if(ret != SQLITE_OK) krb5_warnx(context, "hdb-sqlite: COMMIT problem: %ld: %s", (long)HDB_ERR_UK_SERROR, sqlite3_errmsg(hsdb->db)); return ret == SQLITE_OK ? 0 : HDB_ERR_UK_SERROR; rollback: krb5_data_free(&value); sqlite3_clear_bindings(get_ids); sqlite3_reset(get_ids); krb5_warnx(context, "hdb-sqlite: store rollback problem: %d: %s", ret, sqlite3_errmsg(hsdb->db)); (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0); 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; hdb_sqlite_db *hsdb = (hdb_sqlite_db*)(db->hdb_db); sqlite3_stmt *fetch = hsdb->fetch; krb5_data value; krb5_principal enterprise_principal = NULL; if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { if (principal->name.name_string.len != 1) { ret = KRB5_PARSE_MALFORMED; krb5_set_error_message(context, ret, "malformed principal: " "enterprise name with %d name components", principal->name.name_string.len); return ret; } ret = krb5_parse_name(context, principal->name.name_string.val[0], &enterprise_principal); if (ret) return ret; principal = enterprise_principal; } ret = bind_principal(context, principal, fetch, 1); if (ret) return ret; krb5_free_principal(context, enterprise_principal); 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 = HDB_ERR_UK_RERROR; 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); return ret; }