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