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