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