xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/hdb/db3.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: db3.c,v 1.2 2017/01/28 21:31:48 christos 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 #include <fcntl.h>
39 
40 #if HAVE_DB3
41 
42 #ifdef HAVE_DBHEADER
43 #include <db.h>
44 #elif HAVE_DB6_DB_H
45 #include <db6/db.h>
46 #elif HAVE_DB5_DB_H
47 #include <db5/db.h>
48 #elif HAVE_DB4_DB_H
49 #include <db4/db.h>
50 #elif HAVE_DB3_DB_H
51 #include <db3/db.h>
52 #else
53 #include <db.h>
54 #endif
55 
56 typedef struct {
57     HDB hdb;            /* generic members */
58     int lock_fd;        /* DB3-specific */
59 } DB3_HDB;
60 
61 
62 static krb5_error_code
63 DB_close(krb5_context context, HDB *db)
64 {
65     DB3_HDB *db3 = (DB3_HDB *)db;
66     DB *d = (DB*)db->hdb_db;
67     DBC *dbcp = (DBC*)db->hdb_dbc;
68 
69     heim_assert(d != 0, "Closing already closed HDB");
70 
71     if (dbcp != NULL)
72 	dbcp->c_close(dbcp);
73     if (d != NULL)
74 	d->close(d, 0);
75     if (db3->lock_fd >= 0)
76 	close(db3->lock_fd);
77 
78     db3->lock_fd = -1;
79     db->hdb_dbc = 0;
80     db->hdb_db = 0;
81 
82     return 0;
83 }
84 
85 static krb5_error_code
86 DB_destroy(krb5_context context, HDB *db)
87 {
88     krb5_error_code ret;
89 
90     ret = hdb_clear_master_key (context, db);
91     free(db->hdb_name);
92     free(db);
93     return ret;
94 }
95 
96 static krb5_error_code
97 DB_lock(krb5_context context, HDB *db, int operation)
98 {
99 
100     return 0;
101 }
102 
103 static krb5_error_code
104 DB_unlock(krb5_context context, HDB *db)
105 {
106 
107     return 0;
108 }
109 
110 
111 static krb5_error_code
112 DB_seq(krb5_context context, HDB *db,
113        unsigned flags, hdb_entry_ex *entry, int flag)
114 {
115     DBT key, value;
116     DBC *dbcp = db->hdb_dbc;
117     krb5_data key_data, data;
118     int code;
119 
120     memset(&key, 0, sizeof(DBT));
121     memset(&value, 0, sizeof(DBT));
122     code = (*dbcp->c_get)(dbcp, &key, &value, flag);
123     if (code == DB_NOTFOUND)
124 	return HDB_ERR_NOENTRY;
125     if (code)
126 	return code;
127 
128     key_data.data = key.data;
129     key_data.length = key.size;
130     data.data = value.data;
131     data.length = value.size;
132     memset(entry, 0, sizeof(*entry));
133     if (hdb_value2entry(context, &data, &entry->entry))
134 	return DB_seq(context, db, flags, entry, DB_NEXT);
135     if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
136 	code = hdb_unseal_keys (context, db, &entry->entry);
137 	if (code)
138 	    hdb_free_entry (context, entry);
139     }
140     if (entry->entry.principal == NULL) {
141 	entry->entry.principal = malloc(sizeof(*entry->entry.principal));
142 	if (entry->entry.principal == NULL) {
143 	    hdb_free_entry (context, entry);
144 	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
145 	    return ENOMEM;
146 	} else {
147 	    hdb_key2principal(context, &key_data, entry->entry.principal);
148 	}
149     }
150     return 0;
151 }
152 
153 
154 static krb5_error_code
155 DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
156 {
157     return DB_seq(context, db, flags, entry, DB_FIRST);
158 }
159 
160 
161 static krb5_error_code
162 DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
163 {
164     return DB_seq(context, db, flags, entry, DB_NEXT);
165 }
166 
167 static krb5_error_code
168 DB_rename(krb5_context context, HDB *db, const char *new_name)
169 {
170     int ret;
171     char *old, *new;
172 
173     if (strncmp(new_name, "db:", sizeof("db:") - 1) == 0)
174         new_name += sizeof("db:") - 1;
175     else if (strncmp(new_name, "db3:", sizeof("db3:") - 1) == 0)
176         new_name += sizeof("db3:") - 1;
177 
178     ret = asprintf(&old, "%s.db", db->hdb_name);
179     if (ret == -1)
180 	return ENOMEM;
181     ret = asprintf(&new, "%s.db", new_name);
182     if (ret == -1) {
183 	free(old);
184 	return ENOMEM;
185     }
186     ret = rename(old, new);
187     free(old);
188     if(ret) {
189 	free(new);
190 	return errno;
191     }
192 
193     free(db->hdb_name);
194     new[strlen(new) - 3] = '\0';
195     db->hdb_name = new;
196     return 0;
197 }
198 
199 static krb5_error_code
200 DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
201 {
202     DB *d = (DB*)db->hdb_db;
203     DBT k, v;
204     int code;
205 
206     memset(&k, 0, sizeof(DBT));
207     memset(&v, 0, sizeof(DBT));
208     k.data = key.data;
209     k.size = key.length;
210     k.flags = 0;
211     code = (*d->get)(d, NULL, &k, &v, 0);
212     if(code == DB_NOTFOUND)
213 	return HDB_ERR_NOENTRY;
214     if(code)
215 	return code;
216 
217     krb5_data_copy(reply, v.data, v.size);
218     return 0;
219 }
220 
221 static krb5_error_code
222 DB__put(krb5_context context, HDB *db, int replace,
223 	krb5_data key, krb5_data value)
224 {
225     DB *d = (DB*)db->hdb_db;
226     DBT k, v;
227     int code;
228 
229     memset(&k, 0, sizeof(DBT));
230     memset(&v, 0, sizeof(DBT));
231     k.data = key.data;
232     k.size = key.length;
233     k.flags = 0;
234     v.data = value.data;
235     v.size = value.length;
236     v.flags = 0;
237     code = (*d->put)(d, NULL, &k, &v, replace ? 0 : DB_NOOVERWRITE);
238     if(code == DB_KEYEXIST)
239 	return HDB_ERR_EXISTS;
240     if (code) {
241         /*
242          * Berkeley DB 3 and up have a terrible error reporting
243          * interface...
244          *
245          * DB->err() doesn't output a string.
246          * DB->set_errcall()'s callback function doesn't have a void *
247          * argument that can be used to place the error somewhere.
248          *
249          * The only thing we could do is fopen()/fdopen() a file, set it
250          * with DB->set_errfile(), then call DB->err(), then read the
251          * message from the file, unset it with DB->set_errfile(), close
252          * it and delete it.  That's a lot of work... so we don't do it.
253          */
254         if (code == EACCES || code == ENOSPC || code == EINVAL) {
255             krb5_set_error_message(context, code,
256                                    "Database %s put error: %s",
257                                    db->hdb_name, strerror(code));
258         } else {
259             code = HDB_ERR_UK_SERROR;
260             krb5_set_error_message(context, code,
261                                    "Database %s put error: unknown (%d)",
262                                    db->hdb_name, code);
263         }
264 	return code;
265     }
266     code = (*d->sync)(d, 0);
267     if (code) {
268         if (code == EACCES || code == ENOSPC || code == EINVAL) {
269             krb5_set_error_message(context, code,
270                                    "Database %s put sync error: %s",
271                                    db->hdb_name, strerror(code));
272         } else {
273             code = HDB_ERR_UK_SERROR;
274             krb5_set_error_message(context, code,
275                                    "Database %s put sync error: unknown (%d)",
276                                    db->hdb_name, code);
277         }
278         return code;
279     }
280     return 0;
281 }
282 
283 static krb5_error_code
284 DB__del(krb5_context context, HDB *db, krb5_data key)
285 {
286     DB *d = (DB*)db->hdb_db;
287     DBT k;
288     krb5_error_code code;
289     memset(&k, 0, sizeof(DBT));
290     k.data = key.data;
291     k.size = key.length;
292     k.flags = 0;
293     code = (*d->del)(d, NULL, &k, 0);
294     if(code == DB_NOTFOUND)
295 	return HDB_ERR_NOENTRY;
296     if (code) {
297         if (code == EACCES || code == ENOSPC || code == EINVAL) {
298             krb5_set_error_message(context, code,
299                                    "Database %s del error: %s",
300                                    db->hdb_name, strerror(code));
301         } else {
302             code = HDB_ERR_UK_SERROR;
303             krb5_set_error_message(context, code,
304                                    "Database %s del error: unknown (%d)",
305                                    db->hdb_name, code);
306         }
307 	return code;
308     }
309     code = (*d->sync)(d, 0);
310     if (code) {
311         if (code == EACCES || code == ENOSPC || code == EINVAL) {
312             krb5_set_error_message(context, code,
313                                    "Database %s del sync error: %s",
314                                    db->hdb_name, strerror(code));
315         } else {
316             code = HDB_ERR_UK_SERROR;
317             krb5_set_error_message(context, code,
318                                    "Database %s del sync error: unknown (%d)",
319                                    db->hdb_name, code);
320         }
321         return code;
322     }
323     return 0;
324 }
325 
326 #define RD_CACHE_SZ 0x8000     /* Minimal read cache size */
327 #define WR_CACHE_SZ 0x8000     /* Minimal write cache size */
328 
329 static int
330 _open_db(DB *d, char *fn, int myflags, int flags, mode_t mode, int *fd)
331 {
332     int ret;
333     int cache_size = (myflags & DB_RDONLY) ? RD_CACHE_SZ : WR_CACHE_SZ;
334 
335     *fd = open(fn, flags, mode);
336 
337     if (*fd == -1)
338        return errno;
339 
340     /*
341      * Without DB_FCNTL_LOCKING, the DB library complains when initializing
342      * a database in an empty file. Since the database is our lock file,
343      * we create it before Berkeley DB does, so a new DB always starts empty.
344      */
345     myflags |= DB_FCNTL_LOCKING;
346 
347     ret = flock(*fd, (myflags&DB_RDONLY) ? LOCK_SH : LOCK_EX);
348     if (ret == -1) {
349 	ret = errno;
350 	close(*fd);
351 	*fd = -1;
352 	return ret;
353     }
354 
355     d->set_cachesize(d, 0, cache_size, 0);
356 
357 #if (DB_VERSION_MAJOR > 4) || ((DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR >= 1))
358     ret = (*d->open)(d, NULL, fn, NULL, DB_BTREE, myflags, mode);
359 #else
360     ret = (*d->open)(d, fn, NULL, DB_BTREE, myflags, mode);
361 #endif
362 
363     if (ret != 0) {
364 	close(*fd);
365 	*fd = -1;
366     }
367 
368     return ret;
369 }
370 
371 static krb5_error_code
372 DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
373 {
374     DB3_HDB *db3 = (DB3_HDB *)db;
375     DBC *dbc = NULL;
376     char *fn;
377     krb5_error_code ret;
378     DB *d;
379     int myflags = 0;
380     int aret;
381 
382     heim_assert(db->hdb_db == 0, "Opening already open HDB");
383 
384     if (flags & O_CREAT)
385       myflags |= DB_CREATE;
386 
387     if (flags & O_EXCL)
388       myflags |= DB_EXCL;
389 
390     if((flags & O_ACCMODE) == O_RDONLY)
391       myflags |= DB_RDONLY;
392 
393     if (flags & O_TRUNC)
394       myflags |= DB_TRUNCATE;
395 
396     aret = asprintf(&fn, "%s.db", db->hdb_name);
397     if (aret == -1) {
398 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
399 	return ENOMEM;
400     }
401 
402     if (db_create(&d, NULL, 0) != 0) {
403 	free(fn);
404 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
405 	return ENOMEM;
406     }
407     db->hdb_db = d;
408 
409     /* From here on out always DB_close() before returning on error */
410 
411     ret = _open_db(d, fn, myflags, flags, mode, &db3->lock_fd);
412     free(fn);
413     if (ret == ENOENT) {
414 	/* try to open without .db extension */
415 	ret = _open_db(d, db->hdb_name, myflags, flags, mode, &db3->lock_fd);
416     }
417 
418     if (ret) {
419 	DB_close(context, db);
420 	krb5_set_error_message(context, ret, "opening %s: %s",
421 			       db->hdb_name, strerror(ret));
422 	return ret;
423     }
424 
425 #ifndef DB_CURSOR_BULK
426 # define DB_CURSOR_BULK 0	/* Missing with DB < 4.8 */
427 #endif
428     ret = (*d->cursor)(d, NULL, &dbc, DB_CURSOR_BULK);
429 
430     if (ret) {
431 	DB_close(context, db);
432 	krb5_set_error_message(context, ret, "d->cursor: %s", strerror(ret));
433         return ret;
434     }
435     db->hdb_dbc = dbc;
436 
437     if((flags & O_ACCMODE) == O_RDONLY)
438 	ret = hdb_check_db_format(context, db);
439     else
440 	ret = hdb_init_db(context, db);
441     if(ret == HDB_ERR_NOENTRY)
442 	return 0;
443     if (ret) {
444 	DB_close(context, db);
445 	krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
446 			       (flags & O_ACCMODE) == O_RDONLY ?
447 			       "checking format of" : "initialize",
448 			       db->hdb_name);
449     }
450 
451     return ret;
452 }
453 
454 krb5_error_code
455 hdb_db3_create(krb5_context context, HDB **db,
456 	       const char *filename)
457 {
458     DB3_HDB **db3 = (DB3_HDB **)db;
459     *db3 = calloc(1, sizeof(**db3));    /* Allocate space for the larger db3 */
460     if (*db == NULL) {
461 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
462 	return ENOMEM;
463     }
464 
465     (*db)->hdb_db = NULL;
466     (*db)->hdb_name = strdup(filename);
467     if ((*db)->hdb_name == NULL) {
468 	free(*db);
469 	*db = NULL;
470 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
471 	return ENOMEM;
472     }
473     (*db)->hdb_master_key_set = 0;
474     (*db)->hdb_openp = 0;
475     (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL;
476     (*db)->hdb_open  = DB_open;
477     (*db)->hdb_close = DB_close;
478     (*db)->hdb_fetch_kvno = _hdb_fetch_kvno;
479     (*db)->hdb_store = _hdb_store;
480     (*db)->hdb_remove = _hdb_remove;
481     (*db)->hdb_firstkey = DB_firstkey;
482     (*db)->hdb_nextkey= DB_nextkey;
483     (*db)->hdb_lock = DB_lock;
484     (*db)->hdb_unlock = DB_unlock;
485     (*db)->hdb_rename = DB_rename;
486     (*db)->hdb__get = DB__get;
487     (*db)->hdb__put = DB__put;
488     (*db)->hdb__del = DB__del;
489     (*db)->hdb_destroy = DB_destroy;
490 
491     (*db3)->lock_fd = -1;
492     return 0;
493 }
494 #endif /* HAVE_DB3 */
495