]> the.earth.li Git - onak.git/blob - keydb_db4.c
Fix up memory leaks in fs keydb backend deletion
[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  *      fetch_key_text - Trys to find the keys that contain the supplied text.
477  *      @search: The text to search for.
478  *      @publickey: A pointer to a structure to return the key in.
479  *
480  *      This function searches for the supplied text and returns the keys that
481  *      contain it.
482  */
483 static int db4_fetch_key_text(struct onak_dbctx *dbctx, const char *search,
484                 struct openpgp_publickey **publickey)
485 {
486         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
487         DBC *cursor = NULL;
488         DBT key, data;
489         int ret;
490         uint64_t keyid;
491         int i;
492         int numkeys;
493         char *searchtext = NULL;
494         struct ll *wordlist = NULL;
495         struct ll *curword = NULL;
496         struct keyarray keylist = { NULL, 0, 0 };
497         struct keyarray newkeylist = { NULL, 0, 0 };
498         int firstpass = 1;
499         struct openpgp_fingerprint fingerprint;
500
501         numkeys = 0;
502         searchtext = strdup(search);
503         wordlist = makewordlist(wordlist, searchtext);
504
505         for (curword = wordlist; curword != NULL; curword = curword->next) {
506                 db4_starttrans(dbctx);
507
508                 ret = privctx->worddb->cursor(privctx->worddb,
509                                 privctx->txn,
510                                 &cursor,
511                                 0);   /* flags */
512
513                 if (ret != 0) {
514                         db4_endtrans(dbctx);
515                         break;
516                 }
517
518                 memset(&key, 0, sizeof(key));
519                 memset(&data, 0, sizeof(data));
520                 key.data = curword->object;
521                 key.size = strlen(curword->object);
522                 data.flags = DB_DBT_MALLOC;
523                 ret = cursor->c_get(cursor,
524                                 &key,
525                                 &data,
526                                 DB_SET);
527                 while (ret == 0 && strncmp(key.data, curword->object,
528                                         key.size) == 0 &&
529                                 ((char *) curword->object)[key.size] == 0) {
530
531                         fingerprint.length = data.size;
532                         memcpy(fingerprint.fp, data.data, data.size);
533
534                         /*
535                          * Only add the keys containing this word if this is
536                          * our first pass (ie we have no existing key list),
537                          * or the key contained a previous word.
538                          */
539                         if (firstpass || array_find(&keylist, &fingerprint)) {
540                                 array_add(&newkeylist, &fingerprint);
541                         }
542
543                         free(data.data);
544                         data.data = NULL;
545
546                         ret = cursor->c_get(cursor,
547                                         &key,
548                                         &data,
549                                         DB_NEXT);
550                 }
551                 array_free(&keylist);
552                 keylist.keys = newkeylist.keys;
553                 keylist.count = newkeylist.count;
554                 keylist.size = newkeylist.size;
555                 newkeylist.keys = NULL;
556                 newkeylist.count = newkeylist.size = 0;
557                 if (data.data != NULL) {
558                         free(data.data);
559                         data.data = NULL;
560                 }
561                 cursor->c_close(cursor);
562                 cursor = NULL;
563                 firstpass = 0;
564                 db4_endtrans(dbctx);
565         }
566         llfree(wordlist, NULL);
567         wordlist = NULL;
568
569         if (keylist.count > config.maxkeys) {
570                 keylist.count = config.maxkeys;
571         }
572
573         db4_starttrans(dbctx);
574         for (i = 0; i < keylist.count; i++) {
575                 numkeys += db4_fetch_key_fp(dbctx, &keylist.keys[i],
576                         publickey,
577                         true);
578         }
579         array_free(&keylist);
580         free(searchtext);
581         searchtext = NULL;
582
583         db4_endtrans(dbctx);
584
585         return (numkeys);
586 }
587
588 static int db4_fetch_key_skshash(struct onak_dbctx *dbctx,
589                 const struct skshash *hash,
590                 struct openpgp_publickey **publickey)
591 {
592         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
593         DBT       key, data;
594         DBC      *cursor = NULL;
595         uint64_t  keyid = 0;
596         int       ret;
597         int       count = 0;
598         struct openpgp_fingerprint fingerprint;
599
600         ret = privctx->skshashdb->cursor(privctx->skshashdb,
601                         privctx->txn,
602                         &cursor,
603                         0);   /* flags */
604
605         if (ret != 0) {
606                 return 0;
607         }
608
609         memset(&key, 0, sizeof(key));
610         memset(&data, 0, sizeof(data));
611         key.data = (void *) hash->hash;
612         key.size = sizeof(hash->hash);
613         data.ulen = MAX_FINGERPRINT_LEN;
614         data.data = fingerprint.fp;
615         data.flags = DB_DBT_USERMEM;
616
617         ret = cursor->c_get(cursor,
618                 &key,
619                 &data,
620                 DB_SET);
621
622         if (ret == 0) {
623                 fingerprint.length = data.size;
624                 count = db4_fetch_key_fp(dbctx, &fingerprint,
625                         publickey, false);
626         }
627
628         cursor->c_close(cursor);
629         cursor = NULL;
630
631         return count;
632 }
633
634 /**
635  *      delete_key - Given a keyid delete the key from storage.
636  *      @fp: The fingerprint of the key to delete.
637  *      @intrans: If we're already in a transaction.
638  *
639  *      This function deletes a public key from whatever storage mechanism we
640  *      are using. Returns 0 if the key existed.
641  */
642 static int db4_delete_key(struct onak_dbctx *dbctx,
643                 struct openpgp_fingerprint *fp,
644                 bool intrans)
645 {
646         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
647         struct openpgp_publickey *publickey = NULL;
648         DBT key, data;
649         DBC *cursor = NULL;
650         DBC *cursor64 = NULL;
651         uint32_t shortkeyid = 0;
652         uint64_t subkeyid = 0;
653         struct openpgp_fingerprint *subkeyids = NULL;
654         int ret = 0;
655         int i;
656         char **uids = NULL;
657         char *primary = NULL;
658         struct ll *wordlist = NULL;
659         struct ll *curword  = NULL;
660         bool deadlock = false;
661         struct skshash hash;
662         uint64_t keyid;
663
664         if (!intrans) {
665                 db4_starttrans(dbctx);
666         }
667
668         if (db4_fetch_key_fp(dbctx, fp, &publickey, true) == 0) {
669                 if (!intrans) {
670                         db4_endtrans(dbctx);
671                 }
672                 return 1;
673         }
674
675         if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
676                 return 1;
677         }
678
679         /*
680          * Walk through the uids removing the words from the worddb.
681          */
682         if (publickey != NULL) {
683                 uids = keyuids(publickey, &primary);
684         }
685         if (uids != NULL) {
686                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
687                         wordlist = makewordlist(wordlist, uids[i]);
688                 }
689
690                 privctx->worddb->cursor(privctx->worddb,
691                         privctx->txn,
692                         &cursor,
693                         0);   /* flags */
694
695                 for (curword = wordlist; curword != NULL && !deadlock;
696                                 curword = curword->next) {
697                         /*
698                          * New style uses the fingerprint as the data
699                          * Old (unsupported) style was the 64 bit keyid
700                          */
701                         memset(&key, 0, sizeof(key));
702                         memset(&data, 0, sizeof(data));
703                         key.data = curword->object;
704                         key.size = strlen(key.data);
705                         data.data = fp->fp;
706                         data.size = fp->length;
707
708                         ret = cursor->c_get(cursor,
709                                 &key,
710                                 &data,
711                                 DB_GET_BOTH);
712
713                         if (ret == 0) {
714                                 ret = cursor->c_del(cursor, 0);
715                         }
716
717                         if (ret != 0 && ret != DB_NOTFOUND) {
718                                 logthing(LOGTHING_ERROR,
719                                         "Problem deleting word: %s "
720                                         "(0x%016" PRIX64 ")",
721                                         db_strerror(ret),
722                                         keyid);
723                                 if (ret == DB_LOCK_DEADLOCK) {
724                                         deadlock = true;
725                                 }
726                         }
727                 }
728                 cursor->c_close(cursor);
729                 cursor = NULL;
730
731                 /*
732                  * Free our UID and word lists.
733                  */
734                 llfree(wordlist, NULL);
735                 for (i = 0; uids[i] != NULL; i++) {
736                         free(uids[i]);
737                         uids[i] = NULL;
738                 }
739                 free(uids);
740                 uids = NULL;
741         }
742
743         if (!deadlock) {
744                 privctx->id32db->cursor(privctx->id32db,
745                         privctx->txn,
746                         &cursor,
747                         0);   /* flags */
748                 privctx->id64db->cursor(privctx->id64db,
749                         privctx->txn,
750                         &cursor64,
751                         0);   /* flags */
752
753                 /* 32 bit short key mapping to fingerprint */
754                 shortkeyid = keyid & 0xFFFFFFFF;
755
756                 memset(&key, 0, sizeof(key));
757                 memset(&data, 0, sizeof(data));
758                 key.data = &shortkeyid;
759                 key.size = sizeof(shortkeyid);
760                 data.data = fp->fp;
761                 data.size = fp->length;
762
763                 ret = cursor->c_get(cursor,
764                         &key,
765                         &data,
766                         DB_GET_BOTH);
767
768                 if (ret == 0) {
769                         ret = cursor->c_del(cursor, 0);
770                 }
771
772                 if (ret != 0 && ret != DB_NOTFOUND) {
773                         logthing(LOGTHING_ERROR,
774                                 "Problem deleting short keyid: %s "
775                                 "(0x%016" PRIX64 ")",
776                                 db_strerror(ret),
777                                 keyid);
778                         if (ret == DB_LOCK_DEADLOCK) {
779                                 deadlock = true;
780                         }
781                 }
782
783                 /* 64 bit key mapping to fingerprint */
784                 memset(&key, 0, sizeof(key));
785                 memset(&data, 0, sizeof(data));
786                 key.data = &keyid;
787                 key.size = sizeof(keyid);
788                 data.data = fp->fp;
789                 data.size = fp->length;
790
791                 ret = cursor64->c_get(cursor64,
792                         &key,
793                         &data,
794                         DB_GET_BOTH);
795
796                 if (ret == 0) {
797                         ret = cursor64->c_del(cursor64, 0);
798                 }
799
800                 if (ret != 0 && ret != DB_NOTFOUND) {
801                         logthing(LOGTHING_ERROR,
802                                 "Problem deleting keyid: %s "
803                                 "(0x%016" PRIX64 ")",
804                                 db_strerror(ret),
805                                 keyid);
806                         if (ret == DB_LOCK_DEADLOCK) {
807                                 deadlock = true;
808                         }
809                 }
810
811                 subkeyids = keysubkeys(publickey);
812                 i = 0;
813                 while (subkeyids != NULL && subkeyids[i].length != 0) {
814                         subkeyid = fingerprint2keyid(&subkeyids[i]);
815                         memset(&key, 0, sizeof(key));
816                         key.data = subkeyids[i].fp;
817                         key.size = subkeyids[i].length;
818                         ret = privctx->subkeydb->del(privctx->subkeydb,
819                                         privctx->txn, &key, 0);
820                         if (ret != 0 && ret != DB_NOTFOUND) {
821                                 logthing(LOGTHING_ERROR,
822                                         "Problem deleting subkey id: %s "
823                                         "(0x%016" PRIX64 ")",
824                                         db_strerror(ret),
825                                         keyid);
826                                 if (ret == DB_LOCK_DEADLOCK) {
827                                         deadlock = true;
828                                 }
829                         }
830
831                         shortkeyid = subkeyid & 0xFFFFFFFF;
832
833                         /* Remove 32 bit keyid -> fingerprint mapping */
834                         memset(&key, 0, sizeof(key));
835                         memset(&data, 0, sizeof(data));
836                         key.data = &shortkeyid;
837                         key.size = sizeof(shortkeyid);
838                         data.data = fp->fp;
839                         data.size = fp->length;
840
841                         ret = cursor->c_get(cursor,
842                                 &key,
843                                 &data,
844                                 DB_GET_BOTH);
845
846                         if (ret == 0) {
847                                 ret = cursor->c_del(cursor, 0);
848                         }
849
850                         if (ret != 0 && ret != DB_NOTFOUND) {
851                                 logthing(LOGTHING_ERROR,
852                                         "Problem deleting short keyid: %s "
853                                         "(0x%016" PRIX64 ")",
854                                         db_strerror(ret),
855                                         keyid);
856                                 if (ret == DB_LOCK_DEADLOCK) {
857                                         deadlock = true;
858                                 }
859                         }
860
861                         /* Remove 64 bit keyid -> fingerprint mapping */
862                         memset(&key, 0, sizeof(key));
863                         memset(&data, 0, sizeof(data));
864                         key.data = &subkeyid;
865                         key.size = sizeof(subkeyid);
866                         data.data = fp->fp;
867                         data.size = fp->length;
868
869                         ret = cursor64->c_get(cursor64,
870                                 &key,
871                                 &data,
872                                 DB_GET_BOTH);
873
874                         if (ret == 0) {
875                                 ret = cursor64->c_del(cursor64, 0);
876                         }
877
878                         if (ret != 0 && ret != DB_NOTFOUND) {
879                                 logthing(LOGTHING_ERROR,
880                                         "Problem deleting keyid: %s "
881                                         "(0x%016" PRIX64 ")",
882                                         db_strerror(ret),
883                                         keyid);
884                                 if (ret == DB_LOCK_DEADLOCK) {
885                                         deadlock = true;
886                                 }
887                         }
888                         i++;
889                 }
890                 if (subkeyids != NULL) {
891                         free(subkeyids);
892                         subkeyids = NULL;
893                 }
894                 cursor64->c_close(cursor64);
895                 cursor64 = NULL;
896                 cursor->c_close(cursor);
897                 cursor = NULL;
898         }
899
900         if (!deadlock) {
901                 ret = privctx->skshashdb->cursor(privctx->skshashdb,
902                         privctx->txn,
903                         &cursor,
904                         0);   /* flags */
905                 if (ret == 0) {
906                         get_skshash(publickey, &hash);
907
908                         /* Remove SKS hash -> fingerprint mapping */
909                         memset(&key, 0, sizeof(key));
910                         memset(&data, 0, sizeof(data));
911                         key.data = hash.hash;
912                         key.size = sizeof(hash.hash);
913                         data.data = fp->fp;
914                         data.size = fp->length;
915
916                         ret = cursor->c_get(cursor,
917                                 &key,
918                                 &data,
919                                 DB_GET_BOTH);
920
921                         if (ret == 0) {
922                                 ret = cursor->c_del(cursor, 0);
923                         }
924
925                         if (ret != 0 && ret != DB_NOTFOUND) {
926                                 logthing(LOGTHING_ERROR,
927                                         "Problem deleting skshash: %s "
928                                         "(0x%016" PRIX64 ")",
929                                         db_strerror(ret),
930                                         keyid);
931                                 if (ret == DB_LOCK_DEADLOCK) {
932                                         deadlock = true;
933                                 }
934                         }
935
936                         cursor->c_close(cursor);
937                         cursor = NULL;
938                 }
939         }
940         free_publickey(publickey);
941         publickey = NULL;
942
943         if (!deadlock) {
944                 key.data = fp->fp;
945                 key.size = fp->length;
946
947                 keydb_fp(privctx, fp)->del(keydb_fp(privctx, fp),
948                                 privctx->txn,
949                                 &key,
950                                 0); /* flags */
951         }
952
953         if (!intrans) {
954                 db4_endtrans(dbctx);
955         }
956
957         return deadlock ? (-1) : (ret == DB_NOTFOUND);
958 }
959
960 /**
961  *      store_key - Takes a key and stores it.
962  *      @publickey: A pointer to the public key to store.
963  *      @intrans: If we're already in a transaction.
964  *      @update: If true the key exists and should be updated.
965  *
966  *      Again we just use the hex representation of the keyid as the filename
967  *      to store the key to. We flatten the public key to a list of OpenPGP
968  *      packets and then use write_openpgp_stream() to write the stream out to
969  *      the file. If update is true then we delete the old key first, otherwise
970  *      we trust that it doesn't exist.
971  */
972 static int db4_store_key(struct onak_dbctx *dbctx,
973                 struct openpgp_publickey *publickey, bool intrans,
974                 bool update)
975 {
976         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
977         struct     openpgp_packet_list *packets = NULL;
978         struct     openpgp_packet_list *list_end = NULL;
979         struct     openpgp_publickey *next = NULL;
980         int        ret = 0;
981         int        i = 0;
982         struct     buffer_ctx storebuf;
983         DBT        key;
984         DBT        data;
985         uint64_t   keyid = 0;
986         uint32_t   shortkeyid = 0;
987         struct openpgp_fingerprint *subkeyids = NULL;
988         char     **uids = NULL;
989         char      *primary = NULL;
990         struct ll *wordlist = NULL;
991         struct ll *curword  = NULL;
992         bool       deadlock = false;
993         struct skshash hash;
994         struct openpgp_fingerprint fingerprint;
995
996         if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
997                 logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
998                 return 0;
999         }
1000
1001         if (get_fingerprint(publickey->publickey, &fingerprint) != ONAK_E_OK) {
1002                 logthing(LOGTHING_ERROR, "Couldn't find fingerprint for key.");
1003                 return 0;
1004         }
1005
1006         if (!intrans) {
1007                 db4_starttrans(dbctx);
1008         }
1009
1010         /*
1011          * Delete the key if we already have it.
1012          *
1013          * TODO: Can we optimize this perhaps? Possibly when other data is
1014          * involved as well? I suspect this is easiest and doesn't make a lot
1015          * of difference though - the largest chunk of data is the keydata and
1016          * it definitely needs updated.
1017          */
1018         if (update) {
1019                 deadlock = (db4_delete_key(dbctx, &fingerprint, true) == -1);
1020         }
1021
1022         /*
1023          * Convert the key to a flat set of binary data.
1024          */
1025         if (!deadlock) {
1026                 next = publickey->next;
1027                 publickey->next = NULL;
1028                 flatten_publickey(publickey, &packets, &list_end);
1029                 publickey->next = next;
1030
1031                 storebuf.offset = 0;
1032                 storebuf.size = 8192;
1033                 storebuf.buffer = malloc(8192);
1034
1035                 write_openpgp_stream(buffer_putchar, &storebuf, packets);
1036
1037                 /*
1038                  * Now we have the key data store it in the DB; the fingerprint
1039                  * is the key.
1040                  */
1041                 memset(&key, 0, sizeof(key));
1042                 memset(&data, 0, sizeof(data));
1043                 key.data = fingerprint.fp;
1044                 key.size = fingerprint.length;
1045                 data.size = storebuf.offset;
1046                 data.data = storebuf.buffer;
1047
1048                 ret = keydb_fp(privctx, &fingerprint)->put(
1049                                 keydb_fp(privctx, &fingerprint),
1050                                 privctx->txn,
1051                                 &key,
1052                                 &data,
1053                                 0); /* flags*/
1054                 if (ret != 0) {
1055                         logthing(LOGTHING_ERROR,
1056                                         "Problem storing key: %s",
1057                                         db_strerror(ret));
1058                         if (ret == DB_LOCK_DEADLOCK) {
1059                                 deadlock = true;
1060                         }
1061                 }
1062
1063                 free(storebuf.buffer);
1064                 storebuf.buffer = NULL;
1065                 storebuf.size = 0;
1066                 storebuf.offset = 0;
1067
1068                 free_packet_list(packets);
1069                 packets = NULL;
1070         }
1071
1072         /*
1073          * Walk through our uids storing the words into the db with the
1074          * fingerprint.
1075          */
1076         if (!deadlock) {
1077                 uids = keyuids(publickey, &primary);
1078         }
1079         if (uids != NULL) {
1080                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
1081                         wordlist = makewordlist(wordlist, uids[i]);
1082                 }
1083
1084                 for (curword = wordlist; curword != NULL && !deadlock;
1085                                 curword = curword->next) {
1086                         memset(&key, 0, sizeof(key));
1087                         memset(&data, 0, sizeof(data));
1088                         key.data = curword->object;
1089                         key.size = strlen(key.data);
1090                         data.data = fingerprint.fp;
1091                         data.size = fingerprint.length;
1092
1093                         ret = privctx->worddb->put(privctx->worddb,
1094                                 privctx->txn,
1095                                 &key,
1096                                 &data,
1097                                 0);
1098                         if (ret != 0) {
1099                                 logthing(LOGTHING_ERROR,
1100                                         "Problem storing word: %s",
1101                                         db_strerror(ret));
1102                                 if (ret == DB_LOCK_DEADLOCK) {
1103                                         deadlock = true;
1104                                 }
1105                         }
1106                 }
1107
1108                 /*
1109                  * Free our UID and word lists.
1110                  */
1111                 llfree(wordlist, NULL);
1112                 for (i = 0; uids[i] != NULL; i++) {
1113                         free(uids[i]);
1114                         uids[i] = NULL;
1115                 }
1116                 free(uids);
1117                 uids = NULL;
1118         }
1119
1120         /*
1121          * Write the truncated 32 bit keyid so we can lookup the fingerprint
1122          * for queries.
1123          */
1124         if (!deadlock) {
1125                 shortkeyid = keyid & 0xFFFFFFFF;
1126
1127                 memset(&key, 0, sizeof(key));
1128                 memset(&data, 0, sizeof(data));
1129                 key.data = &shortkeyid;
1130                 key.size = sizeof(shortkeyid);
1131                 data.data = fingerprint.fp;
1132                 data.size = fingerprint.length;
1133
1134                 ret = privctx->id32db->put(privctx->id32db,
1135                         privctx->txn,
1136                         &key,
1137                         &data,
1138                         0);
1139                 if (ret != 0) {
1140                         logthing(LOGTHING_ERROR,
1141                                 "Problem storing short keyid: %s",
1142                                 db_strerror(ret));
1143                         if (ret == DB_LOCK_DEADLOCK) {
1144                                 deadlock = true;
1145                         }
1146                 }
1147         }
1148
1149         /*
1150          * Write the 64 bit keyid so we can lookup the fingerprint for
1151          * queries.
1152          */
1153         if (!deadlock) {
1154                 memset(&key, 0, sizeof(key));
1155                 memset(&data, 0, sizeof(data));
1156                 key.data = &keyid;
1157                 key.size = sizeof(keyid);
1158                 data.data = fingerprint.fp;
1159                 data.size = fingerprint.length;
1160
1161                 ret = privctx->id64db->put(privctx->id64db,
1162                         privctx->txn,
1163                         &key,
1164                         &data,
1165                         0);
1166                 if (ret != 0) {
1167                         logthing(LOGTHING_ERROR,
1168                                 "Problem storing keyid: %s",
1169                                 db_strerror(ret));
1170                         if (ret == DB_LOCK_DEADLOCK) {
1171                                 deadlock = true;
1172                         }
1173                 }
1174         }
1175
1176         if (!deadlock) {
1177                 subkeyids = keysubkeys(publickey);
1178                 i = 0;
1179                 while (subkeyids != NULL && subkeyids[i].length != 0) {
1180                         /* Store the subkey ID -> main key fp mapping */
1181                         memset(&key, 0, sizeof(key));
1182                         memset(&data, 0, sizeof(data));
1183                         key.data = subkeyids[i].fp;
1184                         key.size = subkeyids[i].length;
1185                         data.data = fingerprint.fp;
1186                         data.size = fingerprint.length;
1187
1188                         ret = privctx->subkeydb->put(privctx->subkeydb,
1189                                 privctx->txn,
1190                                 &key,
1191                                 &data,
1192                                 0);
1193                         if (ret != 0) {
1194                                 logthing(LOGTHING_ERROR,
1195                                         "Problem storing subkey keyid: %s",
1196                                         db_strerror(ret));
1197                                 if (ret == DB_LOCK_DEADLOCK) {
1198                                         deadlock = true;
1199                                 }
1200                         }
1201
1202                         /* Store the 64 bit subkey ID -> main key fp mapping */
1203                         memset(&key, 0, sizeof(key));
1204                         memset(&data, 0, sizeof(data));
1205
1206                         keyid = fingerprint2keyid(&subkeyids[i]);
1207                         key.data = &keyid;
1208                         key.size = sizeof(keyid);
1209                         data.data = fingerprint.fp;
1210                         data.size = fingerprint.length;
1211
1212                         ret = privctx->id64db->put(privctx->id64db,
1213                                 privctx->txn,
1214                                 &key,
1215                                 &data,
1216                                 0);
1217                         if (ret != 0) {
1218                                 logthing(LOGTHING_ERROR,
1219                                         "Problem storing keyid: %s",
1220                                         db_strerror(ret));
1221                                 if (ret == DB_LOCK_DEADLOCK) {
1222                                         deadlock = true;
1223                                 }
1224                         }
1225
1226                         /* Store the short subkey ID -> main key fp mapping */
1227                         shortkeyid = keyid & 0xFFFFFFFF;
1228
1229                         memset(&key, 0, sizeof(key));
1230                         memset(&data, 0, sizeof(data));
1231                         key.data = &shortkeyid;
1232                         key.size = sizeof(shortkeyid);
1233                         data.data = fingerprint.fp;
1234                         data.size = fingerprint.length;
1235
1236                         ret = privctx->id32db->put(privctx->id32db,
1237                                 privctx->txn,
1238                                 &key,
1239                                 &data,
1240                                 0);
1241                         if (ret != 0) {
1242                                 logthing(LOGTHING_ERROR,
1243                                         "Problem storing short keyid: %s",
1244                                         db_strerror(ret));
1245                                 if (ret == DB_LOCK_DEADLOCK) {
1246                                         deadlock = true;
1247                                 }
1248                         }
1249                         i++;
1250                 }
1251                 if (subkeyids != NULL) {
1252                         free(subkeyids);
1253                         subkeyids = NULL;
1254                 }
1255         }
1256
1257         if (!deadlock) {
1258                 get_skshash(publickey, &hash);
1259                 memset(&key, 0, sizeof(key));
1260                 memset(&data, 0, sizeof(data));
1261                 key.data = hash.hash;
1262                 key.size = sizeof(hash.hash);
1263                 data.data = fingerprint.fp;
1264                 data.size = fingerprint.length;
1265
1266                 ret = privctx->skshashdb->put(privctx->skshashdb,
1267                         privctx->txn,
1268                         &key,
1269                         &data,
1270                         0);
1271                 if (ret != 0) {
1272                         logthing(LOGTHING_ERROR,
1273                                 "Problem storing SKS hash: %s",
1274                                 db_strerror(ret));
1275                         if (ret == DB_LOCK_DEADLOCK) {
1276                                 deadlock = true;
1277                         }
1278                 }
1279         }
1280
1281         if (!intrans) {
1282                 db4_endtrans(dbctx);
1283         }
1284
1285         return deadlock ? -1 : 0 ;
1286 }
1287
1288 /**
1289  *      iterate_keys - call a function once for each key in the db.
1290  *      @iterfunc: The function to call.
1291  *      @ctx: A context pointer
1292  *
1293  *      Calls iterfunc once for each key in the database. ctx is passed
1294  *      unaltered to iterfunc. This function is intended to aid database dumps
1295  *      and statistic calculations.
1296  *
1297  *      Returns the number of keys we iterated over.
1298  */
1299 static int db4_iterate_keys(struct onak_dbctx *dbctx,
1300                 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
1301                 void *ctx)
1302 {
1303         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
1304         DBT                         dbkey, data;
1305         DBC                        *cursor = NULL;
1306         int                         ret = 0;
1307         int                         i = 0;
1308         int                         numkeys = 0;
1309         struct buffer_ctx           fetchbuf;
1310         struct openpgp_packet_list *packets = NULL;
1311         struct openpgp_publickey   *key = NULL;
1312
1313         for (i = 0; i < privctx->numdbs; i++) {
1314                 ret = privctx->dbconns[i]->cursor(privctx->dbconns[i],
1315                         NULL,
1316                         &cursor,
1317                         0);   /* flags */
1318
1319                 if (ret != 0) {
1320                         continue;
1321                 }
1322
1323                 memset(&dbkey, 0, sizeof(dbkey));
1324                 memset(&data, 0, sizeof(data));
1325                 ret = cursor->c_get(cursor, &dbkey, &data, DB_NEXT);
1326                 while (ret == 0) {
1327                         fetchbuf.buffer = data.data;
1328                         fetchbuf.offset = 0;
1329                         fetchbuf.size = data.size;
1330                         read_openpgp_stream(buffer_fetchchar, &fetchbuf,
1331                                 &packets, 0);
1332                         parse_keys(packets, &key);
1333
1334                         iterfunc(ctx, key);
1335
1336                         free_publickey(key);
1337                         key = NULL;
1338                         free_packet_list(packets);
1339                         packets = NULL;
1340
1341                         memset(&dbkey, 0, sizeof(dbkey));
1342                         memset(&data, 0, sizeof(data));
1343                         ret = cursor->c_get(cursor, &dbkey, &data,
1344                                         DB_NEXT);
1345                         numkeys++;
1346                 }
1347                 if (ret != DB_NOTFOUND) {
1348                         logthing(LOGTHING_ERROR,
1349                                 "Problem reading key: %s",
1350                                 db_strerror(ret));
1351                 }
1352
1353                 cursor->c_close(cursor);
1354                 cursor = NULL;
1355         }
1356
1357         return numkeys;
1358 }
1359
1360 /*
1361  * Include the basic keydb routines.
1362  */
1363 #define NEED_GETKEYSIGS 1
1364 #define NEED_KEYID2UID 1
1365 #define NEED_UPDATEKEYS 1
1366 #include "keydb.c"
1367
1368 /**
1369  *      cleanupdb - De-initialize the key database.
1370  *
1371  *      This function should be called upon program exit to allow the DB to
1372  *      cleanup after itself.
1373  */
1374 static void db4_cleanupdb(struct onak_dbctx *dbctx)
1375 {
1376         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
1377         int i = 0;
1378
1379         if (privctx->dbenv != NULL) {
1380                 privctx->dbenv->txn_checkpoint(privctx->dbenv, 0, 0, 0);
1381                 if (privctx->subkeydb != NULL) {
1382                         privctx->subkeydb->close(privctx->subkeydb, 0);
1383                         privctx->subkeydb = NULL;
1384                 }
1385                 if (privctx->skshashdb != NULL) {
1386                         privctx->skshashdb->close(privctx->skshashdb, 0);
1387                         privctx->skshashdb = NULL;
1388                 }
1389                 if (privctx->id64db != NULL) {
1390                         privctx->id64db->close(privctx->id64db, 0);
1391                         privctx->id64db = NULL;
1392                 }
1393                 if (privctx->id32db != NULL) {
1394                         privctx->id32db->close(privctx->id32db, 0);
1395                         privctx->id32db = NULL;
1396                 }
1397                 if (privctx->worddb != NULL) {
1398                         privctx->worddb->close(privctx->worddb, 0);
1399                         privctx->worddb = NULL;
1400                 }
1401                 for (i = 0; i < privctx->numdbs; i++) {
1402                         if (privctx->dbconns[i] != NULL) {
1403                                 privctx->dbconns[i]->close(privctx->dbconns[i],
1404                                                 0);
1405                                 privctx->dbconns[i] = NULL;
1406                         }
1407                 }
1408                 free(privctx->dbconns);
1409                 privctx->dbconns = NULL;
1410                 privctx->dbenv->close(privctx->dbenv, 0);
1411                 privctx->dbenv = NULL;
1412         }
1413
1414         free(privctx);
1415         dbctx->priv = NULL;
1416         free(dbctx);
1417 }
1418
1419 /**
1420  *      initdb - Initialize the key database.
1421  *
1422  *      This function should be called before any of the other functions in
1423  *      this file are called in order to allow the DB to be initialized ready
1424  *      for access.
1425  */
1426 struct onak_dbctx *keydb_db4_init(struct onak_db_config *dbcfg, bool readonly)
1427 {
1428         char       buf[1024];
1429         FILE      *numdb = NULL;
1430         int        ret = 0;
1431         int        i = 0;
1432         uint32_t   flags = 0;
1433         struct stat statbuf;
1434         int        maxlocks;
1435         struct onak_dbctx *dbctx;
1436         struct onak_db4_dbctx *privctx;
1437
1438         dbctx = malloc(sizeof(*dbctx));
1439         if (dbctx == NULL) {
1440                 return NULL;
1441         }
1442         dbctx->config = dbcfg;
1443         dbctx->priv = privctx = calloc(1, sizeof(*privctx));
1444         if (privctx == NULL) {
1445                 free(dbctx);
1446                 return NULL;
1447         }
1448
1449         /* Default to 16 key data DBs */
1450         privctx->numdbs = 16;
1451
1452         snprintf(buf, sizeof(buf) - 1, "%s/%s", dbcfg->location,
1453                         DB4_UPGRADE_FILE);
1454         ret = stat(buf, &statbuf);
1455         while ((ret == 0) || (errno != ENOENT)) {
1456                 if (ret != 0) {
1457                         logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade "
1458                                 "lock file: %s (%d)", strerror(errno), ret);
1459                         exit(1);
1460                 }
1461                 logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting.");
1462                 sleep(5);
1463                 ret = stat(buf, &statbuf);
1464         }
1465         ret = 0;
1466
1467         snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", dbcfg->location);
1468         numdb = fopen(buf, "r");
1469         if (numdb != NULL) {
1470                 if (fgets(buf, sizeof(buf), numdb) != NULL) {
1471                         privctx->numdbs = atoi(buf);
1472                 }
1473                 fclose(numdb);
1474         } else if (!readonly) {
1475                 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
1476                                 strerror(errno));
1477                 numdb = fopen(buf, "w");
1478                 if (numdb != NULL) {
1479                         fprintf(numdb, "%d", privctx->numdbs);
1480                         fclose(numdb);
1481                 } else {
1482                         logthing(LOGTHING_ERROR,
1483                                 "Couldn't write num_keydb: %s",
1484                                 strerror(errno));
1485                 }
1486         }
1487
1488         privctx->dbconns = calloc(privctx->numdbs, sizeof (DB *));
1489         if (privctx->dbconns == NULL) {
1490                 logthing(LOGTHING_CRITICAL,
1491                                 "Couldn't allocate memory for dbconns");
1492                 ret = 1;
1493         }
1494
1495         if (ret == 0) {
1496                 ret = db_env_create(&privctx->dbenv, 0);
1497                 if (ret != 0) {
1498                         logthing(LOGTHING_CRITICAL,
1499                                 "db_env_create: %s", db_strerror(ret));
1500                 }
1501         }
1502
1503         /*
1504          * Up the number of locks we're allowed at once. We base this on
1505          * the maximum number of keys we're going to return.
1506          */
1507         if (ret == 0) {
1508                 maxlocks = config.maxkeys * 16;
1509                 if (maxlocks < 1000) {
1510                         maxlocks = 1000;
1511                 }
1512                 privctx->dbenv->set_lk_max_locks(privctx->dbenv, maxlocks);
1513                 privctx->dbenv->set_lk_max_objects(privctx->dbenv, maxlocks);
1514         }
1515
1516         /*
1517          * Enable deadlock detection so that we don't block indefinitely on
1518          * anything. What we really want is simple 2 state locks, but I'm not
1519          * sure how to make the standard DB functions do that yet.
1520          */
1521         if (ret == 0) {
1522                 privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
1523                 ret = privctx->dbenv->set_lk_detect(privctx->dbenv, DB_LOCK_DEFAULT);
1524                 if (ret != 0) {
1525                         logthing(LOGTHING_CRITICAL,
1526                                 "db_env_create: %s", db_strerror(ret));
1527                 }
1528         }
1529
1530         if (ret == 0) {
1531                 ret = privctx->dbenv->open(privctx->dbenv, dbcfg->location,
1532                                 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
1533                                 DB_INIT_TXN |
1534                                 DB_CREATE,
1535                                 0);
1536 #ifdef DB_VERSION_MISMATCH
1537                 if (ret == DB_VERSION_MISMATCH) {
1538                         privctx->dbenv->close(privctx->dbenv, 0);
1539                         privctx->dbenv = NULL;
1540                         ret = db4_upgradedb(dbctx);
1541                         if (ret == 0) {
1542                                 ret = db_env_create(&privctx->dbenv, 0);
1543                         }
1544                         if (ret == 0) {
1545                                 privctx->dbenv->set_errcall(privctx->dbenv,
1546                                         &db4_errfunc);
1547                                 privctx->dbenv->set_lk_detect(privctx->dbenv,
1548                                         DB_LOCK_DEFAULT);
1549                                 ret = privctx->dbenv->open(privctx->dbenv,
1550                                         dbcfg->location,
1551                                         DB_INIT_LOG | DB_INIT_MPOOL |
1552                                         DB_INIT_LOCK | DB_INIT_TXN |
1553                                         DB_CREATE | DB_RECOVER,
1554                                         0);
1555
1556                                 if (ret == 0) {
1557                                         privctx->dbenv->txn_checkpoint(
1558                                                         privctx->dbenv,
1559                                                         0,
1560                                                         0,
1561                                                         DB_FORCE);
1562                                 }
1563                         }
1564                 }
1565 #endif
1566                 if (ret != 0) {
1567                         logthing(LOGTHING_CRITICAL,
1568                                         "Error opening db environment: %s (%s)",
1569                                         dbcfg->location,
1570                                         db_strerror(ret));
1571                         if (privctx->dbenv != NULL) {
1572                                 privctx->dbenv->close(privctx->dbenv, 0);
1573                                 privctx->dbenv = NULL;
1574                         }
1575                 }
1576         }
1577
1578         if (ret == 0) {
1579                 db4_starttrans(dbctx);
1580
1581                 for (i = 0; !ret && i < privctx->numdbs; i++) {
1582                         ret = db_create(&privctx->dbconns[i],
1583                                         privctx->dbenv, 0);
1584                         if (ret != 0) {
1585                                 logthing(LOGTHING_CRITICAL,
1586                                         "db_create: %s", db_strerror(ret));
1587                         }
1588
1589                         if (ret == 0) {
1590                                 snprintf(buf, 1023, "keydb.%d.db", i);
1591                                 flags = DB_CREATE;
1592                                 if (readonly) {
1593                                         flags = DB_RDONLY;
1594                                 }
1595                                 ret = privctx->dbconns[i]->open(
1596                                                 privctx->dbconns[i],
1597                                                 privctx->txn,
1598                                                 buf,
1599                                                 "keydb",
1600                                                 DB_HASH,
1601                                                 flags,
1602                                                 0664);
1603                                 if (ret != 0) {
1604                                         logthing(LOGTHING_CRITICAL,
1605                                                 "Error opening key database:"
1606                                                 " %s (%s)",
1607                                                 buf,
1608                                                 db_strerror(ret));
1609                                 }
1610                         }
1611                 }
1612         }
1613
1614         if (ret == 0) {
1615                 ret = db_create(&privctx->worddb, privctx->dbenv, 0);
1616                 if (ret != 0) {
1617                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1618                                         db_strerror(ret));
1619                 }
1620         }
1621
1622         if (ret == 0) {
1623                 ret = privctx->worddb->set_flags(privctx->worddb, DB_DUP);
1624         }
1625
1626         if (ret == 0) {
1627                 ret = privctx->worddb->open(privctx->worddb, privctx->txn,
1628                                 "worddb", "worddb", DB_BTREE,
1629                                 flags,
1630                                 0664);
1631                 if (ret != 0) {
1632                         logthing(LOGTHING_CRITICAL,
1633                                         "Error opening word database: %s (%s)",
1634                                         "worddb",
1635                                         db_strerror(ret));
1636                 }
1637         }
1638
1639         if (ret == 0) {
1640                 ret = db_create(&privctx->id32db, privctx->dbenv, 0);
1641                 if (ret != 0) {
1642                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1643                                         db_strerror(ret));
1644                 }
1645         }
1646
1647         if (ret == 0) {
1648                 ret = privctx->id32db->set_flags(privctx->id32db, DB_DUP);
1649         }
1650
1651         if (ret == 0) {
1652                 ret = privctx->id32db->open(privctx->id32db, privctx->txn,
1653                                 "id32db", "id32db", DB_HASH,
1654                                 flags,
1655                                 0664);
1656                 if (ret != 0) {
1657                         logthing(LOGTHING_CRITICAL,
1658                                         "Error opening id32 database: %s (%s)",
1659                                         "id32db",
1660                                         db_strerror(ret));
1661                 }
1662         }
1663
1664         if (ret == 0) {
1665                 ret = db_create(&privctx->id64db, privctx->dbenv, 0);
1666                 if (ret != 0) {
1667                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1668                                         db_strerror(ret));
1669                 }
1670         }
1671
1672         if (ret == 0) {
1673                 ret = privctx->id64db->set_flags(privctx->id64db, DB_DUP);
1674         }
1675
1676         if (ret == 0) {
1677                 ret = privctx->id64db->open(privctx->id64db, privctx->txn,
1678                                 "id64db", "id64db", DB_HASH,
1679                                 flags,
1680                                 0664);
1681                 if (ret != 0) {
1682                         logthing(LOGTHING_CRITICAL,
1683                                         "Error opening id64 database: %s (%s)",
1684                                         "id64db",
1685                                         db_strerror(ret));
1686                 }
1687         }
1688
1689         if (ret == 0) {
1690                 ret = db_create(&privctx->skshashdb, privctx->dbenv, 0);
1691                 if (ret != 0) {
1692                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1693                                         db_strerror(ret));
1694                 }
1695         }
1696
1697         if (ret == 0) {
1698                 ret = privctx->skshashdb->open(privctx->skshashdb, privctx->txn,
1699                                 "skshashdb",
1700                                 "skshashdb", DB_HASH,
1701                                 flags,
1702                                 0664);
1703                 if (ret != 0) {
1704                         logthing(LOGTHING_CRITICAL,
1705                                 "Error opening skshash database: %s (%s)",
1706                                 "skshashdb",
1707                                 db_strerror(ret));
1708                 }
1709         }
1710
1711         if (ret == 0) {
1712                 ret = db_create(&privctx->subkeydb, privctx->dbenv, 0);
1713                 if (ret != 0) {
1714                         logthing(LOGTHING_CRITICAL, "db_create: %s",
1715                                         db_strerror(ret));
1716                 }
1717         }
1718
1719         if (ret == 0) {
1720                 ret = privctx->subkeydb->open(privctx->subkeydb, privctx->txn,
1721                                 "subkeydb", "subkeydb",
1722                                 DB_HASH,
1723                                 flags,
1724                                 0664);
1725                 if (ret != 0) {
1726                         logthing(LOGTHING_CRITICAL,
1727                                 "Error opening subkey database: %s (%s)",
1728                                 "subkeydb",
1729                                 db_strerror(ret));
1730                 }
1731         }
1732
1733         if (privctx->txn != NULL) {
1734                 db4_endtrans(dbctx);
1735         }
1736
1737         if (ret != 0) {
1738                 db4_cleanupdb(dbctx);
1739                 logthing(LOGTHING_CRITICAL,
1740                                 "Error opening database; exiting");
1741                 exit(EXIT_FAILURE);
1742         }
1743
1744         dbctx->cleanupdb                = db4_cleanupdb;
1745         dbctx->starttrans               = db4_starttrans;
1746         dbctx->endtrans                 = db4_endtrans;
1747         dbctx->fetch_key_id             = db4_fetch_key_id;
1748         dbctx->fetch_key_fp             = db4_fetch_key_fp;
1749         dbctx->fetch_key_text           = db4_fetch_key_text;
1750         dbctx->fetch_key_skshash        = db4_fetch_key_skshash;
1751         dbctx->store_key                = db4_store_key;
1752         dbctx->update_keys              = generic_update_keys;
1753         dbctx->delete_key               = db4_delete_key;
1754         dbctx->getkeysigs               = generic_getkeysigs;
1755         dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
1756         dbctx->keyid2uid                = generic_keyid2uid;
1757         dbctx->iterate_keys             = db4_iterate_keys;
1758
1759         return dbctx;
1760 }