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