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
36#if HAVE_DB3
37
38#ifdef HAVE_DBHEADER
39#include <db.h>
40#elif HAVE_DB5_DB_H
41#include <db5/db.h>
42#elif HAVE_DB4_DB_H
43#include <db4/db.h>
44#elif HAVE_DB3_DB_H
45#include <db3/db.h>
46#else
47#include <db.h>
48#endif
49
50static krb5_error_code
51DB_close(krb5_context context, HDB *db)
52{
53    DB *d = (DB*)db->hdb_db;
54    DBC *dbcp = (DBC*)db->hdb_dbc;
55
56    (*dbcp->c_close)(dbcp);
57    db->hdb_dbc = 0;
58    (*d->close)(d, 0);
59    return 0;
60}
61
62static krb5_error_code
63DB_destroy(krb5_context context, HDB *db)
64{
65    krb5_error_code ret;
66
67    ret = hdb_clear_master_key (context, db);
68    free(db->hdb_name);
69    free(db);
70    return ret;
71}
72
73static krb5_error_code
74DB_lock(krb5_context context, HDB *db, int operation)
75{
76    DB *d = (DB*)db->hdb_db;
77    int fd;
78    if ((*d->fd)(d, &fd))
79	return HDB_ERR_CANT_LOCK_DB;
80    return hdb_lock(fd, operation);
81}
82
83static krb5_error_code
84DB_unlock(krb5_context context, HDB *db)
85{
86    DB *d = (DB*)db->hdb_db;
87    int fd;
88    if ((*d->fd)(d, &fd))
89	return HDB_ERR_CANT_LOCK_DB;
90    return hdb_unlock(fd);
91}
92
93
94static krb5_error_code
95DB_seq(krb5_context context, HDB *db,
96       unsigned flags, hdb_entry_ex *entry, int flag)
97{
98    DBT key, value;
99    DBC *dbcp = db->hdb_dbc;
100    krb5_data key_data, data;
101    int code;
102
103    memset(&key, 0, sizeof(DBT));
104    memset(&value, 0, sizeof(DBT));
105    if ((*db->hdb_lock)(context, db, HDB_RLOCK))
106	return HDB_ERR_DB_INUSE;
107    code = (*dbcp->c_get)(dbcp, &key, &value, flag);
108    (*db->hdb_unlock)(context, db); /* XXX check value */
109    if (code == DB_NOTFOUND)
110	return HDB_ERR_NOENTRY;
111    if (code)
112	return code;
113
114    key_data.data = key.data;
115    key_data.length = key.size;
116    data.data = value.data;
117    data.length = value.size;
118    memset(entry, 0, sizeof(*entry));
119    if (hdb_value2entry(context, &data, &entry->entry))
120	return DB_seq(context, db, flags, entry, DB_NEXT);
121    if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
122	code = hdb_unseal_keys (context, db, &entry->entry);
123	if (code)
124	    hdb_free_entry (context, entry);
125    }
126    if (entry->entry.principal == NULL) {
127	entry->entry.principal = malloc(sizeof(*entry->entry.principal));
128	if (entry->entry.principal == NULL) {
129	    hdb_free_entry (context, entry);
130	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
131	    return ENOMEM;
132	} else {
133	    hdb_key2principal(context, &key_data, entry->entry.principal);
134	}
135    }
136    return 0;
137}
138
139
140static krb5_error_code
141DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
142{
143    return DB_seq(context, db, flags, entry, DB_FIRST);
144}
145
146
147static krb5_error_code
148DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
149{
150    return DB_seq(context, db, flags, entry, DB_NEXT);
151}
152
153static krb5_error_code
154DB_rename(krb5_context context, HDB *db, const char *new_name)
155{
156    int ret;
157    char *old, *new;
158
159    asprintf(&old, "%s.db", db->hdb_name);
160    asprintf(&new, "%s.db", new_name);
161    ret = rename(old, new);
162    free(old);
163    free(new);
164    if(ret)
165	return errno;
166
167    free(db->hdb_name);
168    db->hdb_name = strdup(new_name);
169    return 0;
170}
171
172static krb5_error_code
173DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
174{
175    DB *d = (DB*)db->hdb_db;
176    DBT k, v;
177    int code;
178
179    memset(&k, 0, sizeof(DBT));
180    memset(&v, 0, sizeof(DBT));
181    k.data = key.data;
182    k.size = key.length;
183    k.flags = 0;
184    if ((code = (*db->hdb_lock)(context, db, HDB_RLOCK)))
185	return code;
186    code = (*d->get)(d, NULL, &k, &v, 0);
187    (*db->hdb_unlock)(context, db);
188    if(code == DB_NOTFOUND)
189	return HDB_ERR_NOENTRY;
190    if(code)
191	return code;
192
193    krb5_data_copy(reply, v.data, v.size);
194    return 0;
195}
196
197static krb5_error_code
198DB__put(krb5_context context, HDB *db, int replace,
199	krb5_data key, krb5_data value)
200{
201    DB *d = (DB*)db->hdb_db;
202    DBT k, v;
203    int code;
204
205    memset(&k, 0, sizeof(DBT));
206    memset(&v, 0, sizeof(DBT));
207    k.data = key.data;
208    k.size = key.length;
209    k.flags = 0;
210    v.data = value.data;
211    v.size = value.length;
212    v.flags = 0;
213    if ((code = (*db->hdb_lock)(context, db, HDB_WLOCK)))
214	return code;
215    code = (*d->put)(d, NULL, &k, &v, replace ? 0 : DB_NOOVERWRITE);
216    (*db->hdb_unlock)(context, db);
217    if(code == DB_KEYEXIST)
218	return HDB_ERR_EXISTS;
219    if(code)
220	return errno;
221    return 0;
222}
223
224static krb5_error_code
225DB__del(krb5_context context, HDB *db, krb5_data key)
226{
227    DB *d = (DB*)db->hdb_db;
228    DBT k;
229    krb5_error_code code;
230    memset(&k, 0, sizeof(DBT));
231    k.data = key.data;
232    k.size = key.length;
233    k.flags = 0;
234    code = (*db->hdb_lock)(context, db, HDB_WLOCK);
235    if(code)
236	return code;
237    code = (*d->del)(d, NULL, &k, 0);
238    (*db->hdb_unlock)(context, db);
239    if(code == DB_NOTFOUND)
240	return HDB_ERR_NOENTRY;
241    if(code)
242	return code;
243    return 0;
244}
245
246static krb5_error_code
247DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
248{
249    DBC *dbc = NULL;
250    char *fn;
251    krb5_error_code ret;
252    DB *d;
253    int myflags = 0;
254
255    if (flags & O_CREAT)
256      myflags |= DB_CREATE;
257
258    if (flags & O_EXCL)
259      myflags |= DB_EXCL;
260
261    if((flags & O_ACCMODE) == O_RDONLY)
262      myflags |= DB_RDONLY;
263
264    if (flags & O_TRUNC)
265      myflags |= DB_TRUNCATE;
266
267    asprintf(&fn, "%s.db", db->hdb_name);
268    if (fn == NULL) {
269	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
270	return ENOMEM;
271    }
272    if (db_create(&d, NULL, 0) != 0) {
273	free(fn);
274	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
275	return ENOMEM;
276    }
277    db->hdb_db = d;
278
279#if (DB_VERSION_MAJOR >= 4) && (DB_VERSION_MINOR >= 1)
280    ret = (*d->open)(db->hdb_db, NULL, fn, NULL, DB_BTREE, myflags, mode);
281#else
282    ret = (*d->open)(db->hdb_db, fn, NULL, DB_BTREE, myflags, mode);
283#endif
284
285    if (ret == ENOENT) {
286	/* try to open without .db extension */
287#if (DB_VERSION_MAJOR >= 4) && (DB_VERSION_MINOR >= 1)
288	ret = (*d->open)(db->hdb_db, NULL, db->hdb_name, NULL, DB_BTREE,
289			 myflags, mode);
290#else
291	ret = (*d->open)(db->hdb_db, db->hdb_name, NULL, DB_BTREE,
292			 myflags, mode);
293#endif
294    }
295
296    if (ret) {
297	free(fn);
298	krb5_set_error_message(context, ret, "opening %s: %s",
299			      db->hdb_name, strerror(ret));
300	return ret;
301    }
302    free(fn);
303
304    ret = (*d->cursor)(d, NULL, &dbc, 0);
305    if (ret) {
306	krb5_set_error_message(context, ret, "d->cursor: %s", strerror(ret));
307        return ret;
308    }
309    db->hdb_dbc = dbc;
310
311    if((flags & O_ACCMODE) == O_RDONLY)
312	ret = hdb_check_db_format(context, db);
313    else
314	ret = hdb_init_db(context, db);
315    if(ret == HDB_ERR_NOENTRY)
316	return 0;
317    if (ret) {
318	DB_close(context, db);
319	krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
320			       (flags & O_ACCMODE) == O_RDONLY ?
321			       "checking format of" : "initialize",
322			       db->hdb_name);
323    }
324
325    return ret;
326}
327
328krb5_error_code
329hdb_db_create(krb5_context context, HDB **db,
330	      const char *filename)
331{
332    *db = calloc(1, sizeof(**db));
333    if (*db == NULL) {
334	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
335	return ENOMEM;
336    }
337
338    (*db)->hdb_db = NULL;
339    (*db)->hdb_name = strdup(filename);
340    if ((*db)->hdb_name == NULL) {
341	free(*db);
342	*db = NULL;
343	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
344	return ENOMEM;
345    }
346    (*db)->hdb_master_key_set = 0;
347    (*db)->hdb_openp = 0;
348    (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL;
349    (*db)->hdb_open  = DB_open;
350    (*db)->hdb_close = DB_close;
351    (*db)->hdb_fetch_kvno = _hdb_fetch_kvno;
352    (*db)->hdb_store = _hdb_store;
353    (*db)->hdb_remove = _hdb_remove;
354    (*db)->hdb_firstkey = DB_firstkey;
355    (*db)->hdb_nextkey= DB_nextkey;
356    (*db)->hdb_lock = DB_lock;
357    (*db)->hdb_unlock = DB_unlock;
358    (*db)->hdb_rename = DB_rename;
359    (*db)->hdb__get = DB__get;
360    (*db)->hdb__put = DB__put;
361    (*db)->hdb__del = DB__del;
362    (*db)->hdb_destroy = DB_destroy;
363    return 0;
364}
365#endif /* HAVE_DB3 */
366