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