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