1/*
2 * Copyright (c) 1997 - 2006 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "hdb_locl.h"
35
36RCSID("$Id: db3.c 21610 2007-07-17 07:10:45Z lha $");
37
38#if HAVE_DB3
39
40#ifdef HAVE_DB4_DB_H
41#include <db4/db.h>
42#elif defined(HAVE_DB3_DB_H)
43#include <db3/db.h>
44#else
45#include <db.h>
46#endif
47
48static krb5_error_code
49DB_close(krb5_context context, HDB *db)
50{
51    DB *d = (DB*)db->hdb_db;
52    DBC *dbcp = (DBC*)db->hdb_dbc;
53
54    (*dbcp->c_close)(dbcp);
55    db->hdb_dbc = 0;
56    (*d->close)(d, 0);
57    return 0;
58}
59
60static krb5_error_code
61DB_destroy(krb5_context context, HDB *db)
62{
63    krb5_error_code ret;
64
65    ret = hdb_clear_master_key (context, db);
66    free(db->hdb_name);
67    free(db);
68    return ret;
69}
70
71static krb5_error_code
72DB_lock(krb5_context context, HDB *db, int operation)
73{
74    DB *d = (DB*)db->hdb_db;
75    int fd;
76    if ((*d->fd)(d, &fd))
77	return HDB_ERR_CANT_LOCK_DB;
78    return hdb_lock(fd, operation);
79}
80
81static krb5_error_code
82DB_unlock(krb5_context context, HDB *db)
83{
84    DB *d = (DB*)db->hdb_db;
85    int fd;
86    if ((*d->fd)(d, &fd))
87	return HDB_ERR_CANT_LOCK_DB;
88    return hdb_unlock(fd);
89}
90
91
92static krb5_error_code
93DB_seq(krb5_context context, HDB *db,
94       unsigned flags, hdb_entry_ex *entry, int flag)
95{
96    DBT key, value;
97    DBC *dbcp = db->hdb_dbc;
98    krb5_data key_data, data;
99    int code;
100
101    memset(&key, 0, sizeof(DBT));
102    memset(&value, 0, sizeof(DBT));
103    if ((*db->hdb_lock)(context, db, HDB_RLOCK))
104	return HDB_ERR_DB_INUSE;
105    code = (*dbcp->c_get)(dbcp, &key, &value, flag);
106    (*db->hdb_unlock)(context, db); /* XXX check value */
107    if (code == DB_NOTFOUND)
108	return HDB_ERR_NOENTRY;
109    if (code)
110	return code;
111
112    key_data.data = key.data;
113    key_data.length = key.size;
114    data.data = value.data;
115    data.length = value.size;
116    memset(entry, 0, sizeof(*entry));
117    if (hdb_value2entry(context, &data, &entry->entry))
118	return DB_seq(context, db, flags, entry, DB_NEXT);
119    if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
120	code = hdb_unseal_keys (context, db, &entry->entry);
121	if (code)
122	    hdb_free_entry (context, entry);
123    }
124    if (entry->entry.principal == NULL) {
125	entry->entry.principal = malloc(sizeof(*entry->entry.principal));
126	if (entry->entry.principal == NULL) {
127	    hdb_free_entry (context, entry);
128	    krb5_set_error_string(context, "malloc: out of memory");
129	    return ENOMEM;
130	} else {
131	    hdb_key2principal(context, &key_data, entry->entry.principal);
132	}
133    }
134    return 0;
135}
136
137
138static krb5_error_code
139DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
140{
141    return DB_seq(context, db, flags, entry, DB_FIRST);
142}
143
144
145static krb5_error_code
146DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
147{
148    return DB_seq(context, db, flags, entry, DB_NEXT);
149}
150
151static krb5_error_code
152DB_rename(krb5_context context, HDB *db, const char *new_name)
153{
154    int ret;
155    char *old, *new;
156
157    asprintf(&old, "%s.db", db->hdb_name);
158    asprintf(&new, "%s.db", new_name);
159    ret = rename(old, new);
160    free(old);
161    free(new);
162    if(ret)
163	return errno;
164
165    free(db->hdb_name);
166    db->hdb_name = strdup(new_name);
167    return 0;
168}
169
170static krb5_error_code
171DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
172{
173    DB *d = (DB*)db->hdb_db;
174    DBT k, v;
175    int code;
176
177    memset(&k, 0, sizeof(DBT));
178    memset(&v, 0, sizeof(DBT));
179    k.data = key.data;
180    k.size = key.length;
181    k.flags = 0;
182    if ((code = (*db->hdb_lock)(context, db, HDB_RLOCK)))
183	return code;
184    code = (*d->get)(d, NULL, &k, &v, 0);
185    (*db->hdb_unlock)(context, db);
186    if(code == DB_NOTFOUND)
187	return HDB_ERR_NOENTRY;
188    if(code)
189	return code;
190
191    krb5_data_copy(reply, v.data, v.size);
192    return 0;
193}
194
195static krb5_error_code
196DB__put(krb5_context context, HDB *db, int replace,
197	krb5_data key, krb5_data value)
198{
199    DB *d = (DB*)db->hdb_db;
200    DBT k, v;
201    int code;
202
203    memset(&k, 0, sizeof(DBT));
204    memset(&v, 0, sizeof(DBT));
205    k.data = key.data;
206    k.size = key.length;
207    k.flags = 0;
208    v.data = value.data;
209    v.size = value.length;
210    v.flags = 0;
211    if ((code = (*db->hdb_lock)(context, db, HDB_WLOCK)))
212	return code;
213    code = (*d->put)(d, NULL, &k, &v, replace ? 0 : DB_NOOVERWRITE);
214    (*db->hdb_unlock)(context, db);
215    if(code == DB_KEYEXIST)
216	return HDB_ERR_EXISTS;
217    if(code)
218	return errno;
219    return 0;
220}
221
222static krb5_error_code
223DB__del(krb5_context context, HDB *db, krb5_data key)
224{
225    DB *d = (DB*)db->hdb_db;
226    DBT k;
227    krb5_error_code code;
228    memset(&k, 0, sizeof(DBT));
229    k.data = key.data;
230    k.size = key.length;
231    k.flags = 0;
232    code = (*db->hdb_lock)(context, db, HDB_WLOCK);
233    if(code)
234	return code;
235    code = (*d->del)(d, NULL, &k, 0);
236    (*db->hdb_unlock)(context, db);
237    if(code == DB_NOTFOUND)
238	return HDB_ERR_NOENTRY;
239    if(code)
240	return code;
241    return 0;
242}
243
244static krb5_error_code
245DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
246{
247    DBC *dbc = NULL;
248    char *fn;
249    krb5_error_code ret;
250    DB *d;
251    int myflags = 0;
252
253    if (flags & O_CREAT)
254      myflags |= DB_CREATE;
255
256    if (flags & O_EXCL)
257      myflags |= DB_EXCL;
258
259    if((flags & O_ACCMODE) == O_RDONLY)
260      myflags |= DB_RDONLY;
261
262    if (flags & O_TRUNC)
263      myflags |= DB_TRUNCATE;
264
265    asprintf(&fn, "%s.db", db->hdb_name);
266    if (fn == NULL) {
267	krb5_set_error_string(context, "malloc: out of memory");
268	return ENOMEM;
269    }
270    db_create(&d, NULL, 0);
271    db->hdb_db = d;
272
273#if (DB_VERSION_MAJOR >= 4) && (DB_VERSION_MINOR >= 1)
274    ret = (*d->open)(db->hdb_db, NULL, fn, NULL, DB_BTREE, myflags, mode);
275#else
276    ret = (*d->open)(db->hdb_db, fn, NULL, DB_BTREE, myflags, mode);
277#endif
278
279    if (ret == ENOENT) {
280	/* try to open without .db extension */
281#if (DB_VERSION_MAJOR >= 4) && (DB_VERSION_MINOR >= 1)
282	ret = (*d->open)(db->hdb_db, NULL, db->hdb_name, NULL, DB_BTREE,
283			 myflags, mode);
284#else
285	ret = (*d->open)(db->hdb_db, db->hdb_name, NULL, DB_BTREE,
286			 myflags, mode);
287#endif
288    }
289
290    if (ret) {
291	free(fn);
292	krb5_set_error_string(context, "opening %s: %s",
293			      db->hdb_name, strerror(ret));
294	return ret;
295    }
296    free(fn);
297
298    ret = (*d->cursor)(d, NULL, &dbc, 0);
299    if (ret) {
300	krb5_set_error_string(context, "d->cursor: %s", strerror(ret));
301        return ret;
302    }
303    db->hdb_dbc = dbc;
304
305    if((flags & O_ACCMODE) == O_RDONLY)
306	ret = hdb_check_db_format(context, db);
307    else
308	ret = hdb_init_db(context, db);
309    if(ret == HDB_ERR_NOENTRY)
310	return 0;
311    if (ret) {
312	DB_close(context, db);
313	krb5_set_error_string(context, "hdb_open: failed %s database %s",
314			      (flags & O_ACCMODE) == O_RDONLY ?
315			      "checking format of" : "initialize",
316			      db->hdb_name);
317    }
318
319    return ret;
320}
321
322krb5_error_code
323hdb_db_create(krb5_context context, HDB **db,
324	      const char *filename)
325{
326    *db = calloc(1, sizeof(**db));
327    if (*db == NULL) {
328	krb5_set_error_string(context, "malloc: out of memory");
329	return ENOMEM;
330    }
331
332    (*db)->hdb_db = NULL;
333    (*db)->hdb_name = strdup(filename);
334    if ((*db)->hdb_name == NULL) {
335	krb5_set_error_string(context, "malloc: out of memory");
336	free(*db);
337	*db = NULL;
338	return ENOMEM;
339    }
340    (*db)->hdb_master_key_set = 0;
341    (*db)->hdb_openp = 0;
342    (*db)->hdb_open  = DB_open;
343    (*db)->hdb_close = DB_close;
344    (*db)->hdb_fetch = _hdb_fetch;
345    (*db)->hdb_store = _hdb_store;
346    (*db)->hdb_remove = _hdb_remove;
347    (*db)->hdb_firstkey = DB_firstkey;
348    (*db)->hdb_nextkey= DB_nextkey;
349    (*db)->hdb_lock = DB_lock;
350    (*db)->hdb_unlock = DB_unlock;
351    (*db)->hdb_rename = DB_rename;
352    (*db)->hdb__get = DB__get;
353    (*db)->hdb__put = DB__put;
354    (*db)->hdb__del = DB__del;
355    (*db)->hdb_destroy = DB_destroy;
356    return 0;
357}
358#endif /* HAVE_DB3 */
359