xref: /freebsd/crypto/heimdal/lib/hdb/db3.c (revision f0adf7f5cdd241db2f2c817683191a6ef64a4e95)
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 
36 RCSID("$Id: db3.c,v 1.8.6.1 2003/08/29 16:59:39 lha Exp $");
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 
48 static krb5_error_code
49 DB_close(krb5_context context, HDB *db)
50 {
51     DB *d = (DB*)db->db;
52     DBC *dbcp = (DBC*)db->dbc;
53 
54     dbcp->c_close(dbcp);
55     db->dbc = 0;
56     d->close(d, 0);
57     return 0;
58 }
59 
60 static krb5_error_code
61 DB_destroy(krb5_context context, HDB *db)
62 {
63     krb5_error_code ret;
64 
65     ret = hdb_clear_master_key (context, db);
66     free(db->name);
67     free(db);
68     return ret;
69 }
70 
71 static krb5_error_code
72 DB_lock(krb5_context context, HDB *db, int operation)
73 {
74     DB *d = (DB*)db->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 
81 static krb5_error_code
82 DB_unlock(krb5_context context, HDB *db)
83 {
84     DB *d = (DB*)db->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 
92 static krb5_error_code
93 DB_seq(krb5_context context, HDB *db,
94        unsigned flags, hdb_entry *entry, int flag)
95 {
96     DBT key, value;
97     DBC *dbcp = db->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->lock(context, db, HDB_RLOCK))
104 	return HDB_ERR_DB_INUSE;
105     code = dbcp->c_get(dbcp, &key, &value, flag);
106     db->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     if (hdb_value2entry(context, &data, entry))
117 	return DB_seq(context, db, flags, entry, DB_NEXT);
118     if (db->master_key_set && (flags & HDB_F_DECRYPT)) {
119 	code = hdb_unseal_keys (context, db, entry);
120 	if (code)
121 	    hdb_free_entry (context, entry);
122     }
123     if (entry->principal == NULL) {
124 	entry->principal = malloc(sizeof(*entry->principal));
125 	if (entry->principal == NULL) {
126 	    hdb_free_entry (context, entry);
127 	    krb5_set_error_string(context, "malloc: out of memory");
128 	    return ENOMEM;
129 	} else {
130 	    hdb_key2principal(context, &key_data, entry->principal);
131 	}
132     }
133     return 0;
134 }
135 
136 
137 static krb5_error_code
138 DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry)
139 {
140     return DB_seq(context, db, flags, entry, DB_FIRST);
141 }
142 
143 
144 static krb5_error_code
145 DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry)
146 {
147     return DB_seq(context, db, flags, entry, DB_NEXT);
148 }
149 
150 static krb5_error_code
151 DB_rename(krb5_context context, HDB *db, const char *new_name)
152 {
153     int ret;
154     char *old, *new;
155 
156     asprintf(&old, "%s.db", db->name);
157     asprintf(&new, "%s.db", new_name);
158     ret = rename(old, new);
159     free(old);
160     free(new);
161     if(ret)
162 	return errno;
163 
164     free(db->name);
165     db->name = strdup(new_name);
166     return 0;
167 }
168 
169 static krb5_error_code
170 DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
171 {
172     DB *d = (DB*)db->db;
173     DBT k, v;
174     int code;
175 
176     memset(&k, 0, sizeof(DBT));
177     memset(&v, 0, sizeof(DBT));
178     k.data = key.data;
179     k.size = key.length;
180     k.flags = 0;
181     if ((code = db->lock(context, db, HDB_RLOCK)))
182 	return code;
183     code = d->get(d, NULL, &k, &v, 0);
184     db->unlock(context, db);
185     if(code == DB_NOTFOUND)
186 	return HDB_ERR_NOENTRY;
187     if(code)
188 	return code;
189 
190     krb5_data_copy(reply, v.data, v.size);
191     return 0;
192 }
193 
194 static krb5_error_code
195 DB__put(krb5_context context, HDB *db, int replace,
196 	krb5_data key, krb5_data value)
197 {
198     DB *d = (DB*)db->db;
199     DBT k, v;
200     int code;
201 
202     memset(&k, 0, sizeof(DBT));
203     memset(&v, 0, sizeof(DBT));
204     k.data = key.data;
205     k.size = key.length;
206     k.flags = 0;
207     v.data = value.data;
208     v.size = value.length;
209     v.flags = 0;
210     if ((code = db->lock(context, db, HDB_WLOCK)))
211 	return code;
212     code = d->put(d, NULL, &k, &v, replace ? 0 : DB_NOOVERWRITE);
213     db->unlock(context, db);
214     if(code == DB_KEYEXIST)
215 	return HDB_ERR_EXISTS;
216     if(code)
217 	return errno;
218     return 0;
219 }
220 
221 static krb5_error_code
222 DB__del(krb5_context context, HDB *db, krb5_data key)
223 {
224     DB *d = (DB*)db->db;
225     DBT k;
226     krb5_error_code code;
227     memset(&k, 0, sizeof(DBT));
228     k.data = key.data;
229     k.size = key.length;
230     k.flags = 0;
231     code = db->lock(context, db, HDB_WLOCK);
232     if(code)
233 	return code;
234     code = d->del(d, NULL, &k, 0);
235     db->unlock(context, db);
236     if(code == DB_NOTFOUND)
237 	return HDB_ERR_NOENTRY;
238     if(code)
239 	return code;
240     return 0;
241 }
242 
243 static krb5_error_code
244 DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
245 {
246     char *fn;
247     krb5_error_code ret;
248     DB *d;
249     int myflags = 0;
250 
251     if (flags & O_CREAT)
252       myflags |= DB_CREATE;
253 
254     if (flags & O_EXCL)
255       myflags |= DB_EXCL;
256 
257     if (flags & O_RDONLY)
258       myflags |= DB_RDONLY;
259 
260     if (flags & O_TRUNC)
261       myflags |= DB_TRUNCATE;
262 
263     asprintf(&fn, "%s.db", db->name);
264     if (fn == NULL) {
265 	krb5_set_error_string(context, "malloc: out of memory");
266 	return ENOMEM;
267     }
268     db_create(&d, NULL, 0);
269     db->db = d;
270 #if (DB_VERSION_MAJOR > 3) && (DB_VERSION_MINOR > 0)
271     if ((ret = d->open(db->db, NULL, fn, NULL, DB_BTREE, myflags, mode))) {
272 #else
273     if ((ret = d->open(db->db, fn, NULL, DB_BTREE, myflags, mode))) {
274 #endif
275       if(ret == ENOENT)
276 	/* try to open without .db extension */
277 #if (DB_VERSION_MAJOR > 3) && (DB_VERSION_MINOR > 0)
278 	if (d->open(db->db, NULL, db->name, NULL, DB_BTREE, myflags, mode)) {
279 #else
280 	if (d->open(db->db, db->name, NULL, DB_BTREE, myflags, mode)) {
281 #endif
282 	  free(fn);
283 	  krb5_set_error_string(context, "opening %s: %s",
284 				db->name, strerror(ret));
285 	  return ret;
286 	}
287     }
288     free(fn);
289 
290     ret = d->cursor(d, NULL, (DBC **)&db->dbc, 0);
291     if (ret) {
292 	krb5_set_error_string(context, "d->cursor: %s", strerror(ret));
293         return ret;
294     }
295 
296     if((flags & O_ACCMODE) == O_RDONLY)
297 	ret = hdb_check_db_format(context, db);
298     else
299 	ret = hdb_init_db(context, db);
300     if(ret == HDB_ERR_NOENTRY)
301 	return 0;
302     return ret;
303 }
304 
305 krb5_error_code
306 hdb_db_create(krb5_context context, HDB **db,
307 	      const char *filename)
308 {
309     *db = malloc(sizeof(**db));
310     if (*db == NULL) {
311 	krb5_set_error_string(context, "malloc: out of memory");
312 	return ENOMEM;
313     }
314 
315     (*db)->db = NULL;
316     (*db)->name = strdup(filename);
317     if ((*db)->name == NULL) {
318 	krb5_set_error_string(context, "malloc: out of memory");
319 	free(*db);
320 	*db = NULL;
321 	return ENOMEM;
322     }
323     (*db)->master_key_set = 0;
324     (*db)->openp = 0;
325     (*db)->open  = DB_open;
326     (*db)->close = DB_close;
327     (*db)->fetch = _hdb_fetch;
328     (*db)->store = _hdb_store;
329     (*db)->remove = _hdb_remove;
330     (*db)->firstkey = DB_firstkey;
331     (*db)->nextkey= DB_nextkey;
332     (*db)->lock = DB_lock;
333     (*db)->unlock = DB_unlock;
334     (*db)->rename = DB_rename;
335     (*db)->_get = DB__get;
336     (*db)->_put = DB__put;
337     (*db)->_del = DB__del;
338     (*db)->destroy = DB_destroy;
339     return 0;
340 }
341 #endif /* HAVE_DB3 */
342