]> the.earth.li Git - onak.git/blob - keydb/keydb_db4.c
1f306e267f60e9851c0dd65b0874517ba7afcb39
[onak.git] / keydb / 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         DBT key, data;
407         DBC *cursor = NULL;
408         int ret = 0;
409         int numkeys = 0;
410         uint32_t  shortkeyid = 0;
411         struct openpgp_fingerprint fingerprint;
412         bool first;
413
414         if (!intrans) {
415                 db4_starttrans(dbctx);
416         }
417
418         /* If the key ID fits in 32 bits assume it's a short key id */
419         if (keyid < 0x100000000LL) {
420                 ret = privctx->id32db->cursor(privctx->id32db,
421                                 privctx->txn,
422                                 &cursor,
423                                 0);   /* flags */
424
425                 shortkeyid = keyid & 0xFFFFFFFF;
426                 memset(&key, 0, sizeof(key));
427                 memset(&data, 0, sizeof(data));
428                 key.data = &shortkeyid;
429                 key.size = sizeof(shortkeyid);
430         } else {
431                 ret = privctx->id64db->cursor(privctx->id64db,
432                                 privctx->txn,
433                                 &cursor,
434                                 0); /* flags*/
435
436                 memset(&key, 0, sizeof(key));
437                 memset(&data, 0, sizeof(data));
438                 key.data = &keyid;
439                 key.size = sizeof(keyid);
440         }
441
442         if (ret != 0) {
443                 return 0;
444         }
445
446         memset(&data, 0, sizeof(data));
447         data.ulen = MAX_FINGERPRINT_LEN;
448         data.data = fingerprint.fp;
449         data.flags = DB_DBT_USERMEM;
450
451         first = true;
452         while (cursor->c_get(cursor, &key, &data,
453                                 first ? DB_SET : DB_NEXT_DUP) == 0) {
454                 /* We got a match; retrieve the actual key */
455                 fingerprint.length = data.size;
456
457                 if (db4_fetch_key_fp(dbctx, &fingerprint,
458                                         publickey, true))
459                         numkeys++;
460
461                 memset(&data, 0, sizeof(data));
462                 data.ulen = MAX_FINGERPRINT_LEN;
463                 data.data = fingerprint.fp;
464                 data.flags = DB_DBT_USERMEM;
465                 first = false;
466         }
467         cursor->c_close(cursor);
468         cursor = NULL;
469
470         if (!intrans) {
471                 db4_endtrans(dbctx);
472         }
473
474         return (numkeys);
475 }
476
477 /**
478  *      fetch_key_text - Trys to find the keys that contain the supplied text.
479  *      @search: The text to search for.
480  *      @publickey: A pointer to a structure to return the key in.
481  *
482  *      This function searches for the supplied text and returns the keys that
483  *      contain it.
484  */
485 static int db4_fetch_key_text(struct onak_dbctx *dbctx, const char *search,
486                 struct openpgp_publickey **publickey)
487 {
488         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
489         DBC *cursor = NULL;
490         DBT key, data;
491         int ret;
492         int i;
493         int numkeys;
494         char *searchtext = NULL;
495         struct ll *wordlist = NULL;
496         struct ll *curword = NULL;
497         struct keyarray keylist = { NULL, 0, 0 };
498         struct keyarray newkeylist = { NULL, 0, 0 };
499         int firstpass = 1;
500         struct openpgp_fingerprint fingerprint;
501
502         numkeys = 0;
503         searchtext = strdup(search);
504         wordlist = makewordlist(wordlist, searchtext);
505
506         for (curword = wordlist; curword != NULL; curword = curword->next) {
507                 db4_starttrans(dbctx);
508
509                 ret = privctx->worddb->cursor(privctx->worddb,
510                                 privctx->txn,
511                                 &cursor,
512                                 0);   /* flags */
513
514                 if (ret != 0) {
515                         db4_endtrans(dbctx);
516                         break;
517                 }
518
519                 memset(&key, 0, sizeof(key));
520                 memset(&data, 0, sizeof(data));
521                 key.data = curword->object;
522                 key.size = strlen(curword->object);
523                 data.flags = DB_DBT_MALLOC;
524                 ret = cursor->c_get(cursor,
525                                 &key,
526                                 &data,
527                                 DB_SET);
528                 while (ret == 0 && strncmp(key.data, curword->object,
529                                         key.size) == 0 &&
530                                 ((char *) curword->object)[key.size] == 0) {
531
532                         fingerprint.length = data.size;
533                         memcpy(fingerprint.fp, data.data, data.size);
534
535                         /*
536                          * Only add the keys containing this word if this is
537                          * our first pass (ie we have no existing key list),
538                          * or the key contained a previous word.
539                          */
540                         if (firstpass || array_find(&keylist, &fingerprint)) {
541                                 array_add(&newkeylist, &fingerprint);
542                         }
543
544                         free(data.data);
545                         data.data = NULL;
546
547                         ret = cursor->c_get(cursor,
548                                         &key,
549                                         &data,
550                                         DB_NEXT);
551                 }
552                 array_free(&keylist);
553                 keylist.keys = newkeylist.keys;
554                 keylist.count = newkeylist.count;
555                 keylist.size = newkeylist.size;
556                 newkeylist.keys = NULL;
557                 newkeylist.count = newkeylist.size = 0;
558                 if (data.data != NULL) {
559                         free(data.data);
560                         data.data = NULL;
561                 }
562                 cursor->c_close(cursor);
563                 cursor = NULL;
564                 firstpass = 0;
565                 db4_endtrans(dbctx);
566         }
567         llfree(wordlist, NULL);
568         wordlist = NULL;
569
570         if (keylist.count > config.maxkeys) {
571                 keylist.count = config.maxkeys;
572         }
573
574         db4_starttrans(dbctx);
575         for (i = 0; i < keylist.count; i++) {
576                 numkeys += db4_fetch_key_fp(dbctx, &keylist.keys[i],
577                         publickey,
578                         true);
579         }
580         array_free(&keylist);
581         free(searchtext);
582         searchtext = NULL;
583
584         db4_endtrans(dbctx);
585
586         return (numkeys);
587 }
588
589 static int db4_fetch_key_skshash(struct onak_dbctx *dbctx,
590                 const struct skshash *hash,
591                 struct openpgp_publickey **publickey)
592 {
593         struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
594         DBT       key, data;
595         DBC      *cursor = NULL;
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 }