db.c revision 178825
1/*
2 * Copyright (c) 1997 - 2001 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: db.c 20215 2007-02-09 21:59:53Z lha $");
37
38#if HAVE_DB1
39
40#if defined(HAVE_DB_185_H)
41#include <db_185.h>
42#elif defined(HAVE_DB_H)
43#include <db.h>
44#endif
45
46static krb5_error_code
47DB_close(krb5_context context, HDB *db)
48{
49    DB *d = (DB*)db->hdb_db;
50    (*d->close)(d);
51    return 0;
52}
53
54static krb5_error_code
55DB_destroy(krb5_context context, HDB *db)
56{
57    krb5_error_code ret;
58
59    ret = hdb_clear_master_key (context, db);
60    free(db->hdb_name);
61    free(db);
62    return ret;
63}
64
65static krb5_error_code
66DB_lock(krb5_context context, HDB *db, int operation)
67{
68    DB *d = (DB*)db->hdb_db;
69    int fd = (*d->fd)(d);
70    if(fd < 0) {
71	krb5_set_error_string(context,
72			      "Can't lock database: %s", db->hdb_name);
73	return HDB_ERR_CANT_LOCK_DB;
74    }
75    return hdb_lock(fd, operation);
76}
77
78static krb5_error_code
79DB_unlock(krb5_context context, HDB *db)
80{
81    DB *d = (DB*)db->hdb_db;
82    int fd = (*d->fd)(d);
83    if(fd < 0) {
84	krb5_set_error_string(context,
85			      "Can't unlock database: %s", db->hdb_name);
86	return HDB_ERR_CANT_LOCK_DB;
87    }
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    DB *d = (DB*)db->hdb_db;
97    DBT key, value;
98    krb5_data key_data, data;
99    int code;
100
101    code = db->hdb_lock(context, db, HDB_RLOCK);
102    if(code == -1) {
103	krb5_set_error_string(context, "Database %s in use", db->hdb_name);
104	return HDB_ERR_DB_INUSE;
105    }
106    code = (*d->seq)(d, &key, &value, flag);
107    db->hdb_unlock(context, db); /* XXX check value */
108    if(code == -1) {
109	code = errno;
110	krb5_set_error_string(context, "Database %s seq error: %s",
111			      db->hdb_name, strerror(code));
112	return code;
113    }
114    if(code == 1) {
115	krb5_clear_error_string(context);
116	return HDB_ERR_NOENTRY;
117    }
118
119    key_data.data = key.data;
120    key_data.length = key.size;
121    data.data = value.data;
122    data.length = value.size;
123    memset(entry, 0, sizeof(*entry));
124    if (hdb_value2entry(context, &data, &entry->entry))
125	return DB_seq(context, db, flags, entry, R_NEXT);
126    if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
127	code = hdb_unseal_keys (context, db, &entry->entry);
128	if (code)
129	    hdb_free_entry (context, entry);
130    }
131    if (code == 0 && entry->entry.principal == NULL) {
132	entry->entry.principal = malloc(sizeof(*entry->entry.principal));
133	if (entry->entry.principal == NULL) {
134	    krb5_set_error_string(context, "malloc: out of memory");
135	    code = ENOMEM;
136	    hdb_free_entry (context, entry);
137	} else {
138	    hdb_key2principal(context, &key_data, entry->entry.principal);
139	}
140    }
141    return code;
142}
143
144
145static krb5_error_code
146DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
147{
148    return DB_seq(context, db, flags, entry, R_FIRST);
149}
150
151
152static krb5_error_code
153DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
154{
155    return DB_seq(context, db, flags, entry, R_NEXT);
156}
157
158static krb5_error_code
159DB_rename(krb5_context context, HDB *db, const char *new_name)
160{
161    int ret;
162    char *old, *new;
163
164    asprintf(&old, "%s.db", db->hdb_name);
165    asprintf(&new, "%s.db", new_name);
166    ret = rename(old, new);
167    free(old);
168    free(new);
169    if(ret)
170	return errno;
171
172    free(db->hdb_name);
173    db->hdb_name = strdup(new_name);
174    return 0;
175}
176
177static krb5_error_code
178DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
179{
180    DB *d = (DB*)db->hdb_db;
181    DBT k, v;
182    int code;
183
184    k.data = key.data;
185    k.size = key.length;
186    code = db->hdb_lock(context, db, HDB_RLOCK);
187    if(code)
188	return code;
189    code = (*d->get)(d, &k, &v, 0);
190    db->hdb_unlock(context, db);
191    if(code < 0) {
192	code = errno;
193	krb5_set_error_string(context, "Database %s get error: %s",
194			      db->hdb_name, strerror(code));
195	return code;
196    }
197    if(code == 1) {
198	krb5_clear_error_string(context);
199	return HDB_ERR_NOENTRY;
200    }
201
202    krb5_data_copy(reply, v.data, v.size);
203    return 0;
204}
205
206static krb5_error_code
207DB__put(krb5_context context, HDB *db, int replace,
208	krb5_data key, krb5_data value)
209{
210    DB *d = (DB*)db->hdb_db;
211    DBT k, v;
212    int code;
213
214    k.data = key.data;
215    k.size = key.length;
216    v.data = value.data;
217    v.size = value.length;
218    code = db->hdb_lock(context, db, HDB_WLOCK);
219    if(code)
220	return code;
221    code = (*d->put)(d, &k, &v, replace ? 0 : R_NOOVERWRITE);
222    db->hdb_unlock(context, db);
223    if(code < 0) {
224	code = errno;
225	krb5_set_error_string(context, "Database %s put error: %s",
226			      db->hdb_name, strerror(code));
227	return code;
228    }
229    if(code == 1) {
230	krb5_clear_error_string(context);
231	return HDB_ERR_EXISTS;
232    }
233    return 0;
234}
235
236static krb5_error_code
237DB__del(krb5_context context, HDB *db, krb5_data key)
238{
239    DB *d = (DB*)db->hdb_db;
240    DBT k;
241    krb5_error_code code;
242    k.data = key.data;
243    k.size = key.length;
244    code = db->hdb_lock(context, db, HDB_WLOCK);
245    if(code)
246	return code;
247    code = (*d->del)(d, &k, 0);
248    db->hdb_unlock(context, db);
249    if(code == 1) {
250	code = errno;
251	krb5_set_error_string(context, "Database %s put error: %s",
252			      db->hdb_name, strerror(code));
253	return code;
254    }
255    if(code < 0)
256	return errno;
257    return 0;
258}
259
260static krb5_error_code
261DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
262{
263    char *fn;
264    krb5_error_code ret;
265
266    asprintf(&fn, "%s.db", db->hdb_name);
267    if (fn == NULL) {
268	krb5_set_error_string(context, "malloc: out of memory");
269	return ENOMEM;
270    }
271    db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
272    free(fn);
273    /* try to open without .db extension */
274    if(db->hdb_db == NULL && errno == ENOENT)
275	db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL);
276    if(db->hdb_db == NULL) {
277	ret = errno;
278	krb5_set_error_string(context, "dbopen (%s): %s",
279			      db->hdb_name, strerror(ret));
280	return ret;
281    }
282    if((flags & O_ACCMODE) == O_RDONLY)
283	ret = hdb_check_db_format(context, db);
284    else
285	ret = hdb_init_db(context, db);
286    if(ret == HDB_ERR_NOENTRY) {
287	krb5_clear_error_string(context);
288	return 0;
289    }
290    if (ret) {
291	DB_close(context, db);
292	krb5_set_error_string(context, "hdb_open: failed %s database %s",
293			      (flags & O_ACCMODE) == O_RDONLY ?
294			      "checking format of" : "initialize",
295			      db->hdb_name);
296    }
297    return ret;
298}
299
300krb5_error_code
301hdb_db_create(krb5_context context, HDB **db,
302	      const char *filename)
303{
304    *db = calloc(1, sizeof(**db));
305    if (*db == NULL) {
306	krb5_set_error_string(context, "malloc: out of memory");
307	return ENOMEM;
308    }
309
310    (*db)->hdb_db = NULL;
311    (*db)->hdb_name = strdup(filename);
312    if ((*db)->hdb_name == NULL) {
313	krb5_set_error_string(context, "malloc: out of memory");
314	free(*db);
315	*db = NULL;
316	return ENOMEM;
317    }
318    (*db)->hdb_master_key_set = 0;
319    (*db)->hdb_openp = 0;
320    (*db)->hdb_open = DB_open;
321    (*db)->hdb_close = DB_close;
322    (*db)->hdb_fetch = _hdb_fetch;
323    (*db)->hdb_store = _hdb_store;
324    (*db)->hdb_remove = _hdb_remove;
325    (*db)->hdb_firstkey = DB_firstkey;
326    (*db)->hdb_nextkey= DB_nextkey;
327    (*db)->hdb_lock = DB_lock;
328    (*db)->hdb_unlock = DB_unlock;
329    (*db)->hdb_rename = DB_rename;
330    (*db)->hdb__get = DB__get;
331    (*db)->hdb__put = DB__put;
332    (*db)->hdb__del = DB__del;
333    (*db)->hdb_destroy = DB_destroy;
334    return 0;
335}
336
337#endif /* HAVE_DB1 */
338