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