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