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