]> the.earth.li Git - onak.git/blob - keydb_db4.c
6a21fab73678a811a286eeff35a6d3d7e97f2351
[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         uint64_t  *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
601         if (!intrans) {
602                 db4_starttrans(dbctx);
603         }
604
605         db4_fetch_key_id(dbctx, keyid, &publickey, true);
606
607         /*
608          * Walk through the uids removing the words from the worddb.
609          */
610         if (publickey != NULL) {
611                 uids = keyuids(publickey, &primary);
612         }
613         if (uids != NULL) {
614                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
615                         wordlist = makewordlist(wordlist, uids[i]);
616                 }
617
618                 privctx->worddb->cursor(privctx->worddb,
619                         privctx->txn,
620                         &cursor,
621                         0);   /* flags */
622
623                 for (curword = wordlist; curword != NULL && !deadlock;
624                                 curword = curword->next) {
625                         memset(&key, 0, sizeof(key));
626                         memset(&data, 0, sizeof(data));
627                         key.data = curword->object;
628                         key.size = strlen(key.data);
629                         data.data = worddb_data;
630                         data.size = sizeof(worddb_data);
631
632                         /*
633                          * Our data is the key creation time followed by the
634                          * key id.
635                          */
636                         worddb_data[ 0] = publickey->publickey->data[1];
637                         worddb_data[ 1] = publickey->publickey->data[2];
638                         worddb_data[ 2] = publickey->publickey->data[3];
639                         worddb_data[ 3] = publickey->publickey->data[4];
640                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
641                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
642                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
643                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
644                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
645                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
646                         worddb_data[10] = (keyid >>  8) & 0xFF;
647                         worddb_data[11] = keyid & 0xFF;
648
649                         ret = cursor->c_get(cursor,
650                                 &key,
651                                 &data,
652                                 DB_GET_BOTH);
653
654                         if (ret == 0) {
655                                 ret = cursor->c_del(cursor, 0);
656                         }
657
658                         if (ret != 0) {
659                                 logthing(LOGTHING_ERROR,
660                                         "Problem deleting word: %s "
661                                         "(0x%016" PRIX64 ")",
662                                         db_strerror(ret),
663                                         keyid);
664                                 if (ret == DB_LOCK_DEADLOCK) {
665                                         deadlock = true;
666                                 }
667                         }
668                 }
669                 cursor->c_close(cursor);
670                 cursor = NULL;
671
672                 ret = privctx->skshashdb->cursor(privctx->skshashdb,
673                         privctx->txn,
674                         &cursor,
675                         0);   /* flags */
676                 if (ret == 0) {
677                         get_skshash(publickey, &hash);
678
679                         memset(&key, 0, sizeof(key));
680                         memset(&data, 0, sizeof(data));
681                         key.data = hash.hash;
682                         key.size = sizeof(hash.hash);
683                         data.data = &keyid;
684                         data.size = sizeof(keyid);
685
686                         ret = cursor->c_get(cursor,
687                                 &key,
688                                 &data,
689                                 DB_GET_BOTH);
690
691                         if (ret == 0) {
692                                 ret = cursor->c_del(cursor, 0);
693                         }
694
695                         if (ret != 0) {
696                                 logthing(LOGTHING_ERROR,
697                                         "Problem deleting skshash: %s "
698                                         "(0x%016" PRIX64 ")",
699                                         db_strerror(ret),
700                                         keyid);
701                                 if (ret == DB_LOCK_DEADLOCK) {
702                                         deadlock = true;
703                                 }
704                         }
705
706                         cursor->c_close(cursor);
707                         cursor = NULL;
708                 }
709
710                 /*
711                  * Free our UID and word lists.
712                  */
713                 llfree(wordlist, NULL);
714                 for (i = 0; uids[i] != NULL; i++) {
715                         free(uids[i]);
716                         uids[i] = NULL;
717                 }
718                 free(uids);
719                 uids = NULL;
720                 free_publickey(publickey);
721                 publickey = NULL;
722         }
723
724         if (!deadlock) {
725                 privctx->id32db->cursor(privctx->id32db,
726                         privctx->txn,
727                         &cursor,
728                         0);   /* flags */
729
730                 shortkeyid = keyid & 0xFFFFFFFF;
731
732                 memset(&key, 0, sizeof(key));
733                 memset(&data, 0, sizeof(data));
734                 key.data = &shortkeyid;
735                 key.size = sizeof(shortkeyid);
736                 data.data = &keyid;
737                 data.size = sizeof(keyid);
738
739                 ret = cursor->c_get(cursor,
740                         &key,
741                         &data,
742                         DB_GET_BOTH);
743
744                 if (ret == 0) {
745                         ret = cursor->c_del(cursor, 0);
746                 }
747
748                 if (ret != 0) {
749                         logthing(LOGTHING_ERROR,
750                                 "Problem deleting short keyid: %s "
751                                 "(0x%016" PRIX64 ")",
752                                 db_strerror(ret),
753                                 keyid);
754                         if (ret == DB_LOCK_DEADLOCK) {
755                                 deadlock = true;
756                         }
757                 }
758
759                 subkeyids = keysubkeys(publickey);
760                 i = 0;
761                 while (subkeyids != NULL && subkeyids[i] != 0) {
762                         memset(&key, 0, sizeof(key));
763                         key.data = &subkeyids[i];
764                         key.size = sizeof(subkeyids[i]);
765                         privctx->subkeydb->del(privctx->subkeydb,
766                                         privctx->txn, &key, 0);
767                         if (ret != 0) {
768                                 logthing(LOGTHING_ERROR,
769                                         "Problem deleting subkey id: %s "
770                                         "(0x%016" PRIX64 ")",
771                                         db_strerror(ret),
772                                         keyid);
773                                 if (ret == DB_LOCK_DEADLOCK) {
774                                         deadlock = true;
775                                 }
776                         }
777
778                         shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
779
780                         memset(&key, 0, sizeof(key));
781                         memset(&data, 0, sizeof(data));
782                         key.data = &shortkeyid;
783                         key.size = sizeof(shortkeyid);
784                         data.data = &keyid;
785                         data.size = sizeof(keyid);
786
787                         ret = cursor->c_get(cursor,
788                                 &key,
789                                 &data,
790                                 DB_GET_BOTH);
791
792                         if (ret == 0) {
793                                 ret = cursor->c_del(cursor, 0);
794                         }
795
796                         if (ret != 0) {
797                                 logthing(LOGTHING_ERROR,
798                                         "Problem deleting short keyid: %s "
799                                         "(0x%016" PRIX64 ")",
800                                         db_strerror(ret),
801                                         keyid);
802                                 if (ret == DB_LOCK_DEADLOCK) {
803                                         deadlock = true;
804                                 }
805                         }
806                 }
807                 if (subkeyids != NULL) {
808                         free(subkeyids);
809                         subkeyids = NULL;
810                 }
811                 cursor->c_close(cursor);
812                 cursor = NULL;
813         }
814
815         if (!deadlock) {
816                 key.data = &keyid;
817                 key.size = sizeof(keyid);
818
819                 keydb(privctx, keyid)->del(keydb(privctx, keyid),
820                                 privctx->txn,
821                                 &key,
822                                 0); /* flags */
823         }
824
825         if (!intrans) {
826                 db4_endtrans(dbctx);
827         }
828
829         return deadlock ? (-1) : (ret == DB_NOTFOUND);
830 }
831
832 /**
833  *      store_key - Takes a key and stores it.
834  *      @publickey: A pointer to the public key to store.
835  *      @intrans: If we're already in a transaction.
836  *      @update: If true the key exists and should be updated.
837  *
838  *      Again we just use the hex representation of the keyid as the filename
839  *      to store the key to. We flatten the public key to a list of OpenPGP
840  *      packets and then use write_openpgp_stream() to write the stream out to
841  *      the file. If update is true then we delete the old key first, otherwise
842  *      we trust that it doesn't exist.
843  */
844 static int db4_store_key(struct onak_dbctx *dbctx,
845                 struct openpgp_publickey *publickey, bool intrans,
846                 bool update)
847 {
848         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
849         struct     openpgp_packet_list *packets = NULL;
850         struct     openpgp_packet_list *list_end = NULL;
851         struct     openpgp_publickey *next = NULL;
852         int        ret = 0;
853         int        i = 0;
854         struct     buffer_ctx storebuf;
855         DBT        key;
856         DBT        data;
857         uint64_t   keyid = 0;
858         uint32_t   shortkeyid = 0;
859         uint64_t  *subkeyids = NULL;
860         char     **uids = NULL;
861         char      *primary = NULL;
862         unsigned char worddb_data[12];
863         struct ll *wordlist = NULL;
864         struct ll *curword  = NULL;
865         bool       deadlock = false;
866         struct skshash hash;
867
868         if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
869                 logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
870                 return 0;
871         }
872
873         if (!intrans) {
874                 db4_starttrans(dbctx);
875         }
876
877         /*
878          * Delete the key if we already have it.
879          *
880          * TODO: Can we optimize this perhaps? Possibly when other data is
881          * involved as well? I suspect this is easiest and doesn't make a lot
882          * of difference though - the largest chunk of data is the keydata and
883          * it definitely needs updated.
884          */
885         if (update) {
886                 deadlock = (db4_delete_key(dbctx, keyid, true) == -1);
887         }
888
889         /*
890          * Convert the key to a flat set of binary data.
891          */
892         if (!deadlock) {
893                 next = publickey->next;
894                 publickey->next = NULL;
895                 flatten_publickey(publickey, &packets, &list_end);
896                 publickey->next = next;
897
898                 storebuf.offset = 0;
899                 storebuf.size = 8192;
900                 storebuf.buffer = malloc(8192);
901
902                 write_openpgp_stream(buffer_putchar, &storebuf, packets);
903
904                 /*
905                  * Now we have the key data store it in the DB; the keyid is
906                  * the key.
907                  */
908                 memset(&key, 0, sizeof(key));
909                 memset(&data, 0, sizeof(data));
910                 key.data = &keyid;
911                 key.size = sizeof(keyid);
912                 data.size = storebuf.offset;
913                 data.data = storebuf.buffer;
914
915                 ret = keydb(privctx, keyid)->put(keydb(privctx, keyid),
916                                 privctx->txn,
917                                 &key,
918                                 &data,
919                                 0); /* flags*/
920                 if (ret != 0) {
921                         logthing(LOGTHING_ERROR,
922                                         "Problem storing key: %s",
923                                         db_strerror(ret));
924                         if (ret == DB_LOCK_DEADLOCK) {
925                                 deadlock = true;
926                         }
927                 }
928
929                 free(storebuf.buffer);
930                 storebuf.buffer = NULL;
931                 storebuf.size = 0;
932                 storebuf.offset = 0;
933
934                 free_packet_list(packets);
935                 packets = NULL;
936         }
937
938         /*
939          * Walk through our uids storing the words into the db with the keyid.
940          */
941         if (!deadlock) {
942                 uids = keyuids(publickey, &primary);
943         }
944         if (uids != NULL) {
945                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
946                         wordlist = makewordlist(wordlist, uids[i]);
947                 }
948
949                 for (curword = wordlist; curword != NULL && !deadlock;
950                                 curword = curword->next) {
951                         memset(&key, 0, sizeof(key));
952                         memset(&data, 0, sizeof(data));
953                         key.data = curword->object;
954                         key.size = strlen(key.data);
955                         data.data = worddb_data;
956                         data.size = sizeof(worddb_data);
957
958                         /*
959                          * Our data is the key creation time followed by the
960                          * key id.
961                          */
962                         worddb_data[ 0] = publickey->publickey->data[1];
963                         worddb_data[ 1] = publickey->publickey->data[2];
964                         worddb_data[ 2] = publickey->publickey->data[3];
965                         worddb_data[ 3] = publickey->publickey->data[4];
966                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
967                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
968                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
969                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
970                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
971                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
972                         worddb_data[10] = (keyid >>  8) & 0xFF;
973                         worddb_data[11] = keyid & 0xFF;
974                         ret = privctx->worddb->put(privctx->worddb,
975                                 privctx->txn,
976                                 &key,
977                                 &data,
978                                 0);
979                         if (ret != 0) {
980                                 logthing(LOGTHING_ERROR,
981                                         "Problem storing word: %s",
982                                         db_strerror(ret));
983                                 if (ret == DB_LOCK_DEADLOCK) {
984                                         deadlock = true;
985                                 }
986                         }
987                 }
988
989                 /*
990                  * Free our UID and word lists.
991                  */
992                 llfree(wordlist, NULL);
993                 for (i = 0; uids[i] != NULL; i++) {
994                         free(uids[i]);
995                         uids[i] = NULL;
996                 }
997                 free(uids);
998                 uids = NULL;
999         }
1000
1001         /*
1002          * Write the truncated 32 bit keyid so we can lookup the full id for
1003          * queries.
1004          */
1005         if (!deadlock) {
1006                 shortkeyid = keyid & 0xFFFFFFFF;
1007
1008                 memset(&key, 0, sizeof(key));
1009                 memset(&data, 0, sizeof(data));
1010                 key.data = &shortkeyid;
1011                 key.size = sizeof(shortkeyid);
1012                 data.data = &keyid;
1013                 data.size = sizeof(keyid);
1014
1015                 ret = privctx->id32db->put(privctx->id32db,
1016                         privctx->txn,
1017                         &key,
1018                         &data,
1019                         0);
1020                 if (ret != 0) {
1021                         logthing(LOGTHING_ERROR,
1022                                 "Problem storing short keyid: %s",
1023                                 db_strerror(ret));
1024                         if (ret == DB_LOCK_DEADLOCK) {
1025                                 deadlock = true;
1026                         }
1027                 }
1028         }
1029
1030         if (!deadlock) {
1031                 subkeyids = keysubkeys(publickey);
1032                 i = 0;
1033                 while (subkeyids != NULL && subkeyids[i] != 0) {
1034                         /* Store the subkey ID -> main key ID mapping */
1035                         memset(&key, 0, sizeof(key));
1036                         memset(&data, 0, sizeof(data));
1037                         key.data = &subkeyids[i];
1038                         key.size = sizeof(subkeyids[i]);
1039                         data.data = &keyid;
1040                         data.size = sizeof(keyid);
1041
1042                         ret = privctx->subkeydb->put(privctx->subkeydb,
1043                                 privctx->txn,
1044                                 &key,
1045                                 &data,
1046                                 0);
1047                         if (ret != 0) {
1048                                 logthing(LOGTHING_ERROR,
1049                                         "Problem storing subkey keyid: %s",
1050                                         db_strerror(ret));
1051                                 if (ret == DB_LOCK_DEADLOCK) {
1052                                         deadlock = true;
1053                                 }
1054                         }
1055
1056                         /* Store the short subkey ID -> main key ID mapping */
1057                         shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
1058
1059                         memset(&key, 0, sizeof(key));
1060                         memset(&data, 0, sizeof(data));
1061                         key.data = &shortkeyid;
1062                         key.size = sizeof(shortkeyid);
1063                         data.data = &keyid;
1064                         data.size = sizeof(keyid);
1065
1066                         ret = privctx->id32db->put(privctx->id32db,
1067                                 privctx->txn,
1068                                 &key,
1069                                 &data,
1070                                 0);
1071                         if (ret != 0) {
1072                                 logthing(LOGTHING_ERROR,
1073                                         "Problem storing short keyid: %s",
1074                                         db_strerror(ret));
1075                                 if (ret == DB_LOCK_DEADLOCK) {
1076                                         deadlock = true;
1077                                 }
1078                         }
1079                 }
1080                 if (subkeyids != NULL) {
1081                         free(subkeyids);
1082                         subkeyids = NULL;
1083                 }
1084         }
1085
1086         if (!deadlock) {
1087                 get_skshash(publickey, &hash);
1088                 memset(&key, 0, sizeof(key));
1089                 memset(&data, 0, sizeof(data));
1090                 key.data = hash.hash;
1091                 key.size = sizeof(hash.hash);
1092                 data.data = &keyid;
1093                 data.size = sizeof(keyid);
1094
1095                 ret = privctx->skshashdb->put(privctx->skshashdb,
1096                         privctx->txn,
1097                         &key,
1098                         &data,
1099                         0);
1100                 if (ret != 0) {
1101                         logthing(LOGTHING_ERROR,
1102                                 "Problem storing SKS hash: %s",
1103                                 db_strerror(ret));
1104                         if (ret == DB_LOCK_DEADLOCK) {
1105                                 deadlock = true;
1106                         }
1107                 }
1108         }
1109
1110         if (!intrans) {
1111                 db4_endtrans(dbctx);
1112         }
1113
1114         return deadlock ? -1 : 0 ;
1115 }
1116
1117 /**
1118  *      iterate_keys - call a function once for each key in the db.
1119  *      @iterfunc: The function to call.
1120  *      @ctx: A context pointer
1121  *
1122  *      Calls iterfunc once for each key in the database. ctx is passed
1123  *      unaltered to iterfunc. This function is intended to aid database dumps
1124  *      and statistic calculations.
1125  *
1126  *      Returns the number of keys we iterated over.
1127  */
1128 static int db4_iterate_keys(struct onak_dbctx *dbctx,
1129                 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
1130                 void *ctx)
1131 {
1132         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
1133         DBT                         dbkey, data;
1134         DBC                        *cursor = NULL;
1135         int                         ret = 0;
1136         int                         i = 0;
1137         int                         numkeys = 0;
1138         struct buffer_ctx           fetchbuf;
1139         struct openpgp_packet_list *packets = NULL;
1140         struct openpgp_publickey   *key = NULL;
1141
1142         for (i = 0; i < privctx->numdbs; i++) {
1143                 ret = privctx->dbconns[i]->cursor(privctx->dbconns[i],
1144                         NULL,
1145                         &cursor,
1146                         0);   /* flags */
1147
1148                 if (ret != 0) {
1149                         continue;
1150                 }
1151
1152                 memset(&dbkey, 0, sizeof(dbkey));
1153                 memset(&data, 0, sizeof(data));
1154                 ret = cursor->c_get(cursor, &dbkey, &data, DB_NEXT);
1155                 while (ret == 0) {
1156                         fetchbuf.buffer = data.data;
1157                         fetchbuf.offset = 0;
1158                         fetchbuf.size = data.size;
1159                         read_openpgp_stream(buffer_fetchchar, &fetchbuf,
1160                                 &packets, 0);
1161                         parse_keys(packets, &key);
1162
1163                         iterfunc(ctx, key);
1164
1165                         free_publickey(key);
1166                         key = NULL;
1167                         free_packet_list(packets);
1168                         packets = NULL;
1169
1170                         memset(&dbkey, 0, sizeof(dbkey));
1171                         memset(&data, 0, sizeof(data));
1172                         ret = cursor->c_get(cursor, &dbkey, &data,
1173                                         DB_NEXT);
1174                         numkeys++;
1175                 }
1176                 if (ret != DB_NOTFOUND) {
1177                         logthing(LOGTHING_ERROR,
1178                                 "Problem reading key: %s",
1179                                 db_strerror(ret));
1180                 }
1181
1182                 cursor->c_close(cursor);
1183                 cursor = NULL;
1184         }
1185
1186         return numkeys;
1187 }
1188
1189 /*
1190  * Include the basic keydb routines.
1191  */
1192 #define NEED_GETKEYSIGS 1
1193 #define NEED_KEYID2UID 1
1194 #define NEED_UPDATEKEYS 1
1195 #define NEED_GET_FP 1
1196 #include "keydb.c"
1197
1198 /**
1199  *      cleanupdb - De-initialize the key database.
1200  *
1201  *      This function should be called upon program exit to allow the DB to
1202  *      cleanup after itself.
1203  */
1204 static void db4_cleanupdb(struct onak_dbctx *dbctx)
1205 {
1206         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
1207         int i = 0;
1208
1209         if (privctx->dbenv != NULL) {
1210                 privctx->dbenv->txn_checkpoint(privctx->dbenv, 0, 0, 0);
1211                 if (privctx->subkeydb != NULL) {
1212                         privctx->subkeydb->close(privctx->subkeydb, 0);
1213                         privctx->subkeydb = NULL;
1214                 }
1215                 if (privctx->skshashdb != NULL) {
1216                         privctx->skshashdb->close(privctx->skshashdb, 0);
1217                         privctx->skshashdb = NULL;
1218                 }
1219                 if (privctx->id32db != NULL) {
1220                         privctx->id32db->close(privctx->id32db, 0);
1221                         privctx->id32db = NULL;
1222                 }
1223                 if (privctx->worddb != NULL) {
1224                         privctx->worddb->close(privctx->worddb, 0);
1225                         privctx->worddb = NULL;
1226                 }
1227                 for (i = 0; i < privctx->numdbs; i++) {
1228                         if (privctx->dbconns[i] != NULL) {
1229                                 privctx->dbconns[i]->close(privctx->dbconns[i],
1230                                                 0);
1231                                 privctx->dbconns[i] = NULL;
1232                         }
1233                 }
1234                 free(privctx->dbconns);
1235                 privctx->dbconns = NULL;
1236                 privctx->dbenv->close(privctx->dbenv, 0);
1237                 privctx->dbenv = NULL;
1238         }
1239
1240         free(privctx);
1241         dbctx->priv = NULL;
1242         free(dbctx);
1243 }
1244
1245 /**
1246  *      initdb - Initialize the key database.
1247  *
1248  *      This function should be called before any of the other functions in
1249  *      this file are called in order to allow the DB to be initialized ready
1250  *      for access.
1251  */
1252 struct onak_dbctx *keydb_db4_init(bool readonly)
1253 {
1254         char       buf[1024];
1255         FILE      *numdb = NULL;
1256         int        ret = 0;
1257         int        i = 0;
1258         uint32_t   flags = 0;
1259         struct stat statbuf;
1260         int        maxlocks;
1261         struct onak_dbctx *dbctx;
1262         struct onak_db4_dbctx *privctx;
1263
1264         dbctx = malloc(sizeof(*dbctx));
1265         if (dbctx == NULL) {
1266                 return NULL;
1267         }
1268         dbctx->priv = privctx = calloc(1, sizeof(*privctx));
1269         if (privctx == NULL) {
1270                 free(dbctx);
1271                 return NULL;
1272         }
1273
1274         /* Default to 16 key data DBs */
1275         privctx->numdbs = 16;
1276
1277         snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
1278                         DB4_UPGRADE_FILE);
1279         ret = stat(buf, &statbuf);
1280         while ((ret == 0) || (errno != ENOENT)) {
1281                 if (ret != 0) {
1282                         logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade "
1283                                 "lock file: %s (%d)", strerror(errno), ret);
1284                         exit(1);
1285                 }
1286                 logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting.");
1287                 sleep(5);
1288                 ret = stat(buf, &statbuf);
1289         }
1290         ret = 0;
1291
1292         snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
1293         numdb = fopen(buf, "r");
1294         if (numdb != NULL) {
1295                 if (fgets(buf, sizeof(buf), numdb) != NULL) {
1296                         privctx->numdbs = atoi(buf);
1297                 }
1298                 fclose(numdb);
1299         } else if (!readonly) {
1300                 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
1301                                 strerror(errno));
1302                 numdb = fopen(buf, "w");
1303                 if (numdb != NULL) {
1304                         fprintf(numdb, "%d", privctx->numdbs);
1305                         fclose(numdb);
1306                 } else {
1307                         logthing(LOGTHING_ERROR,
1308                                 "Couldn't write num_keydb: %s",
1309                                 strerror(errno));
1310                 }
1311         }
1312
1313         privctx->dbconns = calloc(privctx->numdbs, sizeof (DB *));
1314         if (privctx->dbconns == NULL) {
1315                 logthing(LOGTHING_CRITICAL,
1316                                 "Couldn't allocate memory for dbconns");
1317                 ret = 1;
1318         }
1319
1320         if (ret == 0) {
1321                 ret = db_env_create(&privctx->dbenv, 0);
1322                 if (ret != 0) {
1323                         logthing(LOGTHING_CRITICAL,
1324                                 "db_env_create: %s", db_strerror(ret));
1325                 }
1326         }
1327
1328         /*
1329          * Up the number of locks we're allowed at once. We base this on
1330          * the maximum number of keys we're going to return.
1331          */
1332         maxlocks = config.maxkeys * 16;
1333         if (maxlocks < 1000) {
1334                 maxlocks = 1000;
1335         }
1336         privctx->dbenv->set_lk_max_locks(privctx->dbenv, maxlocks);
1337         privctx->dbenv->set_lk_max_objects(privctx->dbenv, maxlocks);
1338
1339         /*
1340          * Enable deadlock detection so that we don't block indefinitely on
1341          * anything. What we really want is simple 2 state locks, but I'm not
1342          * sure how to make the standard DB functions do that yet.
1343          */
1344         if (ret == 0) {
1345                 privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
1346                 ret = privctx->dbenv->set_lk_detect(privctx->dbenv, DB_LOCK_DEFAULT);
1347                 if (ret != 0) {
1348                         logthing(LOGTHING_CRITICAL,
1349                                 "db_env_create: %s", db_strerror(ret));
1350                 }
1351         }
1352
1353         if (ret == 0) {
1354                 ret = privctx->dbenv->open(privctx->dbenv, config.db_dir,
1355                                 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
1356                                 DB_INIT_TXN |
1357                                 DB_CREATE,
1358                                 0);
1359 #ifdef DB_VERSION_MISMATCH
1360                 if (ret == DB_VERSION_MISMATCH) {
1361                         privctx->dbenv->close(privctx->dbenv, 0);
1362                         privctx->dbenv = NULL;
1363                         ret = db4_upgradedb(privctx);
1364                         if (ret == 0) {
1365                                 ret = db_env_create(&privctx->dbenv, 0);
1366                         }
1367                         if (ret == 0) {
1368                                 privctx->dbenv->set_errcall(privctx->dbenv,
1369                                         &db4_errfunc);
1370                                 privctx->dbenv->set_lk_detect(privctx->dbenv,
1371                                         DB_LOCK_DEFAULT);
1372                                 ret = privctx->dbenv->open(privctx->dbenv,
1373                                         config.db_dir,
1374                                         DB_INIT_LOG | DB_INIT_MPOOL |
1375                                         DB_INIT_LOCK | DB_INIT_TXN |
1376                                         DB_CREATE | DB_RECOVER,
1377                                         0);
1378
1379                                 if (ret == 0) {
1380                                         privctx->dbenv->txn_checkpoint(
1381                                                         privctx->dbenv,
1382                                                         0,
1383                                                         0,
1384                                                         DB_FORCE);
1385                                 }
1386                         }
1387                 }
1388 #endif
1389                 if (ret != 0) {
1390                         logthing(LOGTHING_CRITICAL,
1391                                         "Error opening db environment: %s (%s)",
1392                                         config.db_dir,
1393                                         db_strerror(ret));
1394                         if (privctx->dbenv != NULL) {
1395                                 privctx->dbenv->close(privctx->dbenv, 0);
1396                                 privctx->dbenv = NULL;
1397                         }
1398                 }
1399         }
1400
1401         if (ret == 0) {
1402                 db4_starttrans(dbctx);
1403
1404                 for (i = 0; !ret && i < privctx->numdbs; i++) {
1405                         ret = db_create(&privctx->dbconns[i],
1406                                         privctx->dbenv, 0);
1407                         if (ret != 0) {
1408                                 logthing(LOGTHING_CRITICAL,
1409                                         "db_create: %s", db_strerror(ret));
1410                         }
1411
1412                         if (ret == 0) {
1413                                 snprintf(buf, 1023, "keydb.%d.db", i);
1414                                 flags = DB_CREATE;
1415                                 if (readonly) {
1416                                         flags = DB_RDONLY;
1417                                 }
1418                                 ret = privctx->dbconns[i]->open(
1419                                                 privctx->dbconns[i],
1420                                                 privctx->txn,
1421                                                 buf,
1422                                                 "keydb",
1423                                                 DB_HASH,
1424                                                 flags,
1425                                                 0664);
1426                                 if (ret != 0) {
1427                                         logthing(LOGTHING_CRITICAL,
1428                                                 "Error opening key database:"
1429                                                 " %s (%s)",
1430                                                 buf,
1431                                                 db_strerror(ret));
1432                                 }
1433                         }
1434                 }
1435         }
1436
1437         if (ret == 0) {
1438                 ret = db_create(&privctx->worddb, privctx->dbenv, 0);
1439                 if (ret != 0) {
1440                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1441                                         db_strerror(ret));
1442                 }
1443         }
1444
1445         if (ret == 0) {
1446                 ret = privctx->worddb->set_flags(privctx->worddb, DB_DUP);
1447         }
1448
1449         if (ret == 0) {
1450                 ret = privctx->worddb->open(privctx->worddb, privctx->txn,
1451                                 "worddb", "worddb", DB_BTREE,
1452                                 flags,
1453                                 0664);
1454                 if (ret != 0) {
1455                         logthing(LOGTHING_CRITICAL,
1456                                         "Error opening word database: %s (%s)",
1457                                         "worddb",
1458                                         db_strerror(ret));
1459                 }
1460         }
1461
1462         if (ret == 0) {
1463                 ret = db_create(&privctx->id32db, privctx->dbenv, 0);
1464                 if (ret != 0) {
1465                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1466                                         db_strerror(ret));
1467                 }
1468         }
1469
1470         if (ret == 0) {
1471                 ret = privctx->id32db->set_flags(privctx->id32db, DB_DUP);
1472         }
1473
1474         if (ret == 0) {
1475                 ret = privctx->id32db->open(privctx->id32db, privctx->txn,
1476                                 "id32db", "id32db", DB_HASH,
1477                                 flags,
1478                                 0664);
1479                 if (ret != 0) {
1480                         logthing(LOGTHING_CRITICAL,
1481                                         "Error opening id32 database: %s (%s)",
1482                                         "id32db",
1483                                         db_strerror(ret));
1484                 }
1485         }
1486
1487         if (ret == 0) {
1488                 ret = db_create(&privctx->skshashdb, 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->skshashdb->open(privctx->skshashdb, privctx->txn,
1497                                 "skshashdb",
1498                                 "skshashdb", DB_HASH,
1499                                 flags,
1500                                 0664);
1501                 if (ret != 0) {
1502                         logthing(LOGTHING_CRITICAL,
1503                                 "Error opening skshash database: %s (%s)",
1504                                 "skshashdb",
1505                                 db_strerror(ret));
1506                 }
1507         }
1508
1509         if (ret == 0) {
1510                 ret = db_create(&privctx->subkeydb, privctx->dbenv, 0);
1511                 if (ret != 0) {
1512                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1513                                         db_strerror(ret));
1514                 }
1515         }
1516
1517         if (ret == 0) {
1518                 ret = privctx->subkeydb->open(privctx->subkeydb, privctx->txn,
1519                                 "subkeydb", "subkeydb",
1520                                 DB_HASH,
1521                                 flags,
1522                                 0664);
1523                 if (ret != 0) {
1524                         logthing(LOGTHING_CRITICAL,
1525                                 "Error opening subkey database: %s (%s)",
1526                                 "subkeydb",
1527                                 db_strerror(ret));
1528                 }
1529         }
1530
1531         if (privctx->txn != NULL) {
1532                 db4_endtrans(dbctx);
1533         }
1534
1535         if (ret != 0) {
1536                 db4_cleanupdb(dbctx);
1537                 logthing(LOGTHING_CRITICAL,
1538                                 "Error opening database; exiting");
1539                 exit(EXIT_FAILURE);
1540         }
1541
1542         dbctx->cleanupdb                = db4_cleanupdb;
1543         dbctx->starttrans               = db4_starttrans;
1544         dbctx->endtrans                 = db4_endtrans;
1545         dbctx->fetch_key_id             = db4_fetch_key_id;
1546         dbctx->fetch_key_fp             = generic_fetch_key_fp;
1547         dbctx->fetch_key_text           = db4_fetch_key_text;
1548         dbctx->fetch_key_skshash        = db4_fetch_key_skshash;
1549         dbctx->store_key                = db4_store_key;
1550         dbctx->update_keys              = generic_update_keys;
1551         dbctx->delete_key               = db4_delete_key;
1552         dbctx->getkeysigs               = generic_getkeysigs;
1553         dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
1554         dbctx->keyid2uid                = generic_keyid2uid;
1555         dbctx->getfullkeyid             = db4_getfullkeyid;
1556         dbctx->iterate_keys             = db4_iterate_keys;
1557
1558         return dbctx;
1559 }