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