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