]> the.earth.li Git - onak.git/blob - keydb_db4.c
Use dynamic context for all backend databases
[onak.git] / keydb_db4.c
1 /*
2  * keydb_db4.c - Routines to store and fetch keys in a DB4 database.
3  *
4  * Copyright 2002-2008 Jonathan McDowell <noodles@earth.li>
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 51
17  * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/uio.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include <db.h>
32
33 #include "charfuncs.h"
34 #include "keyarray.h"
35 #include "keydb.h"
36 #include "keyid.h"
37 #include "decodekey.h"
38 #include "keystructs.h"
39 #include "mem.h"
40 #include "log.h"
41 #include "onak-conf.h"
42 #include "parsekey.h"
43 #include "wordlist.h"
44
45 #define DB4_UPGRADE_FILE "db_upgrade.lck"
46
47 struct onak_db4_dbctx {
48         DB_ENV *dbenv;  /* The database environment context */
49         int numdbs;     /* Number of data databases in use */
50         DB **dbconns;   /* Connections to the key data databases */
51         DB *worddb;     /* Connection to the word lookup database */
52         DB *id32db;     /* Connection to the 32 bit ID lookup database */
53         DB *skshashdb;  /* Connection to the SKS hash database */
54         DB *subkeydb;   /* Connection to the subkey ID lookup database */
55         DB_TXN *txn;    /* Our current transaction ID */
56 };
57
58 DB *keydb(struct onak_db4_dbctx *privctx, uint64_t keyid)
59 {
60         uint64_t keytrun;
61
62         keytrun = keyid >> 8;
63
64         return(privctx->dbconns[keytrun % privctx->numdbs]);
65 }
66
67 /**
68  *      db4_errfunc - Direct DB errors to logfile
69  *
70  *      Basic function to take errors from the DB library and output them to
71  *      the logfile rather than stderr.
72  */
73 #if (DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR < 3)
74 static void db4_errfunc(const char *errpfx, const char *errmsg)
75 #else
76 static void db4_errfunc(const DB_ENV *edbenv, const char *errpfx,
77                 const char *errmsg)
78 #endif
79 {
80         if (errpfx) {
81                 logthing(LOGTHING_DEBUG, "db4 error: %s:%s", errpfx, errmsg);
82         } else {
83                 logthing(LOGTHING_DEBUG, "db4 error: %s", errmsg);
84         }
85
86         return;
87 }
88
89 /**
90  *      starttrans - Start a transaction.
91  *
92  *      Start a transaction. Intended to be used if we're about to perform many
93  *      operations on the database to help speed it all up, or if we want
94  *      something to only succeed if all relevant operations are successful.
95  */
96 static bool db4_starttrans(struct onak_dbctx *dbctx)
97 {
98         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
99         int ret;
100
101         log_assert(privctx->dbenv != NULL);
102         log_assert(privctx->txn == NULL);
103
104         ret = privctx->dbenv->txn_begin(privctx->dbenv,
105                 NULL, /* No parent transaction */
106                 &privctx->txn,
107                 0);
108         if (ret != 0) {
109                 logthing(LOGTHING_CRITICAL,
110                                 "Error starting transaction: %s",
111                                 db_strerror(ret));
112                 exit(1);
113         }
114
115         return true;
116 }
117
118 /**
119  *      endtrans - End a transaction.
120  *
121  *      Ends a transaction.
122  */
123 static void db4_endtrans(struct onak_dbctx *dbctx)
124 {
125         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
126         int ret;
127
128         log_assert(privctx->dbenv != NULL);
129         log_assert(privctx->txn != NULL);
130
131         ret = privctx->txn->commit(privctx->txn,
132                 0);
133         if (ret != 0) {
134                 logthing(LOGTHING_CRITICAL,
135                                 "Error ending transaction: %s",
136                                 db_strerror(ret));
137                 exit(1);
138         }
139         privctx->txn = NULL;
140
141         return;
142 }
143
144 /**
145  *      db4_upgradedb - Upgrade a DB4 database
146  *
147  *      Called if we discover we need to upgrade our DB4 database; ie if
148  *      we're running with a newer version of db4 than the database was
149  *      created with.
150  */
151 static int db4_upgradedb(struct onak_db4_dbctx *privctx)
152 {
153         DB *curdb = NULL;
154         int ret;
155         int i;
156         char buf[1024];
157         int lockfile_fd;
158         struct stat statbuf;
159
160         snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
161                         DB4_UPGRADE_FILE);
162         lockfile_fd = open(buf, O_RDWR | O_CREAT | O_EXCL, 0600);
163         if (lockfile_fd < 0) {
164                 if (errno == EEXIST) {
165                         while (stat(buf, &statbuf) == 0) ;
166                         return 0;
167                 } else {
168                         logthing(LOGTHING_CRITICAL, "Couldn't open database "
169                                 "update lock file: %s", strerror(errno));
170                         return -1;
171                 }
172         }
173         snprintf(buf, sizeof(buf) - 1, "%d", getpid());
174         write(lockfile_fd, buf, strlen(buf));
175         close(lockfile_fd);
176
177         logthing(LOGTHING_NOTICE, "Upgrading DB4 database");
178         ret = db_env_create(&privctx->dbenv, 0);
179         privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
180         privctx->dbenv->remove(privctx->dbenv, config.db_dir, 0);
181         privctx->dbenv = NULL;
182         for (i = 0; i < privctx->numdbs; i++) {
183                 ret = db_create(&curdb, NULL, 0);
184                 if (ret == 0) {
185                         snprintf(buf, sizeof(buf) - 1, "%s/keydb.%d.db",
186                                 config.db_dir, i);
187                         logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
188                         ret = curdb->upgrade(curdb, buf, 0);
189                         curdb->close(curdb, 0);
190                 } else {
191                         logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
192                                 buf,
193                                 db_strerror(ret));
194                 }
195         }
196
197         ret = db_create(&curdb, NULL, 0);
198         if (ret == 0) {
199                 snprintf(buf, sizeof(buf) - 1, "%s/worddb", config.db_dir);
200                 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
201                 ret = curdb->upgrade(curdb, buf, 0);
202                 curdb->close(curdb, 0);
203         } else {
204                 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
205                         buf,
206                         db_strerror(ret));
207         }
208
209         ret = db_create(&curdb, NULL, 0);
210         if (ret == 0) {
211                 snprintf(buf, sizeof(buf) - 1, "%s/id32db", config.db_dir);
212                 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
213                 ret = curdb->upgrade(curdb, buf, 0);
214                 curdb->close(curdb, 0);
215         } else {
216                 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
217                         buf,
218                         db_strerror(ret));
219         }
220
221         ret = db_create(&curdb, NULL, 0);
222         if (ret == 0) {
223                 snprintf(buf, sizeof(buf) - 1, "%s/skshashdb", config.db_dir);
224                 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
225                 ret = curdb->upgrade(curdb, buf, 0);
226                 curdb->close(curdb, 0);
227         } else {
228                 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
229                         buf,
230                         db_strerror(ret));
231         }
232
233         ret = db_create(&curdb, NULL, 0);
234         if (ret == 0) {
235                 snprintf(buf, sizeof(buf) - 1, "%s/subkeydb", config.db_dir);
236                 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
237                 ret = curdb->upgrade(curdb, buf, 0);
238                 curdb->close(curdb, 0);
239         } else {
240                 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
241                         buf,
242                         db_strerror(ret));
243         }
244
245         snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
246                         DB4_UPGRADE_FILE);
247         unlink(buf);
248
249         return ret;
250 }
251
252 /**
253  *      getfullkeyid - Maps a 32bit key id to a 64bit one.
254  *      @keyid: The 32bit keyid.
255  *
256  *      This function maps a 32bit key id to the full 64bit one. It returns the
257  *      full keyid. If the key isn't found a keyid of 0 is returned.
258  */
259 static uint64_t db4_getfullkeyid(struct onak_dbctx *dbctx, uint64_t keyid)
260 {
261         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
262         DBT       key, data;
263         DBC      *cursor = NULL;
264         uint32_t  shortkeyid = 0;
265         int       ret = 0;
266
267         if (keyid < 0x100000000LL) {
268                 ret = privctx->id32db->cursor(privctx->id32db,
269                                 privctx->txn,
270                                 &cursor,
271                                 0);   /* flags */
272
273                 shortkeyid = keyid & 0xFFFFFFFF;
274
275                 memset(&key, 0, sizeof(key));
276                 memset(&data, 0, sizeof(data));
277                 key.data = &shortkeyid;
278                 key.size = sizeof(shortkeyid);
279                 data.flags = DB_DBT_MALLOC;
280
281                 ret = cursor->c_get(cursor,
282                         &key,
283                         &data,
284                         DB_SET);
285
286                 if (ret == 0) {
287                         keyid = *(uint64_t *) data.data;
288
289                         if (data.data != NULL) {
290                                 free(data.data);
291                                 data.data = NULL;
292                         }
293                 }
294
295                 ret = cursor->c_close(cursor);
296                 cursor = NULL;
297         }
298
299         return keyid;
300 }
301
302 /**
303  *      fetch_key_id - Given a keyid fetch the key from storage.
304  *      @keyid: The keyid to fetch.
305  *      @publickey: A pointer to a structure to return the key in.
306  *      @intrans: If we're already in a transaction.
307  *
308  *      We use the hex representation of the keyid as the filename to fetch the
309  *      key from. The key is stored in the file as a binary OpenPGP stream of
310  *      packets, so we can just use read_openpgp_stream() to read the packets
311  *      in and then parse_keys() to parse the packets into a publickey
312  *      structure.
313  */
314 static int db4_fetch_key_id(struct onak_dbctx *dbctx, uint64_t keyid,
315                 struct openpgp_publickey **publickey,
316                 bool intrans)
317 {
318         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
319         struct openpgp_packet_list *packets = NULL;
320         DBT key, data;
321         int ret = 0;
322         int numkeys = 0;
323         struct buffer_ctx fetchbuf;
324
325         if (keyid < 0x100000000LL) {
326                 keyid = db4_getfullkeyid(dbctx, keyid);
327         }
328
329         memset(&key, 0, sizeof(key));
330         memset(&data, 0, sizeof(data));
331
332         data.size = 0;
333         data.data = NULL;
334
335         key.size = sizeof(keyid);
336         key.data = &keyid;
337
338         if (!intrans) {
339                 db4_starttrans(dbctx);
340         }
341
342         ret = keydb(privctx, keyid)->get(keydb(privctx, keyid),
343                         privctx->txn,
344                         &key,
345                         &data,
346                         0); /* flags*/
347
348         if (ret == DB_NOTFOUND) {
349                 /* If we didn't find the key ID see if it's a subkey ID */
350                 memset(&key, 0, sizeof(key));
351                 memset(&data, 0, sizeof(data));
352                 data.size = 0;
353                 data.data = NULL;
354                 key.size = sizeof(keyid);
355                 key.data = &keyid;
356
357                 ret = privctx->subkeydb->get(privctx->subkeydb,
358                         privctx->txn,
359                         &key,
360                         &data,
361                         0); /* flags*/
362
363                 if (ret == 0) {
364                         /* We got a subkey match; retrieve the actual key */
365                         keyid = *(uint64_t *) data.data;
366
367                         memset(&key, 0, sizeof(key));
368                         memset(&data, 0, sizeof(data));
369                         data.size = 0;
370                         data.data = NULL;
371                         key.size = sizeof(keyid);
372                         key.data = &keyid;
373
374                         ret = keydb(privctx, keyid)->get(keydb(privctx, keyid),
375                                 privctx->txn,
376                                 &key,
377                                 &data,
378                                 0); /* flags*/
379                 }
380         }
381
382         if (ret == 0) {
383                 fetchbuf.buffer = data.data;
384                 fetchbuf.offset = 0;
385                 fetchbuf.size = data.size;
386                 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
387                                 &packets, 0);
388                 parse_keys(packets, publickey);
389                 free_packet_list(packets);
390                 packets = NULL;
391                 numkeys++;
392         } else if (ret != DB_NOTFOUND) {
393                 logthing(LOGTHING_ERROR,
394                                 "Problem retrieving key: %s",
395                                 db_strerror(ret));
396         }
397
398         if (!intrans) {
399                 db4_endtrans(dbctx);
400         }
401
402         return (numkeys);
403 }
404
405 int worddb_cmp(const void *d1, const void *d2)
406 {
407         return memcmp(d1, d2, 12);
408 }
409
410 /**
411  *      fetch_key_text - Trys to find the keys that contain the supplied text.
412  *      @search: The text to search for.
413  *      @publickey: A pointer to a structure to return the key in.
414  *
415  *      This function searches for the supplied text and returns the keys that
416  *      contain it.
417  */
418 static int db4_fetch_key_text(struct onak_dbctx *dbctx, const char *search,
419                 struct openpgp_publickey **publickey)
420 {
421         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
422         DBC *cursor = NULL;
423         DBT key, data;
424         int ret;
425         uint64_t keyid;
426         int i;
427         int numkeys;
428         char *searchtext = NULL;
429         struct ll *wordlist = NULL;
430         struct ll *curword = NULL;
431         struct keyarray keylist = { NULL, 0, 0 };
432         struct keyarray newkeylist = { NULL, 0, 0 };
433         int firstpass = 1;
434
435         numkeys = 0;
436         searchtext = strdup(search);
437         wordlist = makewordlist(wordlist, searchtext);
438
439         for (curword = wordlist; curword != NULL; curword = curword->next) {
440                 db4_starttrans(dbctx);
441
442                 ret = privctx->worddb->cursor(privctx->worddb,
443                                 privctx->txn,
444                                 &cursor,
445                                 0);   /* flags */
446
447                 memset(&key, 0, sizeof(key));
448                 memset(&data, 0, sizeof(data));
449                 key.data = curword->object;
450                 key.size = strlen(curword->object);
451                 data.flags = DB_DBT_MALLOC;
452                 ret = cursor->c_get(cursor,
453                                 &key,
454                                 &data,
455                                 DB_SET);
456                 while (ret == 0 && strncmp(key.data, curword->object,
457                                         key.size) == 0 &&
458                                 ((char *) curword->object)[key.size] == 0) {
459                         keyid = 0;
460                         for (i = 4; i < 12; i++) {
461                                 keyid <<= 8;
462                                 keyid += ((unsigned char *)
463                                                 data.data)[i];
464                         }
465
466                         /*
467                          * Only add the keys containing this word if this is
468                          * our first pass (ie we have no existing key list),
469                          * or the key contained a previous word.
470                          */
471                         if (firstpass || array_find(&keylist, keyid)) {
472                                 array_add(&newkeylist, keyid);
473                         }
474
475                         free(data.data);
476                         data.data = NULL;
477
478                         ret = cursor->c_get(cursor,
479                                         &key,
480                                         &data,
481                                         DB_NEXT);
482                 }
483                 array_free(&keylist);
484                 keylist = newkeylist;
485                 newkeylist.keys = NULL;
486                 newkeylist.count = newkeylist.size = 0;
487                 if (data.data != NULL) {
488                         free(data.data);
489                         data.data = NULL;
490                 }
491                 ret = cursor->c_close(cursor);
492                 cursor = NULL;
493                 firstpass = 0;
494                 db4_endtrans(dbctx);
495         }
496         llfree(wordlist, NULL);
497         wordlist = NULL;
498
499         if (keylist.count > config.maxkeys) {
500                 keylist.count = config.maxkeys;
501         }
502
503         db4_starttrans(dbctx);
504         for (i = 0; i < keylist.count; i++) {
505                 numkeys += db4_fetch_key_id(dbctx, keylist.keys[i],
506                         publickey,
507                         true);
508         }
509         array_free(&keylist);
510         free(searchtext);
511         searchtext = NULL;
512
513         db4_endtrans(dbctx);
514
515         return (numkeys);
516 }
517
518 static int db4_fetch_key_skshash(struct onak_dbctx *dbctx,
519                 const struct skshash *hash,
520                 struct openpgp_publickey **publickey)
521 {
522         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
523         DBT       key, data;
524         DBC      *cursor = NULL;
525         uint64_t  keyid = 0;
526         int       ret = 0;
527
528         ret = privctx->skshashdb->cursor(privctx->skshashdb,
529                         privctx->txn,
530                         &cursor,
531                         0);   /* flags */
532
533         memset(&key, 0, sizeof(key));
534         memset(&data, 0, sizeof(data));
535         key.data = (void *) hash->hash;
536         key.size = sizeof(hash->hash);
537         data.flags = DB_DBT_MALLOC;
538
539         ret = cursor->c_get(cursor,
540                 &key,
541                 &data,
542                 DB_SET);
543
544         if (ret == 0) {
545                 keyid = *(uint64_t *) data.data;
546
547                 if (data.data != NULL) {
548                         free(data.data);
549                         data.data = NULL;
550                 }
551         }
552
553         ret = cursor->c_close(cursor);
554         cursor = NULL;
555
556         return db4_fetch_key_id(dbctx, keyid, publickey, false);
557 }
558
559 /**
560  *      delete_key - Given a keyid delete the key from storage.
561  *      @keyid: The keyid to delete.
562  *      @intrans: If we're already in a transaction.
563  *
564  *      This function deletes a public key from whatever storage mechanism we
565  *      are using. Returns 0 if the key existed.
566  */
567 static int db4_delete_key(struct onak_dbctx *dbctx,
568                 uint64_t keyid, bool intrans)
569 {
570         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
571         struct openpgp_publickey *publickey = NULL;
572         DBT key, data;
573         DBC *cursor = NULL;
574         uint32_t   shortkeyid = 0;
575         uint64_t  *subkeyids = NULL;
576         int ret = 0;
577         int i;
578         char **uids = NULL;
579         char *primary = NULL;
580         unsigned char worddb_data[12];
581         struct ll *wordlist = NULL;
582         struct ll *curword  = NULL;
583         bool deadlock = false;
584         struct skshash hash;
585
586         if (!intrans) {
587                 db4_starttrans(dbctx);
588         }
589
590         db4_fetch_key_id(dbctx, keyid, &publickey, true);
591
592         /*
593          * Walk through the uids removing the words from the worddb.
594          */
595         if (publickey != NULL) {
596                 uids = keyuids(publickey, &primary);
597         }
598         if (uids != NULL) {
599                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
600                         wordlist = makewordlist(wordlist, uids[i]);
601                 }
602
603                 ret = privctx->worddb->cursor(privctx->worddb,
604                         privctx->txn,
605                         &cursor,
606                         0);   /* flags */
607
608                 for (curword = wordlist; curword != NULL && !deadlock;
609                                 curword = curword->next) {
610                         memset(&key, 0, sizeof(key));
611                         memset(&data, 0, sizeof(data));
612                         key.data = curword->object;
613                         key.size = strlen(key.data);
614                         data.data = worddb_data;
615                         data.size = sizeof(worddb_data);
616
617                         /*
618                          * Our data is the key creation time followed by the
619                          * key id.
620                          */
621                         worddb_data[ 0] = publickey->publickey->data[1];
622                         worddb_data[ 1] = publickey->publickey->data[2];
623                         worddb_data[ 2] = publickey->publickey->data[3];
624                         worddb_data[ 3] = publickey->publickey->data[4];
625                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
626                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
627                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
628                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
629                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
630                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
631                         worddb_data[10] = (keyid >>  8) & 0xFF;
632                         worddb_data[11] = keyid & 0xFF;
633
634                         ret = cursor->c_get(cursor,
635                                 &key,
636                                 &data,
637                                 DB_GET_BOTH);
638
639                         if (ret == 0) {
640                                 ret = cursor->c_del(cursor, 0);
641                         }
642
643                         if (ret != 0) {
644                                 logthing(LOGTHING_ERROR,
645                                         "Problem deleting word: %s "
646                                         "(0x%016" PRIX64 ")",
647                                         db_strerror(ret),
648                                         keyid);
649                                 if (ret == DB_LOCK_DEADLOCK) {
650                                         deadlock = true;
651                                 }
652                         }
653                 }
654                 ret = cursor->c_close(cursor);
655                 cursor = NULL;
656
657                 ret = privctx->skshashdb->cursor(privctx->skshashdb,
658                         privctx->txn,
659                         &cursor,
660                         0);   /* flags */
661                 get_skshash(publickey, &hash);
662
663                 memset(&key, 0, sizeof(key));
664                 memset(&data, 0, sizeof(data));
665                 key.data = hash.hash;
666                 key.size = sizeof(hash.hash);
667                 data.data = &keyid;
668                 data.size = sizeof(keyid);
669
670                 ret = cursor->c_get(cursor,
671                         &key,
672                         &data,
673                         DB_GET_BOTH);
674
675                 if (ret == 0) {
676                         ret = cursor->c_del(cursor, 0);
677                 }
678
679                 if (ret != 0) {
680                         logthing(LOGTHING_ERROR,
681                                 "Problem deleting skshash: %s "
682                                 "(0x%016" PRIX64 ")",
683                                 db_strerror(ret),
684                                 keyid);
685                         if (ret == DB_LOCK_DEADLOCK) {
686                                 deadlock = true;
687                         }
688                 }
689
690                 ret = cursor->c_close(cursor);
691                 cursor = NULL;
692
693                 /*
694                  * Free our UID and word lists.
695                  */
696                 llfree(wordlist, NULL);
697                 for (i = 0; uids[i] != NULL; i++) {
698                         free(uids[i]);
699                         uids[i] = NULL;
700                 }
701                 free(uids);
702                 uids = NULL;
703                 free_publickey(publickey);
704                 publickey = NULL;
705         }
706
707         if (!deadlock) {
708                 ret = privctx->id32db->cursor(privctx->id32db,
709                         privctx->txn,
710                         &cursor,
711                         0);   /* flags */
712
713                 shortkeyid = keyid & 0xFFFFFFFF;
714
715                 memset(&key, 0, sizeof(key));
716                 memset(&data, 0, sizeof(data));
717                 key.data = &shortkeyid;
718                 key.size = sizeof(shortkeyid);
719                 data.data = &keyid;
720                 data.size = sizeof(keyid);
721
722                 ret = cursor->c_get(cursor,
723                         &key,
724                         &data,
725                         DB_GET_BOTH);
726
727                 if (ret == 0) {
728                         ret = cursor->c_del(cursor, 0);
729                 }
730
731                 if (ret != 0) {
732                         logthing(LOGTHING_ERROR,
733                                 "Problem deleting short keyid: %s "
734                                 "(0x%016" PRIX64 ")",
735                                 db_strerror(ret),
736                                 keyid);
737                         if (ret == DB_LOCK_DEADLOCK) {
738                                 deadlock = true;
739                         }
740                 }
741
742                 subkeyids = keysubkeys(publickey);
743                 i = 0;
744                 while (subkeyids != NULL && subkeyids[i] != 0) {
745                         memset(&key, 0, sizeof(key));
746                         key.data = &subkeyids[i];
747                         key.size = sizeof(subkeyids[i]);
748                         privctx->subkeydb->del(privctx->subkeydb,
749                                         privctx->txn, &key, 0);
750                         if (ret != 0) {
751                                 logthing(LOGTHING_ERROR,
752                                         "Problem deleting subkey id: %s "
753                                         "(0x%016" PRIX64 ")",
754                                         db_strerror(ret),
755                                         keyid);
756                                 if (ret == DB_LOCK_DEADLOCK) {
757                                         deadlock = true;
758                                 }
759                         }
760
761                         shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
762
763                         memset(&key, 0, sizeof(key));
764                         memset(&data, 0, sizeof(data));
765                         key.data = &shortkeyid;
766                         key.size = sizeof(shortkeyid);
767                         data.data = &keyid;
768                         data.size = sizeof(keyid);
769
770                         ret = cursor->c_get(cursor,
771                                 &key,
772                                 &data,
773                                 DB_GET_BOTH);
774
775                         if (ret == 0) {
776                                 ret = cursor->c_del(cursor, 0);
777                         }
778
779                         if (ret != 0) {
780                                 logthing(LOGTHING_ERROR,
781                                         "Problem deleting short keyid: %s "
782                                         "(0x%016" PRIX64 ")",
783                                         db_strerror(ret),
784                                         keyid);
785                                 if (ret == DB_LOCK_DEADLOCK) {
786                                         deadlock = true;
787                                 }
788                         }
789                 }
790                 if (subkeyids != NULL) {
791                         free(subkeyids);
792                         subkeyids = NULL;
793                 }
794                 ret = cursor->c_close(cursor);
795                 cursor = NULL;
796
797         }
798
799         if (!deadlock) {
800                 key.data = &keyid;
801                 key.size = sizeof(keyid);
802
803                 keydb(privctx, keyid)->del(keydb(privctx, keyid),
804                                 privctx->txn,
805                                 &key,
806                                 0); /* flags */
807         }
808
809         if (!intrans) {
810                 db4_endtrans(dbctx);
811         }
812
813         return deadlock ? (-1) : (ret == DB_NOTFOUND);
814 }
815
816 /**
817  *      store_key - Takes a key and stores it.
818  *      @publickey: A pointer to the public key to store.
819  *      @intrans: If we're already in a transaction.
820  *      @update: If true the key exists and should be updated.
821  *
822  *      Again we just use the hex representation of the keyid as the filename
823  *      to store the key to. We flatten the public key to a list of OpenPGP
824  *      packets and then use write_openpgp_stream() to write the stream out to
825  *      the file. If update is true then we delete the old key first, otherwise
826  *      we trust that it doesn't exist.
827  */
828 static int db4_store_key(struct onak_dbctx *dbctx,
829                 struct openpgp_publickey *publickey, bool intrans,
830                 bool update)
831 {
832         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
833         struct     openpgp_packet_list *packets = NULL;
834         struct     openpgp_packet_list *list_end = NULL;
835         struct     openpgp_publickey *next = NULL;
836         int        ret = 0;
837         int        i = 0;
838         struct     buffer_ctx storebuf;
839         DBT        key;
840         DBT        data;
841         uint64_t   keyid = 0;
842         uint32_t   shortkeyid = 0;
843         uint64_t  *subkeyids = NULL;
844         char     **uids = NULL;
845         char      *primary = NULL;
846         unsigned char worddb_data[12];
847         struct ll *wordlist = NULL;
848         struct ll *curword  = NULL;
849         bool       deadlock = false;
850         struct skshash hash;
851
852         if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
853                 logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
854                 return 0;
855         }
856
857         if (!intrans) {
858                 db4_starttrans(dbctx);
859         }
860
861         /*
862          * Delete the key if we already have it.
863          *
864          * TODO: Can we optimize this perhaps? Possibly when other data is
865          * involved as well? I suspect this is easiest and doesn't make a lot
866          * of difference though - the largest chunk of data is the keydata and
867          * it definitely needs updated.
868          */
869         if (update) {
870                 deadlock = (db4_delete_key(dbctx, keyid, true) == -1);
871         }
872
873         /*
874          * Convert the key to a flat set of binary data.
875          */
876         if (!deadlock) {
877                 next = publickey->next;
878                 publickey->next = NULL;
879                 flatten_publickey(publickey, &packets, &list_end);
880                 publickey->next = next;
881
882                 storebuf.offset = 0;
883                 storebuf.size = 8192;
884                 storebuf.buffer = malloc(8192);
885
886                 write_openpgp_stream(buffer_putchar, &storebuf, packets);
887
888                 /*
889                  * Now we have the key data store it in the DB; the keyid is
890                  * the key.
891                  */
892                 memset(&key, 0, sizeof(key));
893                 memset(&data, 0, sizeof(data));
894                 key.data = &keyid;
895                 key.size = sizeof(keyid);
896                 data.size = storebuf.offset;
897                 data.data = storebuf.buffer;
898
899                 ret = keydb(privctx, keyid)->put(keydb(privctx, keyid),
900                                 privctx->txn,
901                                 &key,
902                                 &data,
903                                 0); /* flags*/
904                 if (ret != 0) {
905                         logthing(LOGTHING_ERROR,
906                                         "Problem storing key: %s",
907                                         db_strerror(ret));
908                         if (ret == DB_LOCK_DEADLOCK) {
909                                 deadlock = true;
910                         }
911                 }
912
913                 free(storebuf.buffer);
914                 storebuf.buffer = NULL;
915                 storebuf.size = 0;
916                 storebuf.offset = 0;
917
918                 free_packet_list(packets);
919                 packets = NULL;
920         }
921
922         /*
923          * Walk through our uids storing the words into the db with the keyid.
924          */
925         if (!deadlock) {
926                 uids = keyuids(publickey, &primary);
927         }
928         if (uids != NULL) {
929                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
930                         wordlist = makewordlist(wordlist, uids[i]);
931                 }
932
933                 for (curword = wordlist; curword != NULL && !deadlock;
934                                 curword = curword->next) {
935                         memset(&key, 0, sizeof(key));
936                         memset(&data, 0, sizeof(data));
937                         key.data = curword->object;
938                         key.size = strlen(key.data);
939                         data.data = worddb_data;
940                         data.size = sizeof(worddb_data);
941
942                         /*
943                          * Our data is the key creation time followed by the
944                          * key id.
945                          */
946                         worddb_data[ 0] = publickey->publickey->data[1];
947                         worddb_data[ 1] = publickey->publickey->data[2];
948                         worddb_data[ 2] = publickey->publickey->data[3];
949                         worddb_data[ 3] = publickey->publickey->data[4];
950                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
951                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
952                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
953                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
954                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
955                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
956                         worddb_data[10] = (keyid >>  8) & 0xFF;
957                         worddb_data[11] = keyid & 0xFF;
958                         ret = privctx->worddb->put(privctx->worddb,
959                                 privctx->txn,
960                                 &key,
961                                 &data,
962                                 0);
963                         if (ret != 0) {
964                                 logthing(LOGTHING_ERROR,
965                                         "Problem storing word: %s",
966                                         db_strerror(ret));
967                                 if (ret == DB_LOCK_DEADLOCK) {
968                                         deadlock = true;
969                                 }
970                         }
971                 }
972
973                 /*
974                  * Free our UID and word lists.
975                  */
976                 llfree(wordlist, NULL);
977                 for (i = 0; uids[i] != NULL; i++) {
978                         free(uids[i]);
979                         uids[i] = NULL;
980                 }
981                 free(uids);
982                 uids = NULL;
983         }
984
985         /*
986          * Write the truncated 32 bit keyid so we can lookup the full id for
987          * queries.
988          */
989         if (!deadlock) {
990                 shortkeyid = keyid & 0xFFFFFFFF;
991
992                 memset(&key, 0, sizeof(key));
993                 memset(&data, 0, sizeof(data));
994                 key.data = &shortkeyid;
995                 key.size = sizeof(shortkeyid);
996                 data.data = &keyid;
997                 data.size = sizeof(keyid);
998
999                 ret = privctx->id32db->put(privctx->id32db,
1000                         privctx->txn,
1001                         &key,
1002                         &data,
1003                         0);
1004                 if (ret != 0) {
1005                         logthing(LOGTHING_ERROR,
1006                                 "Problem storing short keyid: %s",
1007                                 db_strerror(ret));
1008                         if (ret == DB_LOCK_DEADLOCK) {
1009                                 deadlock = true;
1010                         }
1011                 }
1012         }
1013
1014         if (!deadlock) {
1015                 subkeyids = keysubkeys(publickey);
1016                 i = 0;
1017                 while (subkeyids != NULL && subkeyids[i] != 0) {
1018                         /* Store the subkey ID -> main key ID mapping */
1019                         memset(&key, 0, sizeof(key));
1020                         memset(&data, 0, sizeof(data));
1021                         key.data = &subkeyids[i];
1022                         key.size = sizeof(subkeyids[i]);
1023                         data.data = &keyid;
1024                         data.size = sizeof(keyid);
1025
1026                         ret = privctx->subkeydb->put(privctx->subkeydb,
1027                                 privctx->txn,
1028                                 &key,
1029                                 &data,
1030                                 0);
1031                         if (ret != 0) {
1032                                 logthing(LOGTHING_ERROR,
1033                                         "Problem storing subkey keyid: %s",
1034                                         db_strerror(ret));
1035                                 if (ret == DB_LOCK_DEADLOCK) {
1036                                         deadlock = true;
1037                                 }
1038                         }
1039
1040                         /* Store the short subkey ID -> main key ID mapping */
1041                         shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
1042
1043                         memset(&key, 0, sizeof(key));
1044                         memset(&data, 0, sizeof(data));
1045                         key.data = &shortkeyid;
1046                         key.size = sizeof(shortkeyid);
1047                         data.data = &keyid;
1048                         data.size = sizeof(keyid);
1049
1050                         ret = privctx->id32db->put(privctx->id32db,
1051                                 privctx->txn,
1052                                 &key,
1053                                 &data,
1054                                 0);
1055                         if (ret != 0) {
1056                                 logthing(LOGTHING_ERROR,
1057                                         "Problem storing short keyid: %s",
1058                                         db_strerror(ret));
1059                                 if (ret == DB_LOCK_DEADLOCK) {
1060                                         deadlock = true;
1061                                 }
1062                         }
1063                 }
1064                 if (subkeyids != NULL) {
1065                         free(subkeyids);
1066                         subkeyids = NULL;
1067                 }
1068         }
1069
1070         if (!deadlock) {
1071                 get_skshash(publickey, &hash);
1072                 memset(&key, 0, sizeof(key));
1073                 memset(&data, 0, sizeof(data));
1074                 key.data = hash.hash;
1075                 key.size = sizeof(hash.hash);
1076                 data.data = &keyid;
1077                 data.size = sizeof(keyid);
1078
1079                 ret = privctx->skshashdb->put(privctx->skshashdb,
1080                         privctx->txn,
1081                         &key,
1082                         &data,
1083                         0);
1084                 if (ret != 0) {
1085                         logthing(LOGTHING_ERROR,
1086                                 "Problem storing SKS hash: %s",
1087                                 db_strerror(ret));
1088                         if (ret == DB_LOCK_DEADLOCK) {
1089                                 deadlock = true;
1090                         }
1091                 }
1092         }
1093
1094         if (!intrans) {
1095                 db4_endtrans(dbctx);
1096         }
1097
1098         return deadlock ? -1 : 0 ;
1099 }
1100
1101 /**
1102  *      iterate_keys - call a function once for each key in the db.
1103  *      @iterfunc: The function to call.
1104  *      @ctx: A context pointer
1105  *
1106  *      Calls iterfunc once for each key in the database. ctx is passed
1107  *      unaltered to iterfunc. This function is intended to aid database dumps
1108  *      and statistic calculations.
1109  *
1110  *      Returns the number of keys we iterated over.
1111  */
1112 static int db4_iterate_keys(struct onak_dbctx *dbctx,
1113                 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
1114                 void *ctx)
1115 {
1116         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
1117         DBT                         dbkey, data;
1118         DBC                        *cursor = NULL;
1119         int                         ret = 0;
1120         int                         i = 0;
1121         int                         numkeys = 0;
1122         struct buffer_ctx           fetchbuf;
1123         struct openpgp_packet_list *packets = NULL;
1124         struct openpgp_publickey   *key = NULL;
1125
1126         for (i = 0; i < privctx->numdbs; i++) {
1127                 ret = privctx->dbconns[i]->cursor(privctx->dbconns[i],
1128                         NULL,
1129                         &cursor,
1130                         0);   /* flags */
1131
1132                 memset(&dbkey, 0, sizeof(dbkey));
1133                 memset(&data, 0, sizeof(data));
1134                 ret = cursor->c_get(cursor, &dbkey, &data, DB_NEXT);
1135                 while (ret == 0) {
1136                         fetchbuf.buffer = data.data;
1137                         fetchbuf.offset = 0;
1138                         fetchbuf.size = data.size;
1139                         read_openpgp_stream(buffer_fetchchar, &fetchbuf,
1140                                 &packets, 0);
1141                         parse_keys(packets, &key);
1142
1143                         iterfunc(ctx, key);
1144
1145                         free_publickey(key);
1146                         key = NULL;
1147                         free_packet_list(packets);
1148                         packets = NULL;
1149
1150                         memset(&dbkey, 0, sizeof(dbkey));
1151                         memset(&data, 0, sizeof(data));
1152                         ret = cursor->c_get(cursor, &dbkey, &data,
1153                                         DB_NEXT);
1154                         numkeys++;
1155                 }
1156                 if (ret != DB_NOTFOUND) {
1157                         logthing(LOGTHING_ERROR,
1158                                 "Problem reading key: %s",
1159                                 db_strerror(ret));
1160                 }
1161
1162                 ret = cursor->c_close(cursor);
1163                 cursor = NULL;
1164         }
1165
1166         return numkeys;
1167 }
1168
1169 /*
1170  * Include the basic keydb routines.
1171  */
1172 #define NEED_GETKEYSIGS 1
1173 #define NEED_KEYID2UID 1
1174 #define NEED_UPDATEKEYS 1
1175 #define NEED_GET_FP 1
1176 #include "keydb.c"
1177
1178 /**
1179  *      cleanupdb - De-initialize the key database.
1180  *
1181  *      This function should be called upon program exit to allow the DB to
1182  *      cleanup after itself.
1183  */
1184 static void db4_cleanupdb(struct onak_dbctx *dbctx)
1185 {
1186         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
1187         int i = 0;
1188
1189         if (privctx->dbenv != NULL) {
1190                 privctx->dbenv->txn_checkpoint(privctx->dbenv, 0, 0, 0);
1191                 if (privctx->subkeydb != NULL) {
1192                         privctx->subkeydb->close(privctx->subkeydb, 0);
1193                         privctx->subkeydb = NULL;
1194                 }
1195                 if (privctx->skshashdb != NULL) {
1196                         privctx->skshashdb->close(privctx->skshashdb, 0);
1197                         privctx->skshashdb = NULL;
1198                 }
1199                 if (privctx->id32db != NULL) {
1200                         privctx->id32db->close(privctx->id32db, 0);
1201                         privctx->id32db = NULL;
1202                 }
1203                 if (privctx->worddb != NULL) {
1204                         privctx->worddb->close(privctx->worddb, 0);
1205                         privctx->worddb = NULL;
1206                 }
1207                 for (i = 0; i < privctx->numdbs; i++) {
1208                         if (privctx->dbconns[i] != NULL) {
1209                                 privctx->dbconns[i]->close(privctx->dbconns[i],
1210                                                 0);
1211                                 privctx->dbconns[i] = NULL;
1212                         }
1213                 }
1214                 free(privctx->dbconns);
1215                 privctx->dbconns = NULL;
1216                 privctx->dbenv->close(privctx->dbenv, 0);
1217                 privctx->dbenv = NULL;
1218         }
1219
1220         free(privctx);
1221         dbctx->priv = NULL;
1222         free(dbctx);
1223 }
1224
1225 /**
1226  *      initdb - Initialize the key database.
1227  *
1228  *      This function should be called before any of the other functions in
1229  *      this file are called in order to allow the DB to be initialized ready
1230  *      for access.
1231  */
1232 struct onak_dbctx *keydb_db4_init(bool readonly)
1233 {
1234         char       buf[1024];
1235         FILE      *numdb = NULL;
1236         int        ret = 0;
1237         int        i = 0;
1238         uint32_t   flags = 0;
1239         struct stat statbuf;
1240         int        maxlocks;
1241         struct onak_dbctx *dbctx;
1242         struct onak_db4_dbctx *privctx;
1243
1244         dbctx = malloc(sizeof(*dbctx));
1245         if (dbctx == NULL) {
1246                 return NULL;
1247         }
1248         dbctx->priv = privctx = calloc(1, sizeof(*privctx));
1249         if (privctx == NULL) {
1250                 free(dbctx);
1251                 return NULL;
1252         }
1253
1254         /* Default to 16 key data DBs */
1255         privctx->numdbs = 16;
1256
1257         snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
1258                         DB4_UPGRADE_FILE);
1259         ret = stat(buf, &statbuf);
1260         while ((ret == 0) || (errno != ENOENT)) {
1261                 if (ret != 0) {
1262                         logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade "
1263                                 "lock file: %s (%d)", strerror(errno), ret);
1264                         exit(1);
1265                 }
1266                 logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting.");
1267                 sleep(5);
1268                 ret = stat(buf, &statbuf);
1269         }
1270         ret = 0;
1271
1272         snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
1273         numdb = fopen(buf, "r");
1274         if (numdb != NULL) {
1275                 if (fgets(buf, sizeof(buf), numdb) != NULL) {
1276                         privctx->numdbs = atoi(buf);
1277                 }
1278                 fclose(numdb);
1279         } else if (!readonly) {
1280                 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
1281                                 strerror(errno));
1282                 numdb = fopen(buf, "w");
1283                 if (numdb != NULL) {
1284                         fprintf(numdb, "%d", privctx->numdbs);
1285                         fclose(numdb);
1286                 } else {
1287                         logthing(LOGTHING_ERROR,
1288                                 "Couldn't write num_keydb: %s",
1289                                 strerror(errno));
1290                 }
1291         }
1292
1293         privctx->dbconns = calloc(privctx->numdbs, sizeof (DB *));
1294         if (privctx->dbconns == NULL) {
1295                 logthing(LOGTHING_CRITICAL,
1296                                 "Couldn't allocate memory for dbconns");
1297                 ret = 1;
1298         }
1299
1300         if (ret == 0) {
1301                 ret = db_env_create(&privctx->dbenv, 0);
1302                 if (ret != 0) {
1303                         logthing(LOGTHING_CRITICAL,
1304                                 "db_env_create: %s", db_strerror(ret));
1305                 }
1306         }
1307
1308         /*
1309          * Up the number of locks we're allowed at once. We base this on
1310          * the maximum number of keys we're going to return.
1311          */
1312         maxlocks = config.maxkeys * 16;
1313         if (maxlocks < 1000) {
1314                 maxlocks = 1000;
1315         }
1316         privctx->dbenv->set_lk_max_locks(privctx->dbenv, maxlocks);
1317         privctx->dbenv->set_lk_max_objects(privctx->dbenv, maxlocks);
1318
1319         /*
1320          * Enable deadlock detection so that we don't block indefinitely on
1321          * anything. What we really want is simple 2 state locks, but I'm not
1322          * sure how to make the standard DB functions do that yet.
1323          */
1324         if (ret == 0) {
1325                 privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
1326                 ret = privctx->dbenv->set_lk_detect(privctx->dbenv, DB_LOCK_DEFAULT);
1327                 if (ret != 0) {
1328                         logthing(LOGTHING_CRITICAL,
1329                                 "db_env_create: %s", db_strerror(ret));
1330                 }
1331         }
1332
1333         if (ret == 0) {
1334                 ret = privctx->dbenv->open(privctx->dbenv, config.db_dir,
1335                                 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
1336                                 DB_INIT_TXN |
1337                                 DB_CREATE,
1338                                 0);
1339 #ifdef DB_VERSION_MISMATCH
1340                 if (ret == DB_VERSION_MISMATCH) {
1341                         privctx->dbenv->close(privctx->dbenv, 0);
1342                         privctx->dbenv = NULL;
1343                         ret = db4_upgradedb(privctx);
1344                         if (ret == 0) {
1345                                 ret = db_env_create(&privctx->dbenv, 0);
1346                         }
1347                         if (ret == 0) {
1348                                 privctx->dbenv->set_errcall(privctx->dbenv,
1349                                         &db4_errfunc);
1350                                 privctx->dbenv->set_lk_detect(privctx->dbenv,
1351                                         DB_LOCK_DEFAULT);
1352                                 ret = privctx->dbenv->open(privctx->dbenv,
1353                                         config.db_dir,
1354                                         DB_INIT_LOG | DB_INIT_MPOOL |
1355                                         DB_INIT_LOCK | DB_INIT_TXN |
1356                                         DB_CREATE | DB_RECOVER,
1357                                         0);
1358
1359                                 if (ret == 0) {
1360                                         privctx->dbenv->txn_checkpoint(
1361                                                         privctx->dbenv,
1362                                                         0,
1363                                                         0,
1364                                                         DB_FORCE);
1365                                 }
1366                         }
1367                 }
1368 #endif
1369                 if (ret != 0) {
1370                         logthing(LOGTHING_CRITICAL,
1371                                         "Error opening db environment: %s (%s)",
1372                                         config.db_dir,
1373                                         db_strerror(ret));
1374                         privctx->dbenv->close(privctx->dbenv, 0);
1375                         privctx->dbenv = NULL;
1376                 }
1377         }
1378
1379         if (ret == 0) {
1380                 db4_starttrans(dbctx);
1381
1382                 for (i = 0; !ret && i < privctx->numdbs; i++) {
1383                         ret = db_create(&privctx->dbconns[i],
1384                                         privctx->dbenv, 0);
1385                         if (ret != 0) {
1386                                 logthing(LOGTHING_CRITICAL,
1387                                         "db_create: %s", db_strerror(ret));
1388                         }
1389
1390                         if (ret == 0) {
1391                                 snprintf(buf, 1023, "keydb.%d.db", i);
1392                                 flags = DB_CREATE;
1393                                 if (readonly) {
1394                                         flags = DB_RDONLY;
1395                                 }
1396                                 ret = privctx->dbconns[i]->open(
1397                                                 privctx->dbconns[i],
1398                                                 privctx->txn,
1399                                                 buf,
1400                                                 "keydb",
1401                                                 DB_HASH,
1402                                                 flags,
1403                                                 0664);
1404                                 if (ret != 0) {
1405                                         logthing(LOGTHING_CRITICAL,
1406                                                 "Error opening key database:"
1407                                                 " %s (%s)",
1408                                                 buf,
1409                                                 db_strerror(ret));
1410                                 }
1411                         }
1412                 }
1413         }
1414
1415         if (ret == 0) {
1416                 ret = db_create(&privctx->worddb, privctx->dbenv, 0);
1417                 if (ret != 0) {
1418                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1419                                         db_strerror(ret));
1420                 }
1421         }
1422
1423         if (ret == 0) {
1424                 ret = privctx->worddb->set_flags(privctx->worddb, DB_DUP);
1425         }
1426
1427         if (ret == 0) {
1428                 ret = privctx->worddb->open(privctx->worddb, privctx->txn,
1429                                 "worddb", "worddb", DB_BTREE,
1430                                 flags,
1431                                 0664);
1432                 if (ret != 0) {
1433                         logthing(LOGTHING_CRITICAL,
1434                                         "Error opening word database: %s (%s)",
1435                                         "worddb",
1436                                         db_strerror(ret));
1437                 }
1438         }
1439
1440         if (ret == 0) {
1441                 ret = db_create(&privctx->id32db, privctx->dbenv, 0);
1442                 if (ret != 0) {
1443                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1444                                         db_strerror(ret));
1445                 }
1446         }
1447
1448         if (ret == 0) {
1449                 ret = privctx->id32db->set_flags(privctx->id32db, DB_DUP);
1450         }
1451
1452         if (ret == 0) {
1453                 ret = privctx->id32db->open(privctx->id32db, privctx->txn,
1454                                 "id32db", "id32db", DB_HASH,
1455                                 flags,
1456                                 0664);
1457                 if (ret != 0) {
1458                         logthing(LOGTHING_CRITICAL,
1459                                         "Error opening id32 database: %s (%s)",
1460                                         "id32db",
1461                                         db_strerror(ret));
1462                 }
1463         }
1464
1465         if (ret == 0) {
1466                 ret = db_create(&privctx->skshashdb, privctx->dbenv, 0);
1467                 if (ret != 0) {
1468                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1469                                         db_strerror(ret));
1470                 }
1471         }
1472
1473         if (ret == 0) {
1474                 ret = privctx->skshashdb->open(privctx->skshashdb, privctx->txn,
1475                                 "skshashdb",
1476                                 "skshashdb", DB_HASH,
1477                                 flags,
1478                                 0664);
1479                 if (ret != 0) {
1480                         logthing(LOGTHING_CRITICAL,
1481                                 "Error opening skshash database: %s (%s)",
1482                                 "skshashdb",
1483                                 db_strerror(ret));
1484                 }
1485         }
1486
1487         if (ret == 0) {
1488                 ret = db_create(&privctx->subkeydb, privctx->dbenv, 0);
1489                 if (ret != 0) {
1490                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1491                                         db_strerror(ret));
1492                 }
1493         }
1494
1495         if (ret == 0) {
1496                 ret = privctx->subkeydb->open(privctx->subkeydb, privctx->txn,
1497                                 "subkeydb", "subkeydb",
1498                                 DB_HASH,
1499                                 flags,
1500                                 0664);
1501                 if (ret != 0) {
1502                         logthing(LOGTHING_CRITICAL,
1503                                 "Error opening subkey database: %s (%s)",
1504                                 "subkeydb",
1505                                 db_strerror(ret));
1506                 }
1507         }
1508
1509         if (privctx->txn != NULL) {
1510                 db4_endtrans(dbctx);
1511         }
1512
1513         if (ret != 0) {
1514                 db4_cleanupdb(dbctx);
1515                 logthing(LOGTHING_CRITICAL,
1516                                 "Error opening database; exiting");
1517                 exit(EXIT_FAILURE);
1518         }
1519
1520         dbctx->cleanupdb                = db4_cleanupdb;
1521         dbctx->starttrans               = db4_starttrans;
1522         dbctx->endtrans                 = db4_endtrans;
1523         dbctx->fetch_key_id             = db4_fetch_key_id;
1524         dbctx->fetch_key_fp             = generic_fetch_key_fp;
1525         dbctx->fetch_key_text           = db4_fetch_key_text;
1526         dbctx->fetch_key_skshash        = db4_fetch_key_skshash;
1527         dbctx->store_key                = db4_store_key;
1528         dbctx->update_keys              = generic_update_keys;
1529         dbctx->delete_key               = db4_delete_key;
1530         dbctx->getkeysigs               = generic_getkeysigs;
1531         dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
1532         dbctx->keyid2uid                = generic_keyid2uid;
1533         dbctx->getfullkeyid             = db4_getfullkeyid;
1534         dbctx->iterate_keys             = db4_iterate_keys;
1535
1536         return dbctx;
1537 }