1/*	$NetBSD: hdb-sqlite.c,v 1.1.1.1 2011/04/13 18:14:42 elric Exp $	*/
2
3/*
4 * Copyright (c) 2009 Kungliga Tekniska H�gskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "hdb_locl.h"
37#include "sqlite3.h"
38
39#define MAX_RETRIES 10
40
41typedef struct hdb_sqlite_db {
42    double version;
43    sqlite3 *db;
44    char *db_file;
45
46    sqlite3_stmt *get_version;
47    sqlite3_stmt *fetch;
48    sqlite3_stmt *get_ids;
49    sqlite3_stmt *add_entry;
50    sqlite3_stmt *add_principal;
51    sqlite3_stmt *add_alias;
52    sqlite3_stmt *delete_aliases;
53    sqlite3_stmt *update_entry;
54    sqlite3_stmt *remove;
55    sqlite3_stmt *get_all_entries;
56
57} hdb_sqlite_db;
58
59/* This should be used to mark updates which make the code incompatible
60 * with databases created with previous versions. Don't update it if
61 * compatibility is not broken. */
62#define HDBSQLITE_VERSION 0.1
63
64#define _HDBSQLITE_STRINGIFY(x) #x
65#define HDBSQLITE_STRINGIFY(x) _HDBSQLITE_STRINGIFY(x)
66
67#define HDBSQLITE_CREATE_TABLES \
68                 " BEGIN TRANSACTION;" \
69                 " CREATE TABLE Version (number REAL);" \
70                 " INSERT INTO Version (number)" \
71                 " VALUES (" HDBSQLITE_STRINGIFY(HDBSQLITE_VERSION) ");" \
72                 " CREATE TABLE Principal" \
73                 "  (id INTEGER PRIMARY KEY," \
74                 "   principal TEXT UNIQUE NOT NULL," \
75                 "   canonical INTEGER," \
76                 "   entry INTEGER);" \
77                 " CREATE TABLE Entry" \
78                 "  (id INTEGER PRIMARY KEY," \
79                 "   data BLOB);" \
80                 " COMMIT"
81#define HDBSQLITE_CREATE_TRIGGERS \
82                 " CREATE TRIGGER remove_principals AFTER DELETE ON Entry" \
83                 " BEGIN" \
84                 "  DELETE FROM Principal" \
85                 "  WHERE entry = OLD.id;" \
86                 " END"
87#define HDBSQLITE_GET_VERSION \
88                 " SELECT number FROM Version"
89#define HDBSQLITE_FETCH \
90                 " SELECT Entry.data FROM Principal, Entry" \
91                 " WHERE Principal.principal = ? AND" \
92                 "       Entry.id = Principal.entry"
93#define HDBSQLITE_GET_IDS \
94                 " SELECT id, entry FROM Principal" \
95                 " WHERE principal = ?"
96#define HDBSQLITE_ADD_ENTRY \
97                 " INSERT INTO Entry (data) VALUES (?)"
98#define HDBSQLITE_ADD_PRINCIPAL \
99                 " INSERT INTO Principal (principal, entry, canonical)" \
100                 " VALUES (?, last_insert_rowid(), 1)"
101#define HDBSQLITE_ADD_ALIAS \
102                 " INSERT INTO Principal (principal, entry, canonical)" \
103                 " VALUES(?, ?, 0)"
104#define HDBSQLITE_DELETE_ALIASES \
105                 " DELETE FROM Principal" \
106                 " WHERE entry = ? AND canonical = 0"
107#define HDBSQLITE_UPDATE_ENTRY \
108                 " UPDATE Entry SET data = ?" \
109                 " WHERE id = ?"
110#define HDBSQLITE_REMOVE \
111                 " DELETE FROM ENTRY WHERE id = " \
112                 "  (SELECT entry FROM Principal" \
113                 "   WHERE principal = ?)"
114#define HDBSQLITE_GET_ALL_ENTRIES \
115                 " SELECT data FROM Entry"
116
117/**
118 * Wrapper around sqlite3_prepare_v2.
119 *
120 * @param context   The current krb5 context
121 * @param statement Where to store the pointer to the statement
122 *                  after preparing it
123 * @param str       SQL code for the statement
124 *
125 * @return          0 if OK, an error code if not
126 */
127static krb5_error_code
128hdb_sqlite_prepare_stmt(krb5_context context,
129                        sqlite3 *db,
130                        sqlite3_stmt **statement,
131                        const char *str)
132{
133    int ret, tries = 0;
134
135    ret = sqlite3_prepare_v2(db, str, -1, statement, NULL);
136    while((tries++ < MAX_RETRIES) &&
137	  ((ret == SQLITE_BUSY) ||
138           (ret == SQLITE_IOERR_BLOCKED) ||
139           (ret == SQLITE_LOCKED))) {
140	krb5_warnx(context, "hdb-sqlite: prepare busy");
141        sleep(1);
142        ret = sqlite3_prepare_v2(db, str, -1, statement, NULL);
143    }
144
145    if (ret != SQLITE_OK) {
146        krb5_set_error_message(context, EINVAL,
147			       "Failed to prepare stmt %s: %s",
148			       str, sqlite3_errmsg(db));
149        return EINVAL;
150    }
151
152    return 0;
153}
154
155/**
156 * A wrapper around sqlite3_exec.
157 *
158 * @param context    The current krb5 context
159 * @param database   An open sqlite3 database handle
160 * @param statement  SQL code to execute
161 * @param error_code What to return if the statement fails
162 *
163 * @return           0 if OK, else error_code
164 */
165static krb5_error_code
166hdb_sqlite_exec_stmt(krb5_context context,
167                     sqlite3 *database,
168                     const char *statement,
169                     krb5_error_code error_code)
170{
171    int ret;
172
173    ret = sqlite3_exec(database, statement, NULL, NULL, NULL);
174
175    while(((ret == SQLITE_BUSY) ||
176           (ret == SQLITE_IOERR_BLOCKED) ||
177           (ret == SQLITE_LOCKED))) {
178	krb5_warnx(context, "hdb-sqlite: exec busy: %d", (int)getpid());
179        sleep(1);
180        ret = sqlite3_exec(database, statement, NULL, NULL, NULL);
181    }
182
183    if (ret != SQLITE_OK && error_code) {
184        krb5_set_error_message(context, error_code,
185			       "Execute %s: %s", statement,
186                              sqlite3_errmsg(database));
187        return error_code;
188    }
189
190    return 0;
191}
192
193/**
194 * Opens an sqlite3 database handle to a file, may create the
195 * database file depending on flags.
196 *
197 * @param context The current krb5 context
198 * @param db      Heimdal database handle
199 * @param flags   Controls whether or not the file may be created,
200 *                may be 0 or SQLITE_OPEN_CREATE
201 */
202static krb5_error_code
203hdb_sqlite_open_database(krb5_context context, HDB *db, int flags)
204{
205    int ret;
206    hdb_sqlite_db *hsdb = (hdb_sqlite_db*) db->hdb_db;
207
208    ret = sqlite3_open_v2(hsdb->db_file, &hsdb->db,
209                          SQLITE_OPEN_READWRITE | flags, NULL);
210
211    if (ret) {
212        if (hsdb->db) {
213	    ret = ENOENT;
214            krb5_set_error_message(context, ret,
215                                  "Error opening sqlite database %s: %s",
216                                  hsdb->db_file, sqlite3_errmsg(hsdb->db));
217            sqlite3_close(hsdb->db);
218            hsdb->db = NULL;
219        } else
220	    ret = krb5_enomem(context);
221        return ret;
222    }
223
224    return 0;
225}
226
227static int
228hdb_sqlite_step(krb5_context context, sqlite3 *db, sqlite3_stmt *stmt)
229{
230    int ret;
231
232    ret = sqlite3_step(stmt);
233    while(((ret == SQLITE_BUSY) ||
234           (ret == SQLITE_IOERR_BLOCKED) ||
235           (ret == SQLITE_LOCKED))) {
236	krb5_warnx(context, "hdb-sqlite: step busy: %d", (int)getpid());
237        sleep(1);
238        ret = sqlite3_step(stmt);
239    }
240    return ret;
241}
242
243/**
244 * Closes the database and frees memory allocated for statements.
245 *
246 * @param context The current krb5 context
247 * @param db      Heimdal database handle
248 */
249static krb5_error_code
250hdb_sqlite_close_database(krb5_context context, HDB *db)
251{
252    hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db;
253
254    sqlite3_finalize(hsdb->get_version);
255    sqlite3_finalize(hsdb->fetch);
256    sqlite3_finalize(hsdb->get_ids);
257    sqlite3_finalize(hsdb->add_entry);
258    sqlite3_finalize(hsdb->add_principal);
259    sqlite3_finalize(hsdb->add_alias);
260    sqlite3_finalize(hsdb->delete_aliases);
261    sqlite3_finalize(hsdb->update_entry);
262    sqlite3_finalize(hsdb->remove);
263    sqlite3_finalize(hsdb->get_all_entries);
264
265    sqlite3_close(hsdb->db);
266
267    return 0;
268}
269
270/**
271 * Opens an sqlite database file and prepares it for use.
272 * If the file does not exist it will be created.
273 *
274 * @param context  The current krb5_context
275 * @param db       The heimdal database handle
276 * @param filename Where to store the database file
277 *
278 * @return         0 if everything worked, an error code if not
279 */
280static krb5_error_code
281hdb_sqlite_make_database(krb5_context context, HDB *db, const char *filename)
282{
283    int ret;
284    int created_file = 0;
285    hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db;
286
287    hsdb->db_file = strdup(filename);
288    if(hsdb->db_file == NULL)
289        return ENOMEM;
290
291    ret = hdb_sqlite_open_database(context, db, 0);
292    if (ret) {
293        ret = hdb_sqlite_open_database(context, db, SQLITE_OPEN_CREATE);
294        if (ret) goto out;
295
296        created_file = 1;
297
298        ret = hdb_sqlite_exec_stmt(context, hsdb->db,
299                                   HDBSQLITE_CREATE_TABLES,
300                                   EINVAL);
301        if (ret) goto out;
302
303        ret = hdb_sqlite_exec_stmt(context, hsdb->db,
304                                   HDBSQLITE_CREATE_TRIGGERS,
305                                   EINVAL);
306        if (ret) goto out;
307    }
308
309    ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
310                                  &hsdb->get_version,
311                                  HDBSQLITE_GET_VERSION);
312    if (ret) goto out;
313    ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
314                                  &hsdb->fetch,
315                                  HDBSQLITE_FETCH);
316    if (ret) goto out;
317    ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
318                                  &hsdb->get_ids,
319                                  HDBSQLITE_GET_IDS);
320    if (ret) goto out;
321    ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
322                                  &hsdb->add_entry,
323                                  HDBSQLITE_ADD_ENTRY);
324    if (ret) goto out;
325    ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
326                                  &hsdb->add_principal,
327                                  HDBSQLITE_ADD_PRINCIPAL);
328    if (ret) goto out;
329    ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
330                                  &hsdb->add_alias,
331                                  HDBSQLITE_ADD_ALIAS);
332    if (ret) goto out;
333    ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
334                                  &hsdb->delete_aliases,
335                                  HDBSQLITE_DELETE_ALIASES);
336    if (ret) goto out;
337    ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
338                                  &hsdb->update_entry,
339                                  HDBSQLITE_UPDATE_ENTRY);
340    if (ret) goto out;
341    ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
342                                  &hsdb->remove,
343                                  HDBSQLITE_REMOVE);
344    if (ret) goto out;
345    ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
346                                  &hsdb->get_all_entries,
347                                  HDBSQLITE_GET_ALL_ENTRIES);
348    if (ret) goto out;
349
350    ret = hdb_sqlite_step(context, hsdb->db, hsdb->get_version);
351    if(ret == SQLITE_ROW) {
352        hsdb->version = sqlite3_column_double(hsdb->get_version, 0);
353    }
354    sqlite3_reset(hsdb->get_version);
355    ret = 0;
356
357    if(hsdb->version != HDBSQLITE_VERSION) {
358        ret = EINVAL;
359        krb5_set_error_message(context, ret, "HDBSQLITE_VERSION mismatch");
360    }
361
362    if(ret) goto out;
363
364    return 0;
365
366 out:
367    if (hsdb->db)
368        sqlite3_close(hsdb->db);
369    if (created_file)
370        unlink(hsdb->db_file);
371
372    return ret;
373}
374
375/**
376 * Retrieves an entry by searching for the given
377 * principal in the Principal database table, both
378 * for canonical principals and aliases.
379 *
380 * @param context   The current krb5_context
381 * @param db        Heimdal database handle
382 * @param principal The principal whose entry to search for
383 * @param flags     Currently only for HDB_F_DECRYPT
384 * @param kvno	    kvno to fetch is HDB_F_KVNO_SPECIFIED use used
385 *
386 * @return          0 if everything worked, an error code if not
387 */
388static krb5_error_code
389hdb_sqlite_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
390		      unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry)
391{
392    int sqlite_error;
393    krb5_error_code ret;
394    char *principal_string;
395    hdb_sqlite_db *hsdb = (hdb_sqlite_db*)(db->hdb_db);
396    sqlite3_stmt *fetch = hsdb->fetch;
397    krb5_data value;
398
399    ret = krb5_unparse_name(context, principal, &principal_string);
400    if (ret) {
401        free(principal_string);
402        return ret;
403    }
404
405    sqlite3_bind_text(fetch, 1, principal_string, -1, SQLITE_STATIC);
406
407    sqlite_error = hdb_sqlite_step(context, hsdb->db, fetch);
408    if (sqlite_error != SQLITE_ROW) {
409        if(sqlite_error == SQLITE_DONE) {
410            ret = HDB_ERR_NOENTRY;
411            goto out;
412        } else {
413            ret = EINVAL;
414            krb5_set_error_message(context, ret,
415                                  "sqlite fetch failed: %d",
416                                  sqlite_error);
417            goto out;
418        }
419    }
420
421    if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
422        ret = hdb_unseal_keys(context, db, &entry->entry);
423        if(ret) {
424           hdb_free_entry(context, entry);
425           goto out;
426        }
427    }
428
429    value.length = sqlite3_column_bytes(fetch, 0);
430    value.data = (void *) sqlite3_column_blob(fetch, 0);
431
432    ret = hdb_value2entry(context, &value, &entry->entry);
433    if(ret)
434        goto out;
435
436    ret = 0;
437
438out:
439
440    sqlite3_clear_bindings(fetch);
441    sqlite3_reset(fetch);
442
443    free(principal_string);
444
445    return ret;
446}
447
448/**
449 * Convenience function to step a prepared statement with no
450 * value once.
451 *
452 * @param context   The current krb5_context
453 * @param statement A prepared sqlite3 statement
454 *
455 * @return        0 if everything worked, an error code if not
456 */
457static krb5_error_code
458hdb_sqlite_step_once(krb5_context context, HDB *db, sqlite3_stmt *statement)
459{
460    int ret;
461    hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db;
462
463    ret = hdb_sqlite_step(context, hsdb->db, statement);
464    sqlite3_clear_bindings(statement);
465    sqlite3_reset(statement);
466
467    return ret;
468}
469
470
471/**
472 * Stores an hdb_entry in the database. If flags contains HDB_F_REPLACE
473 * a previous entry may be replaced.
474 *
475 * @param context The current krb5_context
476 * @param db      Heimdal database handle
477 * @param flags   May currently only contain HDB_F_REPLACE
478 * @param entry   The data to store
479 *
480 * @return        0 if everything worked, an error code if not
481 */
482static krb5_error_code
483hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags,
484                 hdb_entry_ex *entry)
485{
486    int ret;
487    int i;
488    sqlite_int64 entry_id;
489    char *principal_string = NULL;
490    char *alias_string;
491    const HDB_Ext_Aliases *aliases;
492
493    hdb_sqlite_db *hsdb = (hdb_sqlite_db *)(db->hdb_db);
494    krb5_data value;
495    sqlite3_stmt *get_ids = hsdb->get_ids;
496
497    ret = hdb_sqlite_exec_stmt(context, hsdb->db,
498                               "BEGIN IMMEDIATE TRANSACTION", EINVAL);
499    if(ret != SQLITE_OK) {
500	ret = EINVAL;
501        krb5_set_error_message(context, ret,
502			       "SQLite BEGIN TRANSACTION failed: %s",
503			       sqlite3_errmsg(hsdb->db));
504        goto rollback;
505    }
506
507    ret = krb5_unparse_name(context,
508                            entry->entry.principal, &principal_string);
509    if (ret) {
510        goto rollback;
511    }
512
513    ret = hdb_seal_keys(context, db, &entry->entry);
514    if(ret) {
515        goto rollback;
516    }
517
518    ret = hdb_entry2value(context, &entry->entry, &value);
519    if(ret) {
520        goto rollback;
521    }
522
523    sqlite3_bind_text(get_ids, 1, principal_string, -1, SQLITE_STATIC);
524    ret = hdb_sqlite_step(context, hsdb->db, get_ids);
525
526    if(ret == SQLITE_DONE) { /* No such principal */
527
528        sqlite3_bind_blob(hsdb->add_entry, 1,
529                          value.data, value.length, SQLITE_STATIC);
530        ret = hdb_sqlite_step(context, hsdb->db, hsdb->add_entry);
531        sqlite3_clear_bindings(hsdb->add_entry);
532        sqlite3_reset(hsdb->add_entry);
533        if(ret != SQLITE_DONE)
534            goto rollback;
535
536        sqlite3_bind_text(hsdb->add_principal, 1,
537                          principal_string, -1, SQLITE_STATIC);
538        ret = hdb_sqlite_step(context, hsdb->db, hsdb->add_principal);
539        sqlite3_clear_bindings(hsdb->add_principal);
540        sqlite3_reset(hsdb->add_principal);
541        if(ret != SQLITE_DONE)
542            goto rollback;
543
544        entry_id = sqlite3_column_int64(get_ids, 1);
545
546    } else if(ret == SQLITE_ROW) { /* Found a principal */
547
548        if(! (flags & HDB_F_REPLACE)) /* Not allowed to replace it */
549            goto rollback;
550
551        entry_id = sqlite3_column_int64(get_ids, 1);
552
553        sqlite3_bind_int64(hsdb->delete_aliases, 1, entry_id);
554        ret = hdb_sqlite_step_once(context, db, hsdb->delete_aliases);
555        if(ret != SQLITE_DONE)
556            goto rollback;
557
558        sqlite3_bind_blob(hsdb->update_entry, 1,
559                          value.data, value.length, SQLITE_STATIC);
560        sqlite3_bind_int64(hsdb->update_entry, 2, entry_id);
561        ret = hdb_sqlite_step_once(context, db, hsdb->update_entry);
562        if(ret != SQLITE_DONE)
563            goto rollback;
564
565    } else {
566	/* Error! */
567        goto rollback;
568    }
569
570    ret = hdb_entry_get_aliases(&entry->entry, &aliases);
571    if(ret || aliases == NULL)
572        goto commit;
573
574    for(i = 0; i < aliases->aliases.len; i++) {
575
576        ret = krb5_unparse_name(context, &aliases->aliases.val[i],
577				&alias_string);
578        if (ret) {
579            free(alias_string);
580            goto rollback;
581        }
582
583        sqlite3_bind_text(hsdb->add_alias, 1, alias_string,
584                          -1, SQLITE_STATIC);
585        sqlite3_bind_int64(hsdb->add_alias, 2, entry_id);
586        ret = hdb_sqlite_step_once(context, db, hsdb->add_alias);
587
588        free(alias_string);
589
590        if(ret != SQLITE_DONE)
591            goto rollback;
592    }
593
594    ret = 0;
595
596commit:
597
598    free(principal_string);
599
600    krb5_data_free(&value);
601
602    sqlite3_clear_bindings(get_ids);
603    sqlite3_reset(get_ids);
604
605    ret = hdb_sqlite_exec_stmt(context, hsdb->db, "COMMIT", EINVAL);
606    if(ret != SQLITE_OK)
607	krb5_warnx(context, "hdb-sqlite: COMMIT problem: %d: %s",
608		   ret, sqlite3_errmsg(hsdb->db));
609
610    return ret;
611
612rollback:
613
614    krb5_warnx(context, "hdb-sqlite: store rollback problem: %d: %s",
615	       ret, sqlite3_errmsg(hsdb->db));
616
617    free(principal_string);
618
619    ret = hdb_sqlite_exec_stmt(context, hsdb->db,
620                               "ROLLBACK", EINVAL);
621    return ret;
622}
623
624/**
625 * This may be called often by other code, since the BDB backends
626 * can not have several open connections. SQLite can handle
627 * many processes with open handles to the database file
628 * and closing/opening the handle is an expensive operation.
629 * Hence, this function does nothing.
630 *
631 * @param context The current krb5 context
632 * @param db      Heimdal database handle
633 *
634 * @return        Always returns 0
635 */
636static krb5_error_code
637hdb_sqlite_close(krb5_context context, HDB *db)
638{
639    return 0;
640}
641
642/**
643 * The opposite of hdb_sqlite_close. Since SQLite accepts
644 * many open handles to the database file the handle does not
645 * need to be closed, or reopened.
646 *
647 * @param context The current krb5 context
648 * @param db      Heimdal database handle
649 * @param flags
650 * @param mode_t
651 *
652 * @return        Always returns 0
653 */
654static krb5_error_code
655hdb_sqlite_open(krb5_context context, HDB *db, int flags, mode_t mode)
656{
657    return 0;
658}
659
660/**
661 * Closes the databse and frees all resources.
662 *
663 * @param context The current krb5 context
664 * @param db      Heimdal database handle
665 *
666 * @return        0 on success, an error code if not
667 */
668static krb5_error_code
669hdb_sqlite_destroy(krb5_context context, HDB *db)
670{
671    int ret;
672    hdb_sqlite_db *hsdb;
673
674    ret = hdb_clear_master_key(context, db);
675
676    hdb_sqlite_close_database(context, db);
677
678    hsdb = (hdb_sqlite_db*)(db->hdb_db);
679
680    free(hsdb->db_file);
681    free(db->hdb_db);
682    free(db);
683
684    return ret;
685}
686
687/*
688 * Not sure if this is needed.
689 */
690static krb5_error_code
691hdb_sqlite_lock(krb5_context context, HDB *db, int operation)
692{
693    krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
694			   "lock not implemented");
695    return HDB_ERR_CANT_LOCK_DB;
696}
697
698/*
699 * Not sure if this is needed.
700 */
701static krb5_error_code
702hdb_sqlite_unlock(krb5_context context, HDB *db)
703{
704    krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
705			  "unlock not implemented");
706    return HDB_ERR_CANT_LOCK_DB;
707}
708
709/*
710 * Should get the next entry, to allow iteration over all entries.
711 */
712static krb5_error_code
713hdb_sqlite_nextkey(krb5_context context, HDB *db, unsigned flags,
714                   hdb_entry_ex *entry)
715{
716    krb5_error_code ret = 0;
717    int sqlite_error;
718    krb5_data value;
719
720    hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db;
721
722    sqlite_error = hdb_sqlite_step(context, hsdb->db, hsdb->get_all_entries);
723    if(sqlite_error == SQLITE_ROW) {
724	/* Found an entry */
725        value.length = sqlite3_column_bytes(hsdb->get_all_entries, 0);
726        value.data = (void *) sqlite3_column_blob(hsdb->get_all_entries, 0);
727        memset(entry, 0, sizeof(*entry));
728        ret = hdb_value2entry(context, &value, &entry->entry);
729    }
730    else if(sqlite_error == SQLITE_DONE) {
731	/* No more entries */
732        ret = HDB_ERR_NOENTRY;
733        sqlite3_reset(hsdb->get_all_entries);
734    }
735    else {
736	/* XXX SQLite error. Should be handled in some way. */
737        ret = EINVAL;
738    }
739
740    return ret;
741}
742
743/*
744 * Should get the first entry in the database.
745 * What is flags used for?
746 */
747static krb5_error_code
748hdb_sqlite_firstkey(krb5_context context, HDB *db, unsigned flags,
749                    hdb_entry_ex *entry)
750{
751    hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db;
752    krb5_error_code ret;
753
754    sqlite3_reset(hsdb->get_all_entries);
755
756    ret = hdb_sqlite_nextkey(context, db, flags, entry);
757    if(ret)
758        return ret;
759
760    return 0;
761}
762
763/*
764 * Renames the database file.
765 */
766static krb5_error_code
767hdb_sqlite_rename(krb5_context context, HDB *db, const char *new_name)
768{
769    hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db;
770    int ret;
771
772    krb5_warnx(context, "hdb_sqlite_rename");
773
774    if (strncasecmp(new_name, "sqlite:", 7) == 0)
775	new_name += 7;
776
777    hdb_sqlite_close_database(context, db);
778
779    ret = rename(hsdb->db_file, new_name);
780    free(hsdb->db_file);
781
782    hdb_sqlite_make_database(context, db, new_name);
783
784    return ret;
785}
786
787/*
788 * Removes a principal, including aliases and associated entry.
789 */
790static krb5_error_code
791hdb_sqlite_remove(krb5_context context, HDB *db,
792                  krb5_const_principal principal)
793{
794    krb5_error_code ret;
795    char *principal_string;
796    hdb_sqlite_db *hsdb = (hdb_sqlite_db*)(db->hdb_db);
797    sqlite3_stmt *remove = hsdb->remove;
798
799    ret = krb5_unparse_name(context, principal, &principal_string);
800    if (ret) {
801        free(principal_string);
802        return ret;
803    }
804
805    sqlite3_bind_text(remove, 1, principal_string, -1, SQLITE_STATIC);
806
807    ret = hdb_sqlite_step(context, hsdb->db, remove);
808    if (ret != SQLITE_DONE) {
809	ret = EINVAL;
810        krb5_set_error_message(context, ret,
811                              "sqlite remove failed: %d",
812                              ret);
813    } else
814        ret = 0;
815
816    sqlite3_clear_bindings(remove);
817    sqlite3_reset(remove);
818
819    return ret;
820}
821
822/**
823 * Create SQLITE object, and creates the on disk database if its doesn't exists.
824 *
825 * @param context A Kerberos 5 context.
826 * @param db a returned database handle.
827 * @param argument filename
828 *
829 * @return        0 on success, an error code if not
830 */
831
832krb5_error_code
833hdb_sqlite_create(krb5_context context, HDB **db, const char *argument)
834{
835    krb5_error_code ret;
836    hdb_sqlite_db *hsdb;
837
838    *db = calloc(1, sizeof (**db));
839    if (*db == NULL)
840	return krb5_enomem(context);
841
842    hsdb = (hdb_sqlite_db*) calloc(1, sizeof (*hsdb));
843    if (hsdb == NULL) {
844        free(*db);
845        *db = NULL;
846	return krb5_enomem(context);
847    }
848
849    (*db)->hdb_db = hsdb;
850
851    /* XXX make_database should make sure everything else is freed on error */
852    ret = hdb_sqlite_make_database(context, *db, argument);
853    if (ret) {
854        free((*db)->hdb_db);
855        free(*db);
856
857        return ret;
858    }
859
860    (*db)->hdb_master_key_set = 0;
861    (*db)->hdb_openp = 0;
862    (*db)->hdb_capability_flags = 0;
863
864    (*db)->hdb_open = hdb_sqlite_open;
865    (*db)->hdb_close = hdb_sqlite_close;
866
867    (*db)->hdb_lock = hdb_sqlite_lock;
868    (*db)->hdb_unlock = hdb_sqlite_unlock;
869    (*db)->hdb_firstkey = hdb_sqlite_firstkey;
870    (*db)->hdb_nextkey = hdb_sqlite_nextkey;
871    (*db)->hdb_fetch_kvno = hdb_sqlite_fetch_kvno;
872    (*db)->hdb_store = hdb_sqlite_store;
873    (*db)->hdb_remove = hdb_sqlite_remove;
874    (*db)->hdb_destroy = hdb_sqlite_destroy;
875    (*db)->hdb_rename = hdb_sqlite_rename;
876    (*db)->hdb__get = NULL;
877    (*db)->hdb__put = NULL;
878    (*db)->hdb__del = NULL;
879
880    return 0;
881}
882