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