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