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