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