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