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