1226031Sstas/* 2226031Sstas * Copyright (c) 2009 Kungliga Tekniska H�gskolan 3226031Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4226031Sstas * All rights reserved. 5226031Sstas * 6226031Sstas * Redistribution and use in source and binary forms, with or without 7226031Sstas * modification, are permitted provided that the following conditions 8226031Sstas * are met: 9226031Sstas * 10226031Sstas * 1. Redistributions of source code must retain the above copyright 11226031Sstas * notice, this list of conditions and the following disclaimer. 12226031Sstas * 13226031Sstas * 2. Redistributions in binary form must reproduce the above copyright 14226031Sstas * notice, this list of conditions and the following disclaimer in the 15226031Sstas * documentation and/or other materials provided with the distribution. 16226031Sstas * 17226031Sstas * 3. Neither the name of the Institute nor the names of its contributors 18226031Sstas * may be used to endorse or promote products derived from this software 19226031Sstas * without specific prior written permission. 20226031Sstas * 21226031Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24226031Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31226031Sstas * SUCH DAMAGE. 32226031Sstas */ 33226031Sstas 34226031Sstas#include "hdb_locl.h" 35226031Sstas#include "sqlite3.h" 36226031Sstas 37226031Sstas#define MAX_RETRIES 10 38226031Sstas 39226031Sstastypedef struct hdb_sqlite_db { 40226031Sstas double version; 41226031Sstas sqlite3 *db; 42226031Sstas char *db_file; 43226031Sstas 44226031Sstas sqlite3_stmt *get_version; 45226031Sstas sqlite3_stmt *fetch; 46226031Sstas sqlite3_stmt *get_ids; 47226031Sstas sqlite3_stmt *add_entry; 48226031Sstas sqlite3_stmt *add_principal; 49226031Sstas sqlite3_stmt *add_alias; 50226031Sstas sqlite3_stmt *delete_aliases; 51226031Sstas sqlite3_stmt *update_entry; 52226031Sstas sqlite3_stmt *remove; 53226031Sstas sqlite3_stmt *get_all_entries; 54226031Sstas 55226031Sstas} hdb_sqlite_db; 56226031Sstas 57226031Sstas/* This should be used to mark updates which make the code incompatible 58226031Sstas * with databases created with previous versions. Don't update it if 59226031Sstas * compatibility is not broken. */ 60226031Sstas#define HDBSQLITE_VERSION 0.1 61226031Sstas 62226031Sstas#define _HDBSQLITE_STRINGIFY(x) #x 63226031Sstas#define HDBSQLITE_STRINGIFY(x) _HDBSQLITE_STRINGIFY(x) 64226031Sstas 65226031Sstas#define HDBSQLITE_CREATE_TABLES \ 66226031Sstas " BEGIN TRANSACTION;" \ 67226031Sstas " CREATE TABLE Version (number REAL);" \ 68226031Sstas " INSERT INTO Version (number)" \ 69226031Sstas " VALUES (" HDBSQLITE_STRINGIFY(HDBSQLITE_VERSION) ");" \ 70226031Sstas " CREATE TABLE Principal" \ 71226031Sstas " (id INTEGER PRIMARY KEY," \ 72226031Sstas " principal TEXT UNIQUE NOT NULL," \ 73226031Sstas " canonical INTEGER," \ 74226031Sstas " entry INTEGER);" \ 75226031Sstas " CREATE TABLE Entry" \ 76226031Sstas " (id INTEGER PRIMARY KEY," \ 77226031Sstas " data BLOB);" \ 78226031Sstas " COMMIT" 79226031Sstas#define HDBSQLITE_CREATE_TRIGGERS \ 80226031Sstas " CREATE TRIGGER remove_principals AFTER DELETE ON Entry" \ 81226031Sstas " BEGIN" \ 82226031Sstas " DELETE FROM Principal" \ 83226031Sstas " WHERE entry = OLD.id;" \ 84226031Sstas " END" 85226031Sstas#define HDBSQLITE_GET_VERSION \ 86226031Sstas " SELECT number FROM Version" 87226031Sstas#define HDBSQLITE_FETCH \ 88226031Sstas " SELECT Entry.data FROM Principal, Entry" \ 89226031Sstas " WHERE Principal.principal = ? AND" \ 90226031Sstas " Entry.id = Principal.entry" 91226031Sstas#define HDBSQLITE_GET_IDS \ 92226031Sstas " SELECT id, entry FROM Principal" \ 93226031Sstas " WHERE principal = ?" 94226031Sstas#define HDBSQLITE_ADD_ENTRY \ 95226031Sstas " INSERT INTO Entry (data) VALUES (?)" 96226031Sstas#define HDBSQLITE_ADD_PRINCIPAL \ 97226031Sstas " INSERT INTO Principal (principal, entry, canonical)" \ 98226031Sstas " VALUES (?, last_insert_rowid(), 1)" 99226031Sstas#define HDBSQLITE_ADD_ALIAS \ 100226031Sstas " INSERT INTO Principal (principal, entry, canonical)" \ 101226031Sstas " VALUES(?, ?, 0)" 102226031Sstas#define HDBSQLITE_DELETE_ALIASES \ 103226031Sstas " DELETE FROM Principal" \ 104226031Sstas " WHERE entry = ? AND canonical = 0" 105226031Sstas#define HDBSQLITE_UPDATE_ENTRY \ 106226031Sstas " UPDATE Entry SET data = ?" \ 107226031Sstas " WHERE id = ?" 108226031Sstas#define HDBSQLITE_REMOVE \ 109226031Sstas " DELETE FROM ENTRY WHERE id = " \ 110226031Sstas " (SELECT entry FROM Principal" \ 111226031Sstas " WHERE principal = ?)" 112226031Sstas#define HDBSQLITE_GET_ALL_ENTRIES \ 113226031Sstas " SELECT data FROM Entry" 114226031Sstas 115226031Sstas/** 116226031Sstas * Wrapper around sqlite3_prepare_v2. 117226031Sstas * 118226031Sstas * @param context The current krb5 context 119226031Sstas * @param statement Where to store the pointer to the statement 120226031Sstas * after preparing it 121226031Sstas * @param str SQL code for the statement 122226031Sstas * 123226031Sstas * @return 0 if OK, an error code if not 124226031Sstas */ 125226031Sstasstatic krb5_error_code 126226031Sstashdb_sqlite_prepare_stmt(krb5_context context, 127226031Sstas sqlite3 *db, 128226031Sstas sqlite3_stmt **statement, 129226031Sstas const char *str) 130226031Sstas{ 131226031Sstas int ret, tries = 0; 132226031Sstas 133226031Sstas ret = sqlite3_prepare_v2(db, str, -1, statement, NULL); 134226031Sstas while((tries++ < MAX_RETRIES) && 135226031Sstas ((ret == SQLITE_BUSY) || 136226031Sstas (ret == SQLITE_IOERR_BLOCKED) || 137226031Sstas (ret == SQLITE_LOCKED))) { 138226031Sstas krb5_warnx(context, "hdb-sqlite: prepare busy"); 139226031Sstas sleep(1); 140226031Sstas ret = sqlite3_prepare_v2(db, str, -1, statement, NULL); 141226031Sstas } 142226031Sstas 143226031Sstas if (ret != SQLITE_OK) { 144226031Sstas krb5_set_error_message(context, EINVAL, 145226031Sstas "Failed to prepare stmt %s: %s", 146226031Sstas str, sqlite3_errmsg(db)); 147226031Sstas return EINVAL; 148226031Sstas } 149226031Sstas 150226031Sstas return 0; 151226031Sstas} 152226031Sstas 153226031Sstas/** 154226031Sstas * A wrapper around sqlite3_exec. 155226031Sstas * 156226031Sstas * @param context The current krb5 context 157226031Sstas * @param database An open sqlite3 database handle 158226031Sstas * @param statement SQL code to execute 159226031Sstas * @param error_code What to return if the statement fails 160226031Sstas * 161226031Sstas * @return 0 if OK, else error_code 162226031Sstas */ 163226031Sstasstatic krb5_error_code 164226031Sstashdb_sqlite_exec_stmt(krb5_context context, 165226031Sstas sqlite3 *database, 166226031Sstas const char *statement, 167226031Sstas krb5_error_code error_code) 168226031Sstas{ 169226031Sstas int ret; 170226031Sstas 171226031Sstas ret = sqlite3_exec(database, statement, NULL, NULL, NULL); 172226031Sstas 173226031Sstas while(((ret == SQLITE_BUSY) || 174226031Sstas (ret == SQLITE_IOERR_BLOCKED) || 175226031Sstas (ret == SQLITE_LOCKED))) { 176226031Sstas krb5_warnx(context, "hdb-sqlite: exec busy: %d", (int)getpid()); 177226031Sstas sleep(1); 178226031Sstas ret = sqlite3_exec(database, statement, NULL, NULL, NULL); 179226031Sstas } 180226031Sstas 181226031Sstas if (ret != SQLITE_OK && error_code) { 182226031Sstas krb5_set_error_message(context, error_code, 183226031Sstas "Execute %s: %s", statement, 184226031Sstas sqlite3_errmsg(database)); 185226031Sstas return error_code; 186226031Sstas } 187226031Sstas 188226031Sstas return 0; 189226031Sstas} 190226031Sstas 191226031Sstas/** 192226031Sstas * Opens an sqlite3 database handle to a file, may create the 193226031Sstas * database file depending on flags. 194226031Sstas * 195226031Sstas * @param context The current krb5 context 196226031Sstas * @param db Heimdal database handle 197226031Sstas * @param flags Controls whether or not the file may be created, 198226031Sstas * may be 0 or SQLITE_OPEN_CREATE 199226031Sstas */ 200226031Sstasstatic krb5_error_code 201226031Sstashdb_sqlite_open_database(krb5_context context, HDB *db, int flags) 202226031Sstas{ 203226031Sstas int ret; 204226031Sstas hdb_sqlite_db *hsdb = (hdb_sqlite_db*) db->hdb_db; 205226031Sstas 206226031Sstas ret = sqlite3_open_v2(hsdb->db_file, &hsdb->db, 207226031Sstas SQLITE_OPEN_READWRITE | flags, NULL); 208226031Sstas 209226031Sstas if (ret) { 210226031Sstas if (hsdb->db) { 211226031Sstas ret = ENOENT; 212226031Sstas krb5_set_error_message(context, ret, 213226031Sstas "Error opening sqlite database %s: %s", 214226031Sstas hsdb->db_file, sqlite3_errmsg(hsdb->db)); 215226031Sstas sqlite3_close(hsdb->db); 216226031Sstas hsdb->db = NULL; 217226031Sstas } else 218226031Sstas ret = krb5_enomem(context); 219226031Sstas return ret; 220226031Sstas } 221226031Sstas 222226031Sstas return 0; 223226031Sstas} 224226031Sstas 225226031Sstasstatic int 226226031Sstashdb_sqlite_step(krb5_context context, sqlite3 *db, sqlite3_stmt *stmt) 227226031Sstas{ 228226031Sstas int ret; 229226031Sstas 230226031Sstas ret = sqlite3_step(stmt); 231226031Sstas while(((ret == SQLITE_BUSY) || 232226031Sstas (ret == SQLITE_IOERR_BLOCKED) || 233226031Sstas (ret == SQLITE_LOCKED))) { 234226031Sstas krb5_warnx(context, "hdb-sqlite: step busy: %d", (int)getpid()); 235226031Sstas sleep(1); 236226031Sstas ret = sqlite3_step(stmt); 237226031Sstas } 238226031Sstas return ret; 239226031Sstas} 240226031Sstas 241226031Sstas/** 242226031Sstas * Closes the database and frees memory allocated for statements. 243226031Sstas * 244226031Sstas * @param context The current krb5 context 245226031Sstas * @param db Heimdal database handle 246226031Sstas */ 247226031Sstasstatic krb5_error_code 248226031Sstashdb_sqlite_close_database(krb5_context context, HDB *db) 249226031Sstas{ 250226031Sstas hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; 251226031Sstas 252226031Sstas sqlite3_finalize(hsdb->get_version); 253226031Sstas sqlite3_finalize(hsdb->fetch); 254226031Sstas sqlite3_finalize(hsdb->get_ids); 255226031Sstas sqlite3_finalize(hsdb->add_entry); 256226031Sstas sqlite3_finalize(hsdb->add_principal); 257226031Sstas sqlite3_finalize(hsdb->add_alias); 258226031Sstas sqlite3_finalize(hsdb->delete_aliases); 259226031Sstas sqlite3_finalize(hsdb->update_entry); 260226031Sstas sqlite3_finalize(hsdb->remove); 261226031Sstas sqlite3_finalize(hsdb->get_all_entries); 262226031Sstas 263226031Sstas sqlite3_close(hsdb->db); 264226031Sstas 265226031Sstas return 0; 266226031Sstas} 267226031Sstas 268226031Sstas/** 269226031Sstas * Opens an sqlite database file and prepares it for use. 270226031Sstas * If the file does not exist it will be created. 271226031Sstas * 272226031Sstas * @param context The current krb5_context 273226031Sstas * @param db The heimdal database handle 274226031Sstas * @param filename Where to store the database file 275226031Sstas * 276226031Sstas * @return 0 if everything worked, an error code if not 277226031Sstas */ 278226031Sstasstatic krb5_error_code 279226031Sstashdb_sqlite_make_database(krb5_context context, HDB *db, const char *filename) 280226031Sstas{ 281226031Sstas int ret; 282226031Sstas int created_file = 0; 283226031Sstas hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; 284226031Sstas 285226031Sstas hsdb->db_file = strdup(filename); 286226031Sstas if(hsdb->db_file == NULL) 287226031Sstas return ENOMEM; 288226031Sstas 289226031Sstas ret = hdb_sqlite_open_database(context, db, 0); 290226031Sstas if (ret) { 291226031Sstas ret = hdb_sqlite_open_database(context, db, SQLITE_OPEN_CREATE); 292226031Sstas if (ret) goto out; 293226031Sstas 294226031Sstas created_file = 1; 295226031Sstas 296226031Sstas ret = hdb_sqlite_exec_stmt(context, hsdb->db, 297226031Sstas HDBSQLITE_CREATE_TABLES, 298226031Sstas EINVAL); 299226031Sstas if (ret) goto out; 300226031Sstas 301226031Sstas ret = hdb_sqlite_exec_stmt(context, hsdb->db, 302226031Sstas HDBSQLITE_CREATE_TRIGGERS, 303226031Sstas EINVAL); 304226031Sstas if (ret) goto out; 305226031Sstas } 306226031Sstas 307226031Sstas ret = hdb_sqlite_prepare_stmt(context, hsdb->db, 308226031Sstas &hsdb->get_version, 309226031Sstas HDBSQLITE_GET_VERSION); 310226031Sstas if (ret) goto out; 311226031Sstas ret = hdb_sqlite_prepare_stmt(context, hsdb->db, 312226031Sstas &hsdb->fetch, 313226031Sstas HDBSQLITE_FETCH); 314226031Sstas if (ret) goto out; 315226031Sstas ret = hdb_sqlite_prepare_stmt(context, hsdb->db, 316226031Sstas &hsdb->get_ids, 317226031Sstas HDBSQLITE_GET_IDS); 318226031Sstas if (ret) goto out; 319226031Sstas ret = hdb_sqlite_prepare_stmt(context, hsdb->db, 320226031Sstas &hsdb->add_entry, 321226031Sstas HDBSQLITE_ADD_ENTRY); 322226031Sstas if (ret) goto out; 323226031Sstas ret = hdb_sqlite_prepare_stmt(context, hsdb->db, 324226031Sstas &hsdb->add_principal, 325226031Sstas HDBSQLITE_ADD_PRINCIPAL); 326226031Sstas if (ret) goto out; 327226031Sstas ret = hdb_sqlite_prepare_stmt(context, hsdb->db, 328226031Sstas &hsdb->add_alias, 329226031Sstas HDBSQLITE_ADD_ALIAS); 330226031Sstas if (ret) goto out; 331226031Sstas ret = hdb_sqlite_prepare_stmt(context, hsdb->db, 332226031Sstas &hsdb->delete_aliases, 333226031Sstas HDBSQLITE_DELETE_ALIASES); 334226031Sstas if (ret) goto out; 335226031Sstas ret = hdb_sqlite_prepare_stmt(context, hsdb->db, 336226031Sstas &hsdb->update_entry, 337226031Sstas HDBSQLITE_UPDATE_ENTRY); 338226031Sstas if (ret) goto out; 339226031Sstas ret = hdb_sqlite_prepare_stmt(context, hsdb->db, 340226031Sstas &hsdb->remove, 341226031Sstas HDBSQLITE_REMOVE); 342226031Sstas if (ret) goto out; 343226031Sstas ret = hdb_sqlite_prepare_stmt(context, hsdb->db, 344226031Sstas &hsdb->get_all_entries, 345226031Sstas HDBSQLITE_GET_ALL_ENTRIES); 346226031Sstas if (ret) goto out; 347226031Sstas 348226031Sstas ret = hdb_sqlite_step(context, hsdb->db, hsdb->get_version); 349226031Sstas if(ret == SQLITE_ROW) { 350226031Sstas hsdb->version = sqlite3_column_double(hsdb->get_version, 0); 351226031Sstas } 352226031Sstas sqlite3_reset(hsdb->get_version); 353226031Sstas ret = 0; 354226031Sstas 355226031Sstas if(hsdb->version != HDBSQLITE_VERSION) { 356226031Sstas ret = EINVAL; 357226031Sstas krb5_set_error_message(context, ret, "HDBSQLITE_VERSION mismatch"); 358226031Sstas } 359226031Sstas 360226031Sstas if(ret) goto out; 361226031Sstas 362226031Sstas return 0; 363226031Sstas 364226031Sstas out: 365226031Sstas if (hsdb->db) 366226031Sstas sqlite3_close(hsdb->db); 367226031Sstas if (created_file) 368226031Sstas unlink(hsdb->db_file); 369226031Sstas 370226031Sstas return ret; 371226031Sstas} 372226031Sstas 373226031Sstas/** 374226031Sstas * Retrieves an entry by searching for the given 375226031Sstas * principal in the Principal database table, both 376226031Sstas * for canonical principals and aliases. 377226031Sstas * 378226031Sstas * @param context The current krb5_context 379226031Sstas * @param db Heimdal database handle 380226031Sstas * @param principal The principal whose entry to search for 381226031Sstas * @param flags Currently only for HDB_F_DECRYPT 382226031Sstas * @param kvno kvno to fetch is HDB_F_KVNO_SPECIFIED use used 383226031Sstas * 384226031Sstas * @return 0 if everything worked, an error code if not 385226031Sstas */ 386226031Sstasstatic krb5_error_code 387226031Sstashdb_sqlite_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal, 388226031Sstas unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry) 389226031Sstas{ 390226031Sstas int sqlite_error; 391226031Sstas krb5_error_code ret; 392226031Sstas char *principal_string; 393226031Sstas hdb_sqlite_db *hsdb = (hdb_sqlite_db*)(db->hdb_db); 394226031Sstas sqlite3_stmt *fetch = hsdb->fetch; 395226031Sstas krb5_data value; 396226031Sstas 397226031Sstas ret = krb5_unparse_name(context, principal, &principal_string); 398226031Sstas if (ret) { 399226031Sstas free(principal_string); 400226031Sstas return ret; 401226031Sstas } 402226031Sstas 403226031Sstas sqlite3_bind_text(fetch, 1, principal_string, -1, SQLITE_STATIC); 404226031Sstas 405226031Sstas sqlite_error = hdb_sqlite_step(context, hsdb->db, fetch); 406226031Sstas if (sqlite_error != SQLITE_ROW) { 407226031Sstas if(sqlite_error == SQLITE_DONE) { 408226031Sstas ret = HDB_ERR_NOENTRY; 409226031Sstas goto out; 410226031Sstas } else { 411226031Sstas ret = EINVAL; 412226031Sstas krb5_set_error_message(context, ret, 413226031Sstas "sqlite fetch failed: %d", 414226031Sstas sqlite_error); 415226031Sstas goto out; 416226031Sstas } 417226031Sstas } 418226031Sstas 419226031Sstas value.length = sqlite3_column_bytes(fetch, 0); 420226031Sstas value.data = (void *) sqlite3_column_blob(fetch, 0); 421226031Sstas 422226031Sstas ret = hdb_value2entry(context, &value, &entry->entry); 423226031Sstas if(ret) 424226031Sstas goto out; 425226031Sstas 426226031Sstas if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { 427226031Sstas ret = hdb_unseal_keys(context, db, &entry->entry); 428226031Sstas if(ret) { 429226031Sstas hdb_free_entry(context, entry); 430226031Sstas goto out; 431226031Sstas } 432226031Sstas } 433226031Sstas 434226031Sstas ret = 0; 435226031Sstas 436226031Sstasout: 437226031Sstas 438226031Sstas sqlite3_clear_bindings(fetch); 439226031Sstas sqlite3_reset(fetch); 440226031Sstas 441226031Sstas free(principal_string); 442226031Sstas 443226031Sstas return ret; 444226031Sstas} 445226031Sstas 446226031Sstas/** 447226031Sstas * Convenience function to step a prepared statement with no 448226031Sstas * value once. 449226031Sstas * 450226031Sstas * @param context The current krb5_context 451226031Sstas * @param statement A prepared sqlite3 statement 452226031Sstas * 453226031Sstas * @return 0 if everything worked, an error code if not 454226031Sstas */ 455226031Sstasstatic krb5_error_code 456226031Sstashdb_sqlite_step_once(krb5_context context, HDB *db, sqlite3_stmt *statement) 457226031Sstas{ 458226031Sstas int ret; 459226031Sstas hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; 460226031Sstas 461226031Sstas ret = hdb_sqlite_step(context, hsdb->db, statement); 462226031Sstas sqlite3_clear_bindings(statement); 463226031Sstas sqlite3_reset(statement); 464226031Sstas 465226031Sstas return ret; 466226031Sstas} 467226031Sstas 468226031Sstas 469226031Sstas/** 470226031Sstas * Stores an hdb_entry in the database. If flags contains HDB_F_REPLACE 471226031Sstas * a previous entry may be replaced. 472226031Sstas * 473226031Sstas * @param context The current krb5_context 474226031Sstas * @param db Heimdal database handle 475226031Sstas * @param flags May currently only contain HDB_F_REPLACE 476226031Sstas * @param entry The data to store 477226031Sstas * 478226031Sstas * @return 0 if everything worked, an error code if not 479226031Sstas */ 480226031Sstasstatic krb5_error_code 481226031Sstashdb_sqlite_store(krb5_context context, HDB *db, unsigned flags, 482226031Sstas hdb_entry_ex *entry) 483226031Sstas{ 484226031Sstas int ret; 485226031Sstas int i; 486226031Sstas sqlite_int64 entry_id; 487226031Sstas char *principal_string = NULL; 488226031Sstas char *alias_string; 489226031Sstas const HDB_Ext_Aliases *aliases; 490226031Sstas 491226031Sstas hdb_sqlite_db *hsdb = (hdb_sqlite_db *)(db->hdb_db); 492226031Sstas krb5_data value; 493226031Sstas sqlite3_stmt *get_ids = hsdb->get_ids; 494226031Sstas 495226031Sstas ret = hdb_sqlite_exec_stmt(context, hsdb->db, 496226031Sstas "BEGIN IMMEDIATE TRANSACTION", EINVAL); 497226031Sstas if(ret != SQLITE_OK) { 498226031Sstas ret = EINVAL; 499226031Sstas krb5_set_error_message(context, ret, 500226031Sstas "SQLite BEGIN TRANSACTION failed: %s", 501226031Sstas sqlite3_errmsg(hsdb->db)); 502226031Sstas goto rollback; 503226031Sstas } 504226031Sstas 505226031Sstas ret = krb5_unparse_name(context, 506226031Sstas entry->entry.principal, &principal_string); 507226031Sstas if (ret) { 508226031Sstas goto rollback; 509226031Sstas } 510226031Sstas 511226031Sstas ret = hdb_seal_keys(context, db, &entry->entry); 512226031Sstas if(ret) { 513226031Sstas goto rollback; 514226031Sstas } 515226031Sstas 516226031Sstas ret = hdb_entry2value(context, &entry->entry, &value); 517226031Sstas if(ret) { 518226031Sstas goto rollback; 519226031Sstas } 520226031Sstas 521226031Sstas sqlite3_bind_text(get_ids, 1, principal_string, -1, SQLITE_STATIC); 522226031Sstas ret = hdb_sqlite_step(context, hsdb->db, get_ids); 523226031Sstas 524226031Sstas if(ret == SQLITE_DONE) { /* No such principal */ 525226031Sstas 526226031Sstas sqlite3_bind_blob(hsdb->add_entry, 1, 527226031Sstas value.data, value.length, SQLITE_STATIC); 528226031Sstas ret = hdb_sqlite_step(context, hsdb->db, hsdb->add_entry); 529226031Sstas sqlite3_clear_bindings(hsdb->add_entry); 530226031Sstas sqlite3_reset(hsdb->add_entry); 531226031Sstas if(ret != SQLITE_DONE) 532226031Sstas goto rollback; 533226031Sstas 534226031Sstas sqlite3_bind_text(hsdb->add_principal, 1, 535226031Sstas principal_string, -1, SQLITE_STATIC); 536226031Sstas ret = hdb_sqlite_step(context, hsdb->db, hsdb->add_principal); 537226031Sstas sqlite3_clear_bindings(hsdb->add_principal); 538226031Sstas sqlite3_reset(hsdb->add_principal); 539226031Sstas if(ret != SQLITE_DONE) 540226031Sstas goto rollback; 541226031Sstas 542226031Sstas entry_id = sqlite3_column_int64(get_ids, 1); 543226031Sstas 544226031Sstas } else if(ret == SQLITE_ROW) { /* Found a principal */ 545226031Sstas 546226031Sstas if(! (flags & HDB_F_REPLACE)) /* Not allowed to replace it */ 547226031Sstas goto rollback; 548226031Sstas 549226031Sstas entry_id = sqlite3_column_int64(get_ids, 1); 550226031Sstas 551226031Sstas sqlite3_bind_int64(hsdb->delete_aliases, 1, entry_id); 552226031Sstas ret = hdb_sqlite_step_once(context, db, hsdb->delete_aliases); 553226031Sstas if(ret != SQLITE_DONE) 554226031Sstas goto rollback; 555226031Sstas 556226031Sstas sqlite3_bind_blob(hsdb->update_entry, 1, 557226031Sstas value.data, value.length, SQLITE_STATIC); 558226031Sstas sqlite3_bind_int64(hsdb->update_entry, 2, entry_id); 559226031Sstas ret = hdb_sqlite_step_once(context, db, hsdb->update_entry); 560226031Sstas if(ret != SQLITE_DONE) 561226031Sstas goto rollback; 562226031Sstas 563226031Sstas } else { 564226031Sstas /* Error! */ 565226031Sstas goto rollback; 566226031Sstas } 567226031Sstas 568226031Sstas ret = hdb_entry_get_aliases(&entry->entry, &aliases); 569226031Sstas if(ret || aliases == NULL) 570226031Sstas goto commit; 571226031Sstas 572226031Sstas for(i = 0; i < aliases->aliases.len; i++) { 573226031Sstas 574226031Sstas ret = krb5_unparse_name(context, &aliases->aliases.val[i], 575226031Sstas &alias_string); 576226031Sstas if (ret) { 577226031Sstas free(alias_string); 578226031Sstas goto rollback; 579226031Sstas } 580226031Sstas 581226031Sstas sqlite3_bind_text(hsdb->add_alias, 1, alias_string, 582226031Sstas -1, SQLITE_STATIC); 583226031Sstas sqlite3_bind_int64(hsdb->add_alias, 2, entry_id); 584226031Sstas ret = hdb_sqlite_step_once(context, db, hsdb->add_alias); 585226031Sstas 586226031Sstas free(alias_string); 587226031Sstas 588226031Sstas if(ret != SQLITE_DONE) 589226031Sstas goto rollback; 590226031Sstas } 591226031Sstas 592226031Sstas ret = 0; 593226031Sstas 594226031Sstascommit: 595226031Sstas 596226031Sstas free(principal_string); 597226031Sstas 598226031Sstas krb5_data_free(&value); 599226031Sstas 600226031Sstas sqlite3_clear_bindings(get_ids); 601226031Sstas sqlite3_reset(get_ids); 602226031Sstas 603226031Sstas ret = hdb_sqlite_exec_stmt(context, hsdb->db, "COMMIT", EINVAL); 604226031Sstas if(ret != SQLITE_OK) 605226031Sstas krb5_warnx(context, "hdb-sqlite: COMMIT problem: %d: %s", 606226031Sstas ret, sqlite3_errmsg(hsdb->db)); 607226031Sstas 608226031Sstas return ret; 609226031Sstas 610226031Sstasrollback: 611226031Sstas 612226031Sstas krb5_warnx(context, "hdb-sqlite: store rollback problem: %d: %s", 613226031Sstas ret, sqlite3_errmsg(hsdb->db)); 614226031Sstas 615226031Sstas free(principal_string); 616226031Sstas 617226031Sstas ret = hdb_sqlite_exec_stmt(context, hsdb->db, 618226031Sstas "ROLLBACK", EINVAL); 619226031Sstas return ret; 620226031Sstas} 621226031Sstas 622226031Sstas/** 623226031Sstas * This may be called often by other code, since the BDB backends 624226031Sstas * can not have several open connections. SQLite can handle 625226031Sstas * many processes with open handles to the database file 626226031Sstas * and closing/opening the handle is an expensive operation. 627226031Sstas * Hence, this function does nothing. 628226031Sstas * 629226031Sstas * @param context The current krb5 context 630226031Sstas * @param db Heimdal database handle 631226031Sstas * 632226031Sstas * @return Always returns 0 633226031Sstas */ 634226031Sstasstatic krb5_error_code 635226031Sstashdb_sqlite_close(krb5_context context, HDB *db) 636226031Sstas{ 637226031Sstas return 0; 638226031Sstas} 639226031Sstas 640226031Sstas/** 641226031Sstas * The opposite of hdb_sqlite_close. Since SQLite accepts 642226031Sstas * many open handles to the database file the handle does not 643226031Sstas * need to be closed, or reopened. 644226031Sstas * 645226031Sstas * @param context The current krb5 context 646226031Sstas * @param db Heimdal database handle 647226031Sstas * @param flags 648226031Sstas * @param mode_t 649226031Sstas * 650226031Sstas * @return Always returns 0 651226031Sstas */ 652226031Sstasstatic krb5_error_code 653226031Sstashdb_sqlite_open(krb5_context context, HDB *db, int flags, mode_t mode) 654226031Sstas{ 655226031Sstas return 0; 656226031Sstas} 657226031Sstas 658226031Sstas/** 659226031Sstas * Closes the databse and frees all resources. 660226031Sstas * 661226031Sstas * @param context The current krb5 context 662226031Sstas * @param db Heimdal database handle 663226031Sstas * 664226031Sstas * @return 0 on success, an error code if not 665226031Sstas */ 666226031Sstasstatic krb5_error_code 667226031Sstashdb_sqlite_destroy(krb5_context context, HDB *db) 668226031Sstas{ 669226031Sstas int ret; 670226031Sstas hdb_sqlite_db *hsdb; 671226031Sstas 672226031Sstas ret = hdb_clear_master_key(context, db); 673226031Sstas 674226031Sstas hdb_sqlite_close_database(context, db); 675226031Sstas 676226031Sstas hsdb = (hdb_sqlite_db*)(db->hdb_db); 677226031Sstas 678226031Sstas free(hsdb->db_file); 679226031Sstas free(db->hdb_db); 680226031Sstas free(db); 681226031Sstas 682226031Sstas return ret; 683226031Sstas} 684226031Sstas 685226031Sstas/* 686226031Sstas * Not sure if this is needed. 687226031Sstas */ 688226031Sstasstatic krb5_error_code 689226031Sstashdb_sqlite_lock(krb5_context context, HDB *db, int operation) 690226031Sstas{ 691226031Sstas krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB, 692226031Sstas "lock not implemented"); 693226031Sstas return HDB_ERR_CANT_LOCK_DB; 694226031Sstas} 695226031Sstas 696226031Sstas/* 697226031Sstas * Not sure if this is needed. 698226031Sstas */ 699226031Sstasstatic krb5_error_code 700226031Sstashdb_sqlite_unlock(krb5_context context, HDB *db) 701226031Sstas{ 702226031Sstas krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB, 703226031Sstas "unlock not implemented"); 704226031Sstas return HDB_ERR_CANT_LOCK_DB; 705226031Sstas} 706226031Sstas 707226031Sstas/* 708226031Sstas * Should get the next entry, to allow iteration over all entries. 709226031Sstas */ 710226031Sstasstatic krb5_error_code 711226031Sstashdb_sqlite_nextkey(krb5_context context, HDB *db, unsigned flags, 712226031Sstas hdb_entry_ex *entry) 713226031Sstas{ 714226031Sstas krb5_error_code ret = 0; 715226031Sstas int sqlite_error; 716226031Sstas krb5_data value; 717226031Sstas 718226031Sstas hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; 719226031Sstas 720226031Sstas sqlite_error = hdb_sqlite_step(context, hsdb->db, hsdb->get_all_entries); 721226031Sstas if(sqlite_error == SQLITE_ROW) { 722226031Sstas /* Found an entry */ 723226031Sstas value.length = sqlite3_column_bytes(hsdb->get_all_entries, 0); 724226031Sstas value.data = (void *) sqlite3_column_blob(hsdb->get_all_entries, 0); 725226031Sstas memset(entry, 0, sizeof(*entry)); 726226031Sstas ret = hdb_value2entry(context, &value, &entry->entry); 727226031Sstas } 728226031Sstas else if(sqlite_error == SQLITE_DONE) { 729226031Sstas /* No more entries */ 730226031Sstas ret = HDB_ERR_NOENTRY; 731226031Sstas sqlite3_reset(hsdb->get_all_entries); 732226031Sstas } 733226031Sstas else { 734226031Sstas /* XXX SQLite error. Should be handled in some way. */ 735226031Sstas ret = EINVAL; 736226031Sstas } 737226031Sstas 738226031Sstas return ret; 739226031Sstas} 740226031Sstas 741226031Sstas/* 742226031Sstas * Should get the first entry in the database. 743226031Sstas * What is flags used for? 744226031Sstas */ 745226031Sstasstatic krb5_error_code 746226031Sstashdb_sqlite_firstkey(krb5_context context, HDB *db, unsigned flags, 747226031Sstas hdb_entry_ex *entry) 748226031Sstas{ 749226031Sstas hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; 750226031Sstas krb5_error_code ret; 751226031Sstas 752226031Sstas sqlite3_reset(hsdb->get_all_entries); 753226031Sstas 754226031Sstas ret = hdb_sqlite_nextkey(context, db, flags, entry); 755226031Sstas if(ret) 756226031Sstas return ret; 757226031Sstas 758226031Sstas return 0; 759226031Sstas} 760226031Sstas 761226031Sstas/* 762226031Sstas * Renames the database file. 763226031Sstas */ 764226031Sstasstatic krb5_error_code 765226031Sstashdb_sqlite_rename(krb5_context context, HDB *db, const char *new_name) 766226031Sstas{ 767226031Sstas hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db; 768226031Sstas int ret; 769226031Sstas 770226031Sstas krb5_warnx(context, "hdb_sqlite_rename"); 771226031Sstas 772226031Sstas if (strncasecmp(new_name, "sqlite:", 7) == 0) 773226031Sstas new_name += 7; 774226031Sstas 775226031Sstas hdb_sqlite_close_database(context, db); 776226031Sstas 777226031Sstas ret = rename(hsdb->db_file, new_name); 778226031Sstas free(hsdb->db_file); 779226031Sstas 780226031Sstas hdb_sqlite_make_database(context, db, new_name); 781226031Sstas 782226031Sstas return ret; 783226031Sstas} 784226031Sstas 785226031Sstas/* 786226031Sstas * Removes a principal, including aliases and associated entry. 787226031Sstas */ 788226031Sstasstatic krb5_error_code 789226031Sstashdb_sqlite_remove(krb5_context context, HDB *db, 790226031Sstas krb5_const_principal principal) 791226031Sstas{ 792226031Sstas krb5_error_code ret; 793226031Sstas char *principal_string; 794226031Sstas hdb_sqlite_db *hsdb = (hdb_sqlite_db*)(db->hdb_db); 795226031Sstas sqlite3_stmt *remove = hsdb->remove; 796226031Sstas 797226031Sstas ret = krb5_unparse_name(context, principal, &principal_string); 798226031Sstas if (ret) { 799226031Sstas free(principal_string); 800226031Sstas return ret; 801226031Sstas } 802226031Sstas 803226031Sstas sqlite3_bind_text(remove, 1, principal_string, -1, SQLITE_STATIC); 804226031Sstas 805226031Sstas ret = hdb_sqlite_step(context, hsdb->db, remove); 806226031Sstas if (ret != SQLITE_DONE) { 807226031Sstas ret = EINVAL; 808226031Sstas krb5_set_error_message(context, ret, 809226031Sstas "sqlite remove failed: %d", 810226031Sstas ret); 811226031Sstas } else 812226031Sstas ret = 0; 813226031Sstas 814226031Sstas sqlite3_clear_bindings(remove); 815226031Sstas sqlite3_reset(remove); 816226031Sstas 817226031Sstas return ret; 818226031Sstas} 819226031Sstas 820226031Sstas/** 821226031Sstas * Create SQLITE object, and creates the on disk database if its doesn't exists. 822226031Sstas * 823226031Sstas * @param context A Kerberos 5 context. 824226031Sstas * @param db a returned database handle. 825226031Sstas * @param argument filename 826226031Sstas * 827226031Sstas * @return 0 on success, an error code if not 828226031Sstas */ 829226031Sstas 830226031Sstaskrb5_error_code 831226031Sstashdb_sqlite_create(krb5_context context, HDB **db, const char *argument) 832226031Sstas{ 833226031Sstas krb5_error_code ret; 834226031Sstas hdb_sqlite_db *hsdb; 835226031Sstas 836226031Sstas *db = calloc(1, sizeof (**db)); 837226031Sstas if (*db == NULL) 838226031Sstas return krb5_enomem(context); 839226031Sstas 840226031Sstas hsdb = (hdb_sqlite_db*) calloc(1, sizeof (*hsdb)); 841226031Sstas if (hsdb == NULL) { 842226031Sstas free(*db); 843226031Sstas *db = NULL; 844226031Sstas return krb5_enomem(context); 845226031Sstas } 846226031Sstas 847226031Sstas (*db)->hdb_db = hsdb; 848226031Sstas 849226031Sstas /* XXX make_database should make sure everything else is freed on error */ 850226031Sstas ret = hdb_sqlite_make_database(context, *db, argument); 851226031Sstas if (ret) { 852226031Sstas free((*db)->hdb_db); 853226031Sstas free(*db); 854226031Sstas 855226031Sstas return ret; 856226031Sstas } 857226031Sstas 858226031Sstas (*db)->hdb_master_key_set = 0; 859226031Sstas (*db)->hdb_openp = 0; 860226031Sstas (*db)->hdb_capability_flags = 0; 861226031Sstas 862226031Sstas (*db)->hdb_open = hdb_sqlite_open; 863226031Sstas (*db)->hdb_close = hdb_sqlite_close; 864226031Sstas 865226031Sstas (*db)->hdb_lock = hdb_sqlite_lock; 866226031Sstas (*db)->hdb_unlock = hdb_sqlite_unlock; 867226031Sstas (*db)->hdb_firstkey = hdb_sqlite_firstkey; 868226031Sstas (*db)->hdb_nextkey = hdb_sqlite_nextkey; 869226031Sstas (*db)->hdb_fetch_kvno = hdb_sqlite_fetch_kvno; 870226031Sstas (*db)->hdb_store = hdb_sqlite_store; 871226031Sstas (*db)->hdb_remove = hdb_sqlite_remove; 872226031Sstas (*db)->hdb_destroy = hdb_sqlite_destroy; 873226031Sstas (*db)->hdb_rename = hdb_sqlite_rename; 874226031Sstas (*db)->hdb__get = NULL; 875226031Sstas (*db)->hdb__put = NULL; 876226031Sstas (*db)->hdb__del = NULL; 877226031Sstas 878226031Sstas return 0; 879226031Sstas} 880