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