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