]> the.earth.li Git - onak.git/blob - keydb_db4.c
Another deletion fix of 64 bit keys 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                                 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                         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                                 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 = cursor64->c_get(cursor64,
1036                                 &key,
1037                                 &data,
1038                                 DB_GET_BOTH);
1039
1040                         if (ret == 0) {
1041                                 ret = cursor64->c_del(cursor64, 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                                 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 = fingerprint.fp;
1128                 key.size = fingerprint.length;
1129
1130                 keydb_fp(privctx, &fingerprint)->del(keydb_fp(privctx,
1131                                         &fingerprint),
1132                                 privctx->txn,
1133                                 &key,
1134                                 0); /* flags */
1135
1136                 /* Delete old style 64 bit keyid */
1137                 key.data = &keyid;
1138                 key.size = sizeof(keyid);
1139
1140                 keydb_id(privctx, keyid)->del(keydb_id(privctx, keyid),
1141                                 privctx->txn,
1142                                 &key,
1143                                 0); /* flags */
1144         }
1145
1146         if (!intrans) {
1147                 db4_endtrans(dbctx);
1148         }
1149
1150         return deadlock ? (-1) : (ret == DB_NOTFOUND);
1151 }
1152
1153 /**
1154  *      store_key - Takes a key and stores it.
1155  *      @publickey: A pointer to the public key to store.
1156  *      @intrans: If we're already in a transaction.
1157  *      @update: If true the key exists and should be updated.
1158  *
1159  *      Again we just use the hex representation of the keyid as the filename
1160  *      to store the key to. We flatten the public key to a list of OpenPGP
1161  *      packets and then use write_openpgp_stream() to write the stream out to
1162  *      the file. If update is true then we delete the old key first, otherwise
1163  *      we trust that it doesn't exist.
1164  */
1165 static int db4_store_key(struct onak_dbctx *dbctx,
1166                 struct openpgp_publickey *publickey, bool intrans,
1167                 bool update)
1168 {
1169         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
1170         struct     openpgp_packet_list *packets = NULL;
1171         struct     openpgp_packet_list *list_end = NULL;
1172         struct     openpgp_publickey *next = NULL;
1173         int        ret = 0;
1174         int        i = 0;
1175         struct     buffer_ctx storebuf;
1176         DBT        key;
1177         DBT        data;
1178         uint64_t   keyid = 0;
1179         uint32_t   shortkeyid = 0;
1180         struct openpgp_fingerprint *subkeyids = NULL;
1181         char     **uids = NULL;
1182         char      *primary = NULL;
1183         struct ll *wordlist = NULL;
1184         struct ll *curword  = NULL;
1185         bool       deadlock = false;
1186         struct skshash hash;
1187         struct openpgp_fingerprint fingerprint;
1188
1189         if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
1190                 logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
1191                 return 0;
1192         }
1193
1194         if (get_fingerprint(publickey->publickey, &fingerprint) != ONAK_E_OK) {
1195                 logthing(LOGTHING_ERROR, "Couldn't find fingerprint for key.");
1196                 return 0;
1197         }
1198
1199         if (!intrans) {
1200                 db4_starttrans(dbctx);
1201         }
1202
1203         /*
1204          * Delete the key if we already have it.
1205          *
1206          * TODO: Can we optimize this perhaps? Possibly when other data is
1207          * involved as well? I suspect this is easiest and doesn't make a lot
1208          * of difference though - the largest chunk of data is the keydata and
1209          * it definitely needs updated.
1210          */
1211         if (update) {
1212                 deadlock = (db4_delete_key(dbctx, keyid, true) == -1);
1213         }
1214
1215         /*
1216          * Convert the key to a flat set of binary data.
1217          */
1218         if (!deadlock) {
1219                 next = publickey->next;
1220                 publickey->next = NULL;
1221                 flatten_publickey(publickey, &packets, &list_end);
1222                 publickey->next = next;
1223
1224                 storebuf.offset = 0;
1225                 storebuf.size = 8192;
1226                 storebuf.buffer = malloc(8192);
1227
1228                 write_openpgp_stream(buffer_putchar, &storebuf, packets);
1229
1230                 /*
1231                  * Now we have the key data store it in the DB; the keyid is
1232                  * the key.
1233                  */
1234                 memset(&key, 0, sizeof(key));
1235                 memset(&data, 0, sizeof(data));
1236                 key.data = fingerprint.fp;
1237                 key.size = fingerprint.length;
1238                 data.size = storebuf.offset;
1239                 data.data = storebuf.buffer;
1240
1241                 ret = keydb_fp(privctx, &fingerprint)->put(
1242                                 keydb_fp(privctx, &fingerprint),
1243                                 privctx->txn,
1244                                 &key,
1245                                 &data,
1246                                 0); /* flags*/
1247                 if (ret != 0) {
1248                         logthing(LOGTHING_ERROR,
1249                                         "Problem storing key: %s",
1250                                         db_strerror(ret));
1251                         if (ret == DB_LOCK_DEADLOCK) {
1252                                 deadlock = true;
1253                         }
1254                 }
1255
1256                 free(storebuf.buffer);
1257                 storebuf.buffer = NULL;
1258                 storebuf.size = 0;
1259                 storebuf.offset = 0;
1260
1261                 free_packet_list(packets);
1262                 packets = NULL;
1263         }
1264
1265         /*
1266          * Walk through our uids storing the words into the db with the keyid.
1267          */
1268         if (!deadlock) {
1269                 uids = keyuids(publickey, &primary);
1270         }
1271         if (uids != NULL) {
1272                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
1273                         wordlist = makewordlist(wordlist, uids[i]);
1274                 }
1275
1276                 for (curword = wordlist; curword != NULL && !deadlock;
1277                                 curword = curword->next) {
1278                         memset(&key, 0, sizeof(key));
1279                         memset(&data, 0, sizeof(data));
1280                         key.data = curword->object;
1281                         key.size = strlen(key.data);
1282                         data.data = fingerprint.fp;
1283                         data.size = fingerprint.length;
1284
1285                         ret = privctx->worddb->put(privctx->worddb,
1286                                 privctx->txn,
1287                                 &key,
1288                                 &data,
1289                                 0);
1290                         if (ret != 0) {
1291                                 logthing(LOGTHING_ERROR,
1292                                         "Problem storing word: %s",
1293                                         db_strerror(ret));
1294                                 if (ret == DB_LOCK_DEADLOCK) {
1295                                         deadlock = true;
1296                                 }
1297                         }
1298                 }
1299
1300                 /*
1301                  * Free our UID and word lists.
1302                  */
1303                 llfree(wordlist, NULL);
1304                 for (i = 0; uids[i] != NULL; i++) {
1305                         free(uids[i]);
1306                         uids[i] = NULL;
1307                 }
1308                 free(uids);
1309                 uids = NULL;
1310         }
1311
1312         /*
1313          * Write the truncated 32 bit keyid so we can lookup the fingerprint
1314          * for queries.
1315          */
1316         if (!deadlock) {
1317                 shortkeyid = keyid & 0xFFFFFFFF;
1318
1319                 memset(&key, 0, sizeof(key));
1320                 memset(&data, 0, sizeof(data));
1321                 key.data = &shortkeyid;
1322                 key.size = sizeof(shortkeyid);
1323                 data.data = fingerprint.fp;
1324                 data.size = fingerprint.length;
1325
1326                 ret = privctx->id32db->put(privctx->id32db,
1327                         privctx->txn,
1328                         &key,
1329                         &data,
1330                         0);
1331                 if (ret != 0) {
1332                         logthing(LOGTHING_ERROR,
1333                                 "Problem storing short keyid: %s",
1334                                 db_strerror(ret));
1335                         if (ret == DB_LOCK_DEADLOCK) {
1336                                 deadlock = true;
1337                         }
1338                 }
1339         }
1340
1341         /*
1342          * Write the 64 bit keyid so we can lookup the fingerprint for
1343          * queries.
1344          */
1345         if (!deadlock) {
1346                 memset(&key, 0, sizeof(key));
1347                 memset(&data, 0, sizeof(data));
1348                 key.data = &keyid;
1349                 key.size = sizeof(keyid);
1350                 data.data = fingerprint.fp;
1351                 data.size = fingerprint.length;
1352
1353                 ret = privctx->id64db->put(privctx->id64db,
1354                         privctx->txn,
1355                         &key,
1356                         &data,
1357                         0);
1358                 if (ret != 0) {
1359                         logthing(LOGTHING_ERROR,
1360                                 "Problem storing keyid: %s",
1361                                 db_strerror(ret));
1362                         if (ret == DB_LOCK_DEADLOCK) {
1363                                 deadlock = true;
1364                         }
1365                 }
1366         }
1367
1368         if (!deadlock) {
1369                 subkeyids = keysubkeys(publickey);
1370                 i = 0;
1371                 while (subkeyids != NULL && subkeyids[i].length != 0) {
1372                         /* Store the subkey ID -> main key fp mapping */
1373                         memset(&key, 0, sizeof(key));
1374                         memset(&data, 0, sizeof(data));
1375                         key.data = subkeyids[i].fp;
1376                         key.size = subkeyids[i].length;
1377                         data.data = fingerprint.fp;
1378                         data.size = fingerprint.length;
1379
1380                         ret = privctx->subkeydb->put(privctx->subkeydb,
1381                                 privctx->txn,
1382                                 &key,
1383                                 &data,
1384                                 0);
1385                         if (ret != 0) {
1386                                 logthing(LOGTHING_ERROR,
1387                                         "Problem storing subkey keyid: %s",
1388                                         db_strerror(ret));
1389                                 if (ret == DB_LOCK_DEADLOCK) {
1390                                         deadlock = true;
1391                                 }
1392                         }
1393
1394                         /* Store the 64 bit subkey ID -> main key fp mapping */
1395                         memset(&key, 0, sizeof(key));
1396                         memset(&data, 0, sizeof(data));
1397
1398                         keyid = fingerprint2keyid(&subkeyids[i]);
1399                         key.data = &keyid;
1400                         key.size = sizeof(keyid);
1401                         data.data = fingerprint.fp;
1402                         data.size = fingerprint.length;
1403
1404                         ret = privctx->id64db->put(privctx->id64db,
1405                                 privctx->txn,
1406                                 &key,
1407                                 &data,
1408                                 0);
1409                         if (ret != 0) {
1410                                 logthing(LOGTHING_ERROR,
1411                                         "Problem storing keyid: %s",
1412                                         db_strerror(ret));
1413                                 if (ret == DB_LOCK_DEADLOCK) {
1414                                         deadlock = true;
1415                                 }
1416                         }
1417
1418                         /* Store the short subkey ID -> main key fp mapping */
1419                         shortkeyid = keyid & 0xFFFFFFFF;
1420
1421                         memset(&key, 0, sizeof(key));
1422                         memset(&data, 0, sizeof(data));
1423                         key.data = &shortkeyid;
1424                         key.size = sizeof(shortkeyid);
1425                         data.data = fingerprint.fp;
1426                         data.size = fingerprint.length;
1427
1428                         ret = privctx->id32db->put(privctx->id32db,
1429                                 privctx->txn,
1430                                 &key,
1431                                 &data,
1432                                 0);
1433                         if (ret != 0) {
1434                                 logthing(LOGTHING_ERROR,
1435                                         "Problem storing short keyid: %s",
1436                                         db_strerror(ret));
1437                                 if (ret == DB_LOCK_DEADLOCK) {
1438                                         deadlock = true;
1439                                 }
1440                         }
1441                         i++;
1442                 }
1443                 if (subkeyids != NULL) {
1444                         free(subkeyids);
1445                         subkeyids = NULL;
1446                 }
1447         }
1448
1449         if (!deadlock) {
1450                 get_skshash(publickey, &hash);
1451                 memset(&key, 0, sizeof(key));
1452                 memset(&data, 0, sizeof(data));
1453                 key.data = hash.hash;
1454                 key.size = sizeof(hash.hash);
1455                 data.data = fingerprint.fp;
1456                 data.size = fingerprint.length;
1457
1458                 ret = privctx->skshashdb->put(privctx->skshashdb,
1459                         privctx->txn,
1460                         &key,
1461                         &data,
1462                         0);
1463                 if (ret != 0) {
1464                         logthing(LOGTHING_ERROR,
1465                                 "Problem storing SKS hash: %s",
1466                                 db_strerror(ret));
1467                         if (ret == DB_LOCK_DEADLOCK) {
1468                                 deadlock = true;
1469                         }
1470                 }
1471         }
1472
1473         if (!intrans) {
1474                 db4_endtrans(dbctx);
1475         }
1476
1477         return deadlock ? -1 : 0 ;
1478 }
1479
1480 /**
1481  *      iterate_keys - call a function once for each key in the db.
1482  *      @iterfunc: The function to call.
1483  *      @ctx: A context pointer
1484  *
1485  *      Calls iterfunc once for each key in the database. ctx is passed
1486  *      unaltered to iterfunc. This function is intended to aid database dumps
1487  *      and statistic calculations.
1488  *
1489  *      Returns the number of keys we iterated over.
1490  */
1491 static int db4_iterate_keys(struct onak_dbctx *dbctx,
1492                 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
1493                 void *ctx)
1494 {
1495         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
1496         DBT                         dbkey, data;
1497         DBC                        *cursor = NULL;
1498         int                         ret = 0;
1499         int                         i = 0;
1500         int                         numkeys = 0;
1501         struct buffer_ctx           fetchbuf;
1502         struct openpgp_packet_list *packets = NULL;
1503         struct openpgp_publickey   *key = NULL;
1504
1505         for (i = 0; i < privctx->numdbs; i++) {
1506                 ret = privctx->dbconns[i]->cursor(privctx->dbconns[i],
1507                         NULL,
1508                         &cursor,
1509                         0);   /* flags */
1510
1511                 if (ret != 0) {
1512                         continue;
1513                 }
1514
1515                 memset(&dbkey, 0, sizeof(dbkey));
1516                 memset(&data, 0, sizeof(data));
1517                 ret = cursor->c_get(cursor, &dbkey, &data, DB_NEXT);
1518                 while (ret == 0) {
1519                         fetchbuf.buffer = data.data;
1520                         fetchbuf.offset = 0;
1521                         fetchbuf.size = data.size;
1522                         read_openpgp_stream(buffer_fetchchar, &fetchbuf,
1523                                 &packets, 0);
1524                         parse_keys(packets, &key);
1525
1526                         iterfunc(ctx, key);
1527
1528                         free_publickey(key);
1529                         key = NULL;
1530                         free_packet_list(packets);
1531                         packets = NULL;
1532
1533                         memset(&dbkey, 0, sizeof(dbkey));
1534                         memset(&data, 0, sizeof(data));
1535                         ret = cursor->c_get(cursor, &dbkey, &data,
1536                                         DB_NEXT);
1537                         numkeys++;
1538                 }
1539                 if (ret != DB_NOTFOUND) {
1540                         logthing(LOGTHING_ERROR,
1541                                 "Problem reading key: %s",
1542                                 db_strerror(ret));
1543                 }
1544
1545                 cursor->c_close(cursor);
1546                 cursor = NULL;
1547         }
1548
1549         return numkeys;
1550 }
1551
1552 /*
1553  * Include the basic keydb routines.
1554  */
1555 #define NEED_GETKEYSIGS 1
1556 #define NEED_KEYID2UID 1
1557 #define NEED_UPDATEKEYS 1
1558 #include "keydb.c"
1559
1560 /**
1561  *      cleanupdb - De-initialize the key database.
1562  *
1563  *      This function should be called upon program exit to allow the DB to
1564  *      cleanup after itself.
1565  */
1566 static void db4_cleanupdb(struct onak_dbctx *dbctx)
1567 {
1568         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
1569         int i = 0;
1570
1571         if (privctx->dbenv != NULL) {
1572                 privctx->dbenv->txn_checkpoint(privctx->dbenv, 0, 0, 0);
1573                 if (privctx->subkeydb != NULL) {
1574                         privctx->subkeydb->close(privctx->subkeydb, 0);
1575                         privctx->subkeydb = NULL;
1576                 }
1577                 if (privctx->skshashdb != NULL) {
1578                         privctx->skshashdb->close(privctx->skshashdb, 0);
1579                         privctx->skshashdb = NULL;
1580                 }
1581                 if (privctx->id64db != NULL) {
1582                         privctx->id64db->close(privctx->id64db, 0);
1583                         privctx->id64db = NULL;
1584                 }
1585                 if (privctx->id32db != NULL) {
1586                         privctx->id32db->close(privctx->id32db, 0);
1587                         privctx->id32db = NULL;
1588                 }
1589                 if (privctx->worddb != NULL) {
1590                         privctx->worddb->close(privctx->worddb, 0);
1591                         privctx->worddb = NULL;
1592                 }
1593                 for (i = 0; i < privctx->numdbs; i++) {
1594                         if (privctx->dbconns[i] != NULL) {
1595                                 privctx->dbconns[i]->close(privctx->dbconns[i],
1596                                                 0);
1597                                 privctx->dbconns[i] = NULL;
1598                         }
1599                 }
1600                 free(privctx->dbconns);
1601                 privctx->dbconns = NULL;
1602                 privctx->dbenv->close(privctx->dbenv, 0);
1603                 privctx->dbenv = NULL;
1604         }
1605
1606         free(privctx);
1607         dbctx->priv = NULL;
1608         free(dbctx);
1609 }
1610
1611 /**
1612  *      initdb - Initialize the key database.
1613  *
1614  *      This function should be called before any of the other functions in
1615  *      this file are called in order to allow the DB to be initialized ready
1616  *      for access.
1617  */
1618 struct onak_dbctx *keydb_db4_init(bool readonly)
1619 {
1620         char       buf[1024];
1621         FILE      *numdb = NULL;
1622         int        ret = 0;
1623         int        i = 0;
1624         uint32_t   flags = 0;
1625         struct stat statbuf;
1626         int        maxlocks;
1627         struct onak_dbctx *dbctx;
1628         struct onak_db4_dbctx *privctx;
1629
1630         dbctx = malloc(sizeof(*dbctx));
1631         if (dbctx == NULL) {
1632                 return NULL;
1633         }
1634         dbctx->priv = privctx = calloc(1, sizeof(*privctx));
1635         if (privctx == NULL) {
1636                 free(dbctx);
1637                 return NULL;
1638         }
1639
1640         /* Default to 16 key data DBs */
1641         privctx->numdbs = 16;
1642
1643         snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
1644                         DB4_UPGRADE_FILE);
1645         ret = stat(buf, &statbuf);
1646         while ((ret == 0) || (errno != ENOENT)) {
1647                 if (ret != 0) {
1648                         logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade "
1649                                 "lock file: %s (%d)", strerror(errno), ret);
1650                         exit(1);
1651                 }
1652                 logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting.");
1653                 sleep(5);
1654                 ret = stat(buf, &statbuf);
1655         }
1656         ret = 0;
1657
1658         snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
1659         numdb = fopen(buf, "r");
1660         if (numdb != NULL) {
1661                 if (fgets(buf, sizeof(buf), numdb) != NULL) {
1662                         privctx->numdbs = atoi(buf);
1663                 }
1664                 fclose(numdb);
1665         } else if (!readonly) {
1666                 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
1667                                 strerror(errno));
1668                 numdb = fopen(buf, "w");
1669                 if (numdb != NULL) {
1670                         fprintf(numdb, "%d", privctx->numdbs);
1671                         fclose(numdb);
1672                 } else {
1673                         logthing(LOGTHING_ERROR,
1674                                 "Couldn't write num_keydb: %s",
1675                                 strerror(errno));
1676                 }
1677         }
1678
1679         privctx->dbconns = calloc(privctx->numdbs, sizeof (DB *));
1680         if (privctx->dbconns == NULL) {
1681                 logthing(LOGTHING_CRITICAL,
1682                                 "Couldn't allocate memory for dbconns");
1683                 ret = 1;
1684         }
1685
1686         if (ret == 0) {
1687                 ret = db_env_create(&privctx->dbenv, 0);
1688                 if (ret != 0) {
1689                         logthing(LOGTHING_CRITICAL,
1690                                 "db_env_create: %s", db_strerror(ret));
1691                 }
1692         }
1693
1694         /*
1695          * Up the number of locks we're allowed at once. We base this on
1696          * the maximum number of keys we're going to return.
1697          */
1698         maxlocks = config.maxkeys * 16;
1699         if (maxlocks < 1000) {
1700                 maxlocks = 1000;
1701         }
1702         privctx->dbenv->set_lk_max_locks(privctx->dbenv, maxlocks);
1703         privctx->dbenv->set_lk_max_objects(privctx->dbenv, maxlocks);
1704
1705         /*
1706          * Enable deadlock detection so that we don't block indefinitely on
1707          * anything. What we really want is simple 2 state locks, but I'm not
1708          * sure how to make the standard DB functions do that yet.
1709          */
1710         if (ret == 0) {
1711                 privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
1712                 ret = privctx->dbenv->set_lk_detect(privctx->dbenv, DB_LOCK_DEFAULT);
1713                 if (ret != 0) {
1714                         logthing(LOGTHING_CRITICAL,
1715                                 "db_env_create: %s", db_strerror(ret));
1716                 }
1717         }
1718
1719         if (ret == 0) {
1720                 ret = privctx->dbenv->open(privctx->dbenv, config.db_dir,
1721                                 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
1722                                 DB_INIT_TXN |
1723                                 DB_CREATE,
1724                                 0);
1725 #ifdef DB_VERSION_MISMATCH
1726                 if (ret == DB_VERSION_MISMATCH) {
1727                         privctx->dbenv->close(privctx->dbenv, 0);
1728                         privctx->dbenv = NULL;
1729                         ret = db4_upgradedb(privctx);
1730                         if (ret == 0) {
1731                                 ret = db_env_create(&privctx->dbenv, 0);
1732                         }
1733                         if (ret == 0) {
1734                                 privctx->dbenv->set_errcall(privctx->dbenv,
1735                                         &db4_errfunc);
1736                                 privctx->dbenv->set_lk_detect(privctx->dbenv,
1737                                         DB_LOCK_DEFAULT);
1738                                 ret = privctx->dbenv->open(privctx->dbenv,
1739                                         config.db_dir,
1740                                         DB_INIT_LOG | DB_INIT_MPOOL |
1741                                         DB_INIT_LOCK | DB_INIT_TXN |
1742                                         DB_CREATE | DB_RECOVER,
1743                                         0);
1744
1745                                 if (ret == 0) {
1746                                         privctx->dbenv->txn_checkpoint(
1747                                                         privctx->dbenv,
1748                                                         0,
1749                                                         0,
1750                                                         DB_FORCE);
1751                                 }
1752                         }
1753                 }
1754 #endif
1755                 if (ret != 0) {
1756                         logthing(LOGTHING_CRITICAL,
1757                                         "Error opening db environment: %s (%s)",
1758                                         config.db_dir,
1759                                         db_strerror(ret));
1760                         if (privctx->dbenv != NULL) {
1761                                 privctx->dbenv->close(privctx->dbenv, 0);
1762                                 privctx->dbenv = NULL;
1763                         }
1764                 }
1765         }
1766
1767         if (ret == 0) {
1768                 db4_starttrans(dbctx);
1769
1770                 for (i = 0; !ret && i < privctx->numdbs; i++) {
1771                         ret = db_create(&privctx->dbconns[i],
1772                                         privctx->dbenv, 0);
1773                         if (ret != 0) {
1774                                 logthing(LOGTHING_CRITICAL,
1775                                         "db_create: %s", db_strerror(ret));
1776                         }
1777
1778                         if (ret == 0) {
1779                                 snprintf(buf, 1023, "keydb.%d.db", i);
1780                                 flags = DB_CREATE;
1781                                 if (readonly) {
1782                                         flags = DB_RDONLY;
1783                                 }
1784                                 ret = privctx->dbconns[i]->open(
1785                                                 privctx->dbconns[i],
1786                                                 privctx->txn,
1787                                                 buf,
1788                                                 "keydb",
1789                                                 DB_HASH,
1790                                                 flags,
1791                                                 0664);
1792                                 if (ret != 0) {
1793                                         logthing(LOGTHING_CRITICAL,
1794                                                 "Error opening key database:"
1795                                                 " %s (%s)",
1796                                                 buf,
1797                                                 db_strerror(ret));
1798                                 }
1799                         }
1800                 }
1801         }
1802
1803         if (ret == 0) {
1804                 ret = db_create(&privctx->worddb, privctx->dbenv, 0);
1805                 if (ret != 0) {
1806                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1807                                         db_strerror(ret));
1808                 }
1809         }
1810
1811         if (ret == 0) {
1812                 ret = privctx->worddb->set_flags(privctx->worddb, DB_DUP);
1813         }
1814
1815         if (ret == 0) {
1816                 ret = privctx->worddb->open(privctx->worddb, privctx->txn,
1817                                 "worddb", "worddb", DB_BTREE,
1818                                 flags,
1819                                 0664);
1820                 if (ret != 0) {
1821                         logthing(LOGTHING_CRITICAL,
1822                                         "Error opening word database: %s (%s)",
1823                                         "worddb",
1824                                         db_strerror(ret));
1825                 }
1826         }
1827
1828         if (ret == 0) {
1829                 ret = db_create(&privctx->id32db, privctx->dbenv, 0);
1830                 if (ret != 0) {
1831                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1832                                         db_strerror(ret));
1833                 }
1834         }
1835
1836         if (ret == 0) {
1837                 ret = privctx->id32db->set_flags(privctx->id32db, DB_DUP);
1838         }
1839
1840         if (ret == 0) {
1841                 ret = privctx->id32db->open(privctx->id32db, privctx->txn,
1842                                 "id32db", "id32db", DB_HASH,
1843                                 flags,
1844                                 0664);
1845                 if (ret != 0) {
1846                         logthing(LOGTHING_CRITICAL,
1847                                         "Error opening id32 database: %s (%s)",
1848                                         "id32db",
1849                                         db_strerror(ret));
1850                 }
1851         }
1852
1853         if (ret == 0) {
1854                 ret = db_create(&privctx->id64db, privctx->dbenv, 0);
1855                 if (ret != 0) {
1856                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1857                                         db_strerror(ret));
1858                 }
1859         }
1860
1861         if (ret == 0) {
1862                 ret = privctx->id64db->set_flags(privctx->id64db, DB_DUP);
1863         }
1864
1865         if (ret == 0) {
1866                 ret = privctx->id64db->open(privctx->id64db, privctx->txn,
1867                                 "id64db", "id64db", DB_HASH,
1868                                 flags,
1869                                 0664);
1870                 if (ret != 0) {
1871                         logthing(LOGTHING_CRITICAL,
1872                                         "Error opening id64 database: %s (%s)",
1873                                         "id64db",
1874                                         db_strerror(ret));
1875                 }
1876         }
1877
1878         if (ret == 0) {
1879                 ret = db_create(&privctx->skshashdb, privctx->dbenv, 0);
1880                 if (ret != 0) {
1881                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1882                                         db_strerror(ret));
1883                 }
1884         }
1885
1886         if (ret == 0) {
1887                 ret = privctx->skshashdb->open(privctx->skshashdb, privctx->txn,
1888                                 "skshashdb",
1889                                 "skshashdb", DB_HASH,
1890                                 flags,
1891                                 0664);
1892                 if (ret != 0) {
1893                         logthing(LOGTHING_CRITICAL,
1894                                 "Error opening skshash database: %s (%s)",
1895                                 "skshashdb",
1896                                 db_strerror(ret));
1897                 }
1898         }
1899
1900         if (ret == 0) {
1901                 ret = db_create(&privctx->subkeydb, privctx->dbenv, 0);
1902                 if (ret != 0) {
1903                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1904                                         db_strerror(ret));
1905                 }
1906         }
1907
1908         if (ret == 0) {
1909                 ret = privctx->subkeydb->open(privctx->subkeydb, privctx->txn,
1910                                 "subkeydb", "subkeydb",
1911                                 DB_HASH,
1912                                 flags,
1913                                 0664);
1914                 if (ret != 0) {
1915                         logthing(LOGTHING_CRITICAL,
1916                                 "Error opening subkey database: %s (%s)",
1917                                 "subkeydb",
1918                                 db_strerror(ret));
1919                 }
1920         }
1921
1922         if (privctx->txn != NULL) {
1923                 db4_endtrans(dbctx);
1924         }
1925
1926         if (ret != 0) {
1927                 db4_cleanupdb(dbctx);
1928                 logthing(LOGTHING_CRITICAL,
1929                                 "Error opening database; exiting");
1930                 exit(EXIT_FAILURE);
1931         }
1932
1933         dbctx->cleanupdb                = db4_cleanupdb;
1934         dbctx->starttrans               = db4_starttrans;
1935         dbctx->endtrans                 = db4_endtrans;
1936         dbctx->fetch_key_id             = db4_fetch_key_id;
1937         dbctx->fetch_key_fp             = db4_fetch_key_fp;
1938         dbctx->fetch_key_text           = db4_fetch_key_text;
1939         dbctx->fetch_key_skshash        = db4_fetch_key_skshash;
1940         dbctx->store_key                = db4_store_key;
1941         dbctx->update_keys              = generic_update_keys;
1942         dbctx->delete_key               = db4_delete_key;
1943         dbctx->getkeysigs               = generic_getkeysigs;
1944         dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
1945         dbctx->keyid2uid                = generic_keyid2uid;
1946         dbctx->getfullkeyid             = db4_getfullkeyid;
1947         dbctx->iterate_keys             = db4_iterate_keys;
1948
1949         return dbctx;
1950 }