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