2 * keydb_db4.c - Routines to store and fetch keys in a DB4 database.
4 * Copyright 2002-2008 Jonathan McDowell <noodles@earth.li>
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.
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
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/>.
19 #include <sys/types.h>
34 #include "build-config.h"
35 #include "charfuncs.h"
39 #include "decodekey.h"
40 #include "keystructs.h"
45 #include "onak-conf.h"
49 #define DB4_UPGRADE_FILE "db_upgrade.lck"
51 struct onak_db4_dbctx {
52 DB_ENV *dbenv; /* The database environment context */
53 int numdbs; /* Number of data databases in use */
54 DB **dbconns; /* Connections to the key data databases */
55 DB *worddb; /* Connection to the word lookup database */
56 DB *id32db; /* Connection to the 32 bit ID lookup database */
57 DB *id64db; /* Connection to the 64 bit ID lookup database */
58 DB *skshashdb; /* Connection to the SKS hash database */
59 DB *subkeydb; /* Connection to the subkey ID lookup database */
60 DB_TXN *txn; /* Our current transaction ID */
63 DB *keydb_id(struct onak_db4_dbctx *privctx, uint64_t keyid)
69 return(privctx->dbconns[keytrun % privctx->numdbs]);
72 DB *keydb_fp(struct onak_db4_dbctx *privctx, struct openpgp_fingerprint *fp)
84 return(privctx->dbconns[keytrun % privctx->numdbs]);
88 * db4_errfunc - Direct DB errors to logfile
90 * Basic function to take errors from the DB library and output them to
91 * the logfile rather than stderr.
93 #if (DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR < 3)
94 static void db4_errfunc(const char *errpfx, const char *errmsg)
96 static void db4_errfunc(__unused const DB_ENV *edbenv, const char *errpfx,
101 logthing(LOGTHING_DEBUG, "db4 error: %s:%s", errpfx, errmsg);
103 logthing(LOGTHING_DEBUG, "db4 error: %s", errmsg);
110 * starttrans - Start a transaction.
112 * Start a transaction. Intended to be used if we're about to perform many
113 * operations on the database to help speed it all up, or if we want
114 * something to only succeed if all relevant operations are successful.
116 static bool db4_starttrans(struct onak_dbctx *dbctx)
118 struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
121 log_assert(privctx->dbenv != NULL);
122 log_assert(privctx->txn == NULL);
124 ret = privctx->dbenv->txn_begin(privctx->dbenv,
125 NULL, /* No parent transaction */
129 logthing(LOGTHING_CRITICAL,
130 "Error starting transaction: %s",
139 * endtrans - End a transaction.
141 * Ends a transaction.
143 static void db4_endtrans(struct onak_dbctx *dbctx)
145 struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
148 log_assert(privctx->dbenv != NULL);
149 log_assert(privctx->txn != NULL);
151 ret = privctx->txn->commit(privctx->txn,
154 logthing(LOGTHING_CRITICAL,
155 "Error ending transaction: %s",
165 * db4_upgradedb - Upgrade a DB4 database
167 * Called if we discover we need to upgrade our DB4 database; ie if
168 * we're running with a newer version of db4 than the database was
171 static int db4_upgradedb(struct onak_dbctx *dbctx)
173 struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
182 snprintf(buf, sizeof(buf) - 1, "%s/%s", dbctx->config->location,
184 lockfile_fd = open(buf, O_RDWR | O_CREAT | O_EXCL, 0600);
185 if (lockfile_fd < 0) {
186 if (errno == EEXIST) {
187 while (stat(buf, &statbuf) == 0) ;
190 logthing(LOGTHING_CRITICAL, "Couldn't open database "
191 "update lock file: %s", strerror(errno));
195 snprintf(buf, sizeof(buf) - 1, "%d", getpid());
196 written = write(lockfile_fd, buf, strlen(buf));
198 if (written != strlen(buf)) {
199 logthing(LOGTHING_CRITICAL, "Couldn't write PID to lockfile: "
200 "%s", strerror(errno));
201 snprintf(buf, sizeof(buf) - 1, "%s/%s", dbctx->config->location,
207 logthing(LOGTHING_NOTICE, "Upgrading DB4 database");
208 ret = db_env_create(&privctx->dbenv, 0);
210 privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
211 privctx->dbenv->remove(privctx->dbenv, dbctx->config->location, 0);
212 privctx->dbenv = NULL;
214 for (i = 0; i < privctx->numdbs; i++) {
215 ret = db_create(&curdb, NULL, 0);
217 snprintf(buf, sizeof(buf) - 1, "%s/keydb.%d.db",
218 dbctx->config->location, i);
219 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
220 curdb->upgrade(curdb, buf, 0);
221 curdb->close(curdb, 0);
223 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
229 ret = db_create(&curdb, NULL, 0);
231 snprintf(buf, sizeof(buf) - 1, "%s/worddb", dbctx->config->location);
232 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
233 curdb->upgrade(curdb, buf, 0);
234 curdb->close(curdb, 0);
236 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
241 ret = db_create(&curdb, NULL, 0);
243 snprintf(buf, sizeof(buf) - 1, "%s/id32db", dbctx->config->location);
244 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
245 curdb->upgrade(curdb, buf, 0);
246 curdb->close(curdb, 0);
248 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
253 ret = db_create(&curdb, NULL, 0);
255 snprintf(buf, sizeof(buf) - 1, "%s/id64db", dbctx->config->location);
256 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
257 curdb->upgrade(curdb, buf, 0);
258 curdb->close(curdb, 0);
260 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
265 ret = db_create(&curdb, NULL, 0);
267 snprintf(buf, sizeof(buf) - 1, "%s/skshashdb", dbctx->config->location);
268 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
269 curdb->upgrade(curdb, buf, 0);
270 curdb->close(curdb, 0);
272 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
277 ret = db_create(&curdb, NULL, 0);
279 snprintf(buf, sizeof(buf) - 1, "%s/subkeydb", dbctx->config->location);
280 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
281 curdb->upgrade(curdb, buf, 0);
282 curdb->close(curdb, 0);
284 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
289 snprintf(buf, sizeof(buf) - 1, "%s/%s", dbctx->config->location,
297 * fetch_key_fp - Given a fingerprint fetch the key from storage.
299 static int db4_fetch_key_int(struct onak_dbctx *dbctx,
300 struct openpgp_fingerprint *fingerprint,
301 struct openpgp_publickey **publickey,
305 struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
306 struct openpgp_packet_list *packets = NULL;
310 struct buffer_ctx fetchbuf;
311 struct openpgp_fingerprint subfp;
313 memset(&key, 0, sizeof(key));
314 memset(&data, 0, sizeof(data));
319 key.size = fingerprint->length;
320 key.data = fingerprint->fp;
323 db4_starttrans(dbctx);
326 ret = keydb_fp(privctx, fingerprint)->get(keydb_fp(privctx,
333 if (ret == DB_NOTFOUND && dosubkey) {
334 /* If we didn't find the key ID see if it's a subkey ID */
335 memset(&key, 0, sizeof(key));
336 memset(&data, 0, sizeof(data));
337 data.data = subfp.fp;
338 data.ulen = MAX_FINGERPRINT_LEN;
339 data.flags = DB_DBT_USERMEM;
340 key.data = fingerprint->fp;
341 key.size = fingerprint->length;
343 ret = privctx->subkeydb->get(privctx->subkeydb,
350 /* We got a subkey match; retrieve the actual key */
351 memset(&key, 0, sizeof(key));
352 key.size = subfp.length = data.size;
355 memset(&data, 0, sizeof(data));
359 ret = keydb_fp(privctx, &subfp)->get(
360 keydb_fp(privctx, &subfp),
369 fetchbuf.buffer = data.data;
371 fetchbuf.size = data.size;
372 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
374 parse_keys(packets, publickey);
375 free_packet_list(packets);
378 } else if (ret != DB_NOTFOUND) {
379 logthing(LOGTHING_ERROR,
380 "Problem retrieving key: %s",
391 static int db4_fetch_key(struct onak_dbctx *dbctx,
392 struct openpgp_fingerprint *fingerprint,
393 struct openpgp_publickey **publickey,
396 return db4_fetch_key_int(dbctx, fingerprint, publickey, intrans, false);
399 static int db4_fetch_key_fp(struct onak_dbctx *dbctx,
400 struct openpgp_fingerprint *fingerprint,
401 struct openpgp_publickey **publickey,
404 return db4_fetch_key_int(dbctx, fingerprint, publickey, intrans, true);
408 * fetch_key_id - Given a keyid fetch the key from storage.
409 * @keyid: The keyid to fetch.
410 * @publickey: A pointer to a structure to return the key in.
411 * @intrans: If we're already in a transaction.
413 * We use the hex representation of the keyid as the filename to fetch the
414 * key from. The key is stored in the file as a binary OpenPGP stream of
415 * packets, so we can just use read_openpgp_stream() to read the packets
416 * in and then parse_keys() to parse the packets into a publickey
419 static int db4_fetch_key_id(struct onak_dbctx *dbctx, uint64_t keyid,
420 struct openpgp_publickey **publickey,
423 struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
428 uint32_t shortkeyid = 0;
429 struct openpgp_fingerprint fingerprint;
433 db4_starttrans(dbctx);
436 /* If the key ID fits in 32 bits assume it's a short key id */
437 if (keyid < 0x100000000LL) {
438 ret = privctx->id32db->cursor(privctx->id32db,
443 shortkeyid = keyid & 0xFFFFFFFF;
444 memset(&key, 0, sizeof(key));
445 memset(&data, 0, sizeof(data));
446 key.data = &shortkeyid;
447 key.size = sizeof(shortkeyid);
449 ret = privctx->id64db->cursor(privctx->id64db,
454 memset(&key, 0, sizeof(key));
455 memset(&data, 0, sizeof(data));
457 key.size = sizeof(keyid);
464 memset(&data, 0, sizeof(data));
465 data.ulen = MAX_FINGERPRINT_LEN;
466 data.data = fingerprint.fp;
467 data.flags = DB_DBT_USERMEM;
470 while (cursor->c_get(cursor, &key, &data,
471 first ? DB_SET : DB_NEXT_DUP) == 0) {
472 /* We got a match; retrieve the actual key */
473 fingerprint.length = data.size;
475 if (db4_fetch_key_fp(dbctx, &fingerprint,
479 memset(&data, 0, sizeof(data));
480 data.ulen = MAX_FINGERPRINT_LEN;
481 data.data = fingerprint.fp;
482 data.flags = DB_DBT_USERMEM;
485 cursor->c_close(cursor);
496 * fetch_key_text - Trys to find the keys that contain the supplied text.
497 * @search: The text to search for.
498 * @publickey: A pointer to a structure to return the key in.
500 * This function searches for the supplied text and returns the keys that
503 static int db4_fetch_key_text(struct onak_dbctx *dbctx, const char *search,
504 struct openpgp_publickey **publickey)
506 struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
512 char *searchtext = NULL;
513 struct ll *wordlist = NULL;
514 struct ll *curword = NULL;
515 struct keyarray keylist = { NULL, 0, 0 };
516 struct keyarray newkeylist = { NULL, 0, 0 };
518 struct openpgp_fingerprint fingerprint;
521 searchtext = strdup(search);
522 wordlist = makewordlist(wordlist, searchtext);
524 for (curword = wordlist; curword != NULL; curword = curword->next) {
525 db4_starttrans(dbctx);
527 ret = privctx->worddb->cursor(privctx->worddb,
537 memset(&key, 0, sizeof(key));
538 memset(&data, 0, sizeof(data));
539 key.data = curword->object;
540 key.size = strlen(curword->object);
541 data.flags = DB_DBT_MALLOC;
542 ret = cursor->c_get(cursor,
546 while (ret == 0 && strncmp(key.data, curword->object,
548 ((char *) curword->object)[key.size] == 0) {
550 fingerprint.length = data.size;
551 memcpy(fingerprint.fp, data.data, data.size);
554 * Only add the keys containing this word if this is
555 * our first pass (ie we have no existing key list),
556 * or the key contained a previous word.
558 if (firstpass || array_find(&keylist, &fingerprint)) {
559 array_add(&newkeylist, &fingerprint);
565 ret = cursor->c_get(cursor,
570 array_free(&keylist);
571 keylist.keys = newkeylist.keys;
572 keylist.count = newkeylist.count;
573 keylist.size = newkeylist.size;
574 newkeylist.keys = NULL;
575 newkeylist.count = newkeylist.size = 0;
576 if (data.data != NULL) {
580 cursor->c_close(cursor);
585 llfree(wordlist, NULL);
588 if (keylist.count > config.maxkeys) {
589 keylist.count = config.maxkeys;
592 db4_starttrans(dbctx);
593 for (i = 0; i < keylist.count; i++) {
594 numkeys += db4_fetch_key_fp(dbctx, &keylist.keys[i],
598 array_free(&keylist);
607 static int db4_fetch_key_skshash(struct onak_dbctx *dbctx,
608 const struct skshash *hash,
609 struct openpgp_publickey **publickey)
611 struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
616 struct openpgp_fingerprint fingerprint;
618 ret = privctx->skshashdb->cursor(privctx->skshashdb,
627 memset(&key, 0, sizeof(key));
628 memset(&data, 0, sizeof(data));
629 key.data = (void *) hash->hash;
630 key.size = sizeof(hash->hash);
631 data.ulen = MAX_FINGERPRINT_LEN;
632 data.data = fingerprint.fp;
633 data.flags = DB_DBT_USERMEM;
635 ret = cursor->c_get(cursor,
641 fingerprint.length = data.size;
642 count = db4_fetch_key_fp(dbctx, &fingerprint,
646 cursor->c_close(cursor);
653 * delete_key - Given a keyid delete the key from storage.
654 * @fp: The fingerprint of the key to delete.
655 * @intrans: If we're already in a transaction.
657 * This function deletes a public key from whatever storage mechanism we
658 * are using. Returns 0 if the key existed.
660 static int db4_delete_key(struct onak_dbctx *dbctx,
661 struct openpgp_fingerprint *fp,
664 struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
665 struct openpgp_publickey *publickey = NULL;
668 DBC *cursor64 = NULL;
669 uint32_t shortkeyid = 0;
670 uint64_t subkeyid = 0;
671 struct openpgp_fingerprint *subkeyids = NULL;
675 char *primary = NULL;
676 struct ll *wordlist = NULL;
677 struct ll *curword = NULL;
678 bool deadlock = false;
683 db4_starttrans(dbctx);
686 if (db4_fetch_key_fp(dbctx, fp, &publickey, true) == 0) {
693 if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
698 * Walk through the uids removing the words from the worddb.
700 if (publickey != NULL) {
701 uids = keyuids(publickey, &primary);
704 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
705 wordlist = makewordlist(wordlist, uids[i]);
708 privctx->worddb->cursor(privctx->worddb,
713 for (curword = wordlist; curword != NULL && !deadlock;
714 curword = curword->next) {
716 * New style uses the fingerprint as the data
717 * Old (unsupported) style was the 64 bit keyid
719 memset(&key, 0, sizeof(key));
720 memset(&data, 0, sizeof(data));
721 key.data = curword->object;
722 key.size = strlen(key.data);
724 data.size = fp->length;
726 ret = cursor->c_get(cursor,
732 ret = cursor->c_del(cursor, 0);
735 if (ret != 0 && ret != DB_NOTFOUND) {
736 logthing(LOGTHING_ERROR,
737 "Problem deleting word: %s "
738 "(0x%016" PRIX64 ")",
741 if (ret == DB_LOCK_DEADLOCK) {
746 cursor->c_close(cursor);
750 * Free our UID and word lists.
752 llfree(wordlist, NULL);
753 for (i = 0; uids[i] != NULL; i++) {
762 privctx->id32db->cursor(privctx->id32db,
766 privctx->id64db->cursor(privctx->id64db,
771 /* 32 bit short key mapping to fingerprint */
772 shortkeyid = keyid & 0xFFFFFFFF;
774 memset(&key, 0, sizeof(key));
775 memset(&data, 0, sizeof(data));
776 key.data = &shortkeyid;
777 key.size = sizeof(shortkeyid);
779 data.size = fp->length;
781 ret = cursor->c_get(cursor,
787 ret = cursor->c_del(cursor, 0);
790 if (ret != 0 && ret != DB_NOTFOUND) {
791 logthing(LOGTHING_ERROR,
792 "Problem deleting short keyid: %s "
793 "(0x%016" PRIX64 ")",
796 if (ret == DB_LOCK_DEADLOCK) {
801 /* 64 bit key mapping to fingerprint */
802 memset(&key, 0, sizeof(key));
803 memset(&data, 0, sizeof(data));
805 key.size = sizeof(keyid);
807 data.size = fp->length;
809 ret = cursor64->c_get(cursor64,
815 ret = cursor64->c_del(cursor64, 0);
818 if (ret != 0 && ret != DB_NOTFOUND) {
819 logthing(LOGTHING_ERROR,
820 "Problem deleting keyid: %s "
821 "(0x%016" PRIX64 ")",
824 if (ret == DB_LOCK_DEADLOCK) {
829 subkeyids = keysubkeys(publickey);
831 while (subkeyids != NULL && subkeyids[i].length != 0) {
832 subkeyid = fingerprint2keyid(&subkeyids[i]);
833 memset(&key, 0, sizeof(key));
834 key.data = subkeyids[i].fp;
835 key.size = subkeyids[i].length;
836 ret = privctx->subkeydb->del(privctx->subkeydb,
837 privctx->txn, &key, 0);
838 if (ret != 0 && ret != DB_NOTFOUND) {
839 logthing(LOGTHING_ERROR,
840 "Problem deleting subkey id: %s "
841 "(0x%016" PRIX64 ")",
844 if (ret == DB_LOCK_DEADLOCK) {
849 shortkeyid = subkeyid & 0xFFFFFFFF;
851 /* Remove 32 bit keyid -> fingerprint mapping */
852 memset(&key, 0, sizeof(key));
853 memset(&data, 0, sizeof(data));
854 key.data = &shortkeyid;
855 key.size = sizeof(shortkeyid);
857 data.size = fp->length;
859 ret = cursor->c_get(cursor,
865 ret = cursor->c_del(cursor, 0);
868 if (ret != 0 && ret != DB_NOTFOUND) {
869 logthing(LOGTHING_ERROR,
870 "Problem deleting short keyid: %s "
871 "(0x%016" PRIX64 ")",
874 if (ret == DB_LOCK_DEADLOCK) {
879 /* Remove 64 bit keyid -> fingerprint mapping */
880 memset(&key, 0, sizeof(key));
881 memset(&data, 0, sizeof(data));
882 key.data = &subkeyid;
883 key.size = sizeof(subkeyid);
885 data.size = fp->length;
887 ret = cursor64->c_get(cursor64,
893 ret = cursor64->c_del(cursor64, 0);
896 if (ret != 0 && ret != DB_NOTFOUND) {
897 logthing(LOGTHING_ERROR,
898 "Problem deleting keyid: %s "
899 "(0x%016" PRIX64 ")",
902 if (ret == DB_LOCK_DEADLOCK) {
908 if (subkeyids != NULL) {
912 cursor64->c_close(cursor64);
914 cursor->c_close(cursor);
919 ret = privctx->skshashdb->cursor(privctx->skshashdb,
924 get_skshash(publickey, &hash);
926 /* Remove SKS hash -> fingerprint mapping */
927 memset(&key, 0, sizeof(key));
928 memset(&data, 0, sizeof(data));
929 key.data = hash.hash;
930 key.size = sizeof(hash.hash);
932 data.size = fp->length;
934 ret = cursor->c_get(cursor,
940 ret = cursor->c_del(cursor, 0);
943 if (ret != 0 && ret != DB_NOTFOUND) {
944 logthing(LOGTHING_ERROR,
945 "Problem deleting skshash: %s "
946 "(0x%016" PRIX64 ")",
949 if (ret == DB_LOCK_DEADLOCK) {
954 cursor->c_close(cursor);
958 free_publickey(publickey);
963 key.size = fp->length;
965 keydb_fp(privctx, fp)->del(keydb_fp(privctx, fp),
975 return deadlock ? (-1) : (ret == DB_NOTFOUND);
979 * store_key - Takes a key and stores it.
980 * @publickey: A pointer to the public key to store.
981 * @intrans: If we're already in a transaction.
982 * @update: If true the key exists and should be updated.
984 * Again we just use the hex representation of the keyid as the filename
985 * to store the key to. We flatten the public key to a list of OpenPGP
986 * packets and then use write_openpgp_stream() to write the stream out to
987 * the file. If update is true then we delete the old key first, otherwise
988 * we trust that it doesn't exist.
990 static int db4_store_key(struct onak_dbctx *dbctx,
991 struct openpgp_publickey *publickey, bool intrans,
994 struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
995 struct openpgp_packet_list *packets = NULL;
996 struct openpgp_packet_list *list_end = NULL;
997 struct openpgp_publickey *next = NULL;
1000 struct buffer_ctx storebuf;
1004 uint32_t shortkeyid = 0;
1005 struct openpgp_fingerprint *subkeyids = NULL;
1007 char *primary = NULL;
1008 struct ll *wordlist = NULL;
1009 struct ll *curword = NULL;
1010 bool deadlock = false;
1011 struct skshash hash;
1012 struct openpgp_fingerprint fingerprint;
1014 if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
1015 logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
1019 if (get_fingerprint(publickey->publickey, &fingerprint) != ONAK_E_OK) {
1020 logthing(LOGTHING_ERROR, "Couldn't find fingerprint for key.");
1025 db4_starttrans(dbctx);
1029 * Delete the key if we already have it.
1031 * TODO: Can we optimize this perhaps? Possibly when other data is
1032 * involved as well? I suspect this is easiest and doesn't make a lot
1033 * of difference though - the largest chunk of data is the keydata and
1034 * it definitely needs updated.
1037 deadlock = (db4_delete_key(dbctx, &fingerprint, true) == -1);
1041 * Convert the key to a flat set of binary data.
1044 next = publickey->next;
1045 publickey->next = NULL;
1046 flatten_publickey(publickey, &packets, &list_end);
1047 publickey->next = next;
1049 storebuf.offset = 0;
1050 storebuf.size = 8192;
1051 storebuf.buffer = malloc(8192);
1053 write_openpgp_stream(buffer_putchar, &storebuf, packets);
1056 * Now we have the key data store it in the DB; the fingerprint
1059 memset(&key, 0, sizeof(key));
1060 memset(&data, 0, sizeof(data));
1061 key.data = fingerprint.fp;
1062 key.size = fingerprint.length;
1063 data.size = storebuf.offset;
1064 data.data = storebuf.buffer;
1066 ret = keydb_fp(privctx, &fingerprint)->put(
1067 keydb_fp(privctx, &fingerprint),
1073 logthing(LOGTHING_ERROR,
1074 "Problem storing key: %s",
1076 if (ret == DB_LOCK_DEADLOCK) {
1081 free(storebuf.buffer);
1082 storebuf.buffer = NULL;
1084 storebuf.offset = 0;
1086 free_packet_list(packets);
1091 * Walk through our uids storing the words into the db with the
1095 uids = keyuids(publickey, &primary);
1098 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
1099 wordlist = makewordlist(wordlist, uids[i]);
1102 for (curword = wordlist; curword != NULL && !deadlock;
1103 curword = curword->next) {
1104 memset(&key, 0, sizeof(key));
1105 memset(&data, 0, sizeof(data));
1106 key.data = curword->object;
1107 key.size = strlen(key.data);
1108 data.data = fingerprint.fp;
1109 data.size = fingerprint.length;
1111 ret = privctx->worddb->put(privctx->worddb,
1117 logthing(LOGTHING_ERROR,
1118 "Problem storing word: %s",
1120 if (ret == DB_LOCK_DEADLOCK) {
1127 * Free our UID and word lists.
1129 llfree(wordlist, NULL);
1130 for (i = 0; uids[i] != NULL; i++) {
1139 * Write the truncated 32 bit keyid so we can lookup the fingerprint
1143 shortkeyid = keyid & 0xFFFFFFFF;
1145 memset(&key, 0, sizeof(key));
1146 memset(&data, 0, sizeof(data));
1147 key.data = &shortkeyid;
1148 key.size = sizeof(shortkeyid);
1149 data.data = fingerprint.fp;
1150 data.size = fingerprint.length;
1152 ret = privctx->id32db->put(privctx->id32db,
1158 logthing(LOGTHING_ERROR,
1159 "Problem storing short keyid: %s",
1161 if (ret == DB_LOCK_DEADLOCK) {
1168 * Write the 64 bit keyid so we can lookup the fingerprint for
1172 memset(&key, 0, sizeof(key));
1173 memset(&data, 0, sizeof(data));
1175 key.size = sizeof(keyid);
1176 data.data = fingerprint.fp;
1177 data.size = fingerprint.length;
1179 ret = privctx->id64db->put(privctx->id64db,
1185 logthing(LOGTHING_ERROR,
1186 "Problem storing keyid: %s",
1188 if (ret == DB_LOCK_DEADLOCK) {
1195 subkeyids = keysubkeys(publickey);
1197 while (subkeyids != NULL && subkeyids[i].length != 0) {
1198 /* Store the subkey ID -> main key fp mapping */
1199 memset(&key, 0, sizeof(key));
1200 memset(&data, 0, sizeof(data));
1201 key.data = subkeyids[i].fp;
1202 key.size = subkeyids[i].length;
1203 data.data = fingerprint.fp;
1204 data.size = fingerprint.length;
1206 ret = privctx->subkeydb->put(privctx->subkeydb,
1212 logthing(LOGTHING_ERROR,
1213 "Problem storing subkey keyid: %s",
1215 if (ret == DB_LOCK_DEADLOCK) {
1220 /* Store the 64 bit subkey ID -> main key fp mapping */
1221 memset(&key, 0, sizeof(key));
1222 memset(&data, 0, sizeof(data));
1224 keyid = fingerprint2keyid(&subkeyids[i]);
1226 key.size = sizeof(keyid);
1227 data.data = fingerprint.fp;
1228 data.size = fingerprint.length;
1230 ret = privctx->id64db->put(privctx->id64db,
1236 logthing(LOGTHING_ERROR,
1237 "Problem storing keyid: %s",
1239 if (ret == DB_LOCK_DEADLOCK) {
1244 /* Store the short subkey ID -> main key fp mapping */
1245 shortkeyid = keyid & 0xFFFFFFFF;
1247 memset(&key, 0, sizeof(key));
1248 memset(&data, 0, sizeof(data));
1249 key.data = &shortkeyid;
1250 key.size = sizeof(shortkeyid);
1251 data.data = fingerprint.fp;
1252 data.size = fingerprint.length;
1254 ret = privctx->id32db->put(privctx->id32db,
1260 logthing(LOGTHING_ERROR,
1261 "Problem storing short keyid: %s",
1263 if (ret == DB_LOCK_DEADLOCK) {
1269 if (subkeyids != NULL) {
1276 get_skshash(publickey, &hash);
1277 memset(&key, 0, sizeof(key));
1278 memset(&data, 0, sizeof(data));
1279 key.data = hash.hash;
1280 key.size = sizeof(hash.hash);
1281 data.data = fingerprint.fp;
1282 data.size = fingerprint.length;
1284 ret = privctx->skshashdb->put(privctx->skshashdb,
1290 logthing(LOGTHING_ERROR,
1291 "Problem storing SKS hash: %s",
1293 if (ret == DB_LOCK_DEADLOCK) {
1300 db4_endtrans(dbctx);
1303 return deadlock ? -1 : 0 ;
1307 * iterate_keys - call a function once for each key in the db.
1308 * @iterfunc: The function to call.
1309 * @ctx: A context pointer
1311 * Calls iterfunc once for each key in the database. ctx is passed
1312 * unaltered to iterfunc. This function is intended to aid database dumps
1313 * and statistic calculations.
1315 * Returns the number of keys we iterated over.
1317 static int db4_iterate_keys(struct onak_dbctx *dbctx,
1318 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
1321 struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
1327 struct buffer_ctx fetchbuf;
1328 struct openpgp_packet_list *packets = NULL;
1329 struct openpgp_publickey *key = NULL;
1331 for (i = 0; i < privctx->numdbs; i++) {
1332 ret = privctx->dbconns[i]->cursor(privctx->dbconns[i],
1341 memset(&dbkey, 0, sizeof(dbkey));
1342 memset(&data, 0, sizeof(data));
1343 ret = cursor->c_get(cursor, &dbkey, &data, DB_NEXT);
1345 fetchbuf.buffer = data.data;
1346 fetchbuf.offset = 0;
1347 fetchbuf.size = data.size;
1348 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
1350 parse_keys(packets, &key);
1354 free_publickey(key);
1356 free_packet_list(packets);
1359 memset(&dbkey, 0, sizeof(dbkey));
1360 memset(&data, 0, sizeof(data));
1361 ret = cursor->c_get(cursor, &dbkey, &data,
1365 if (ret != DB_NOTFOUND) {
1366 logthing(LOGTHING_ERROR,
1367 "Problem reading key: %s",
1371 cursor->c_close(cursor);
1379 * Include the basic keydb routines.
1381 #define NEED_GETKEYSIGS 1
1382 #define NEED_KEYID2UID 1
1383 #define NEED_UPDATEKEYS 1
1387 * cleanupdb - De-initialize the key database.
1389 * This function should be called upon program exit to allow the DB to
1390 * cleanup after itself.
1392 static void db4_cleanupdb(struct onak_dbctx *dbctx)
1394 struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
1397 if (privctx->dbenv != NULL) {
1398 privctx->dbenv->txn_checkpoint(privctx->dbenv, 0, 0, 0);
1399 if (privctx->subkeydb != NULL) {
1400 privctx->subkeydb->close(privctx->subkeydb, 0);
1401 privctx->subkeydb = NULL;
1403 if (privctx->skshashdb != NULL) {
1404 privctx->skshashdb->close(privctx->skshashdb, 0);
1405 privctx->skshashdb = NULL;
1407 if (privctx->id64db != NULL) {
1408 privctx->id64db->close(privctx->id64db, 0);
1409 privctx->id64db = NULL;
1411 if (privctx->id32db != NULL) {
1412 privctx->id32db->close(privctx->id32db, 0);
1413 privctx->id32db = NULL;
1415 if (privctx->worddb != NULL) {
1416 privctx->worddb->close(privctx->worddb, 0);
1417 privctx->worddb = NULL;
1419 for (i = 0; i < privctx->numdbs; i++) {
1420 if (privctx->dbconns[i] != NULL) {
1421 privctx->dbconns[i]->close(privctx->dbconns[i],
1423 privctx->dbconns[i] = NULL;
1426 free(privctx->dbconns);
1427 privctx->dbconns = NULL;
1428 privctx->dbenv->close(privctx->dbenv, 0);
1429 privctx->dbenv = NULL;
1438 * initdb - Initialize the key database.
1440 * This function should be called before any of the other functions in
1441 * this file are called in order to allow the DB to be initialized ready
1444 struct onak_dbctx *keydb_db4_init(struct onak_db_config *dbcfg, bool readonly)
1451 struct stat statbuf;
1453 struct onak_dbctx *dbctx;
1454 struct onak_db4_dbctx *privctx;
1456 dbctx = malloc(sizeof(*dbctx));
1457 if (dbctx == NULL) {
1460 dbctx->config = dbcfg;
1461 dbctx->priv = privctx = calloc(1, sizeof(*privctx));
1462 if (privctx == NULL) {
1467 /* Default to 16 key data DBs */
1468 privctx->numdbs = 16;
1470 snprintf(buf, sizeof(buf) - 1, "%s/%s", dbcfg->location,
1472 ret = stat(buf, &statbuf);
1473 while ((ret == 0) || (errno != ENOENT)) {
1475 logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade "
1476 "lock file: %s (%d)", strerror(errno), ret);
1479 logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting.");
1481 ret = stat(buf, &statbuf);
1485 snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", dbcfg->location);
1486 numdb = fopen(buf, "r");
1487 if (numdb != NULL) {
1488 if (fgets(buf, sizeof(buf), numdb) != NULL) {
1489 privctx->numdbs = atoi(buf);
1492 } else if (!readonly) {
1493 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
1495 numdb = fopen(buf, "w");
1496 if (numdb != NULL) {
1497 fprintf(numdb, "%d", privctx->numdbs);
1500 logthing(LOGTHING_ERROR,
1501 "Couldn't write num_keydb: %s",
1506 privctx->dbconns = calloc(privctx->numdbs, sizeof (DB *));
1507 if (privctx->dbconns == NULL) {
1508 logthing(LOGTHING_CRITICAL,
1509 "Couldn't allocate memory for dbconns");
1514 ret = db_env_create(&privctx->dbenv, 0);
1516 logthing(LOGTHING_CRITICAL,
1517 "db_env_create: %s", db_strerror(ret));
1522 * Up the number of locks we're allowed at once. We base this on
1523 * the maximum number of keys we're going to return.
1526 maxlocks = config.maxkeys * 16;
1527 if (maxlocks < 1000) {
1530 privctx->dbenv->set_lk_max_locks(privctx->dbenv, maxlocks);
1531 privctx->dbenv->set_lk_max_objects(privctx->dbenv, maxlocks);
1535 * Enable deadlock detection so that we don't block indefinitely on
1536 * anything. What we really want is simple 2 state locks, but I'm not
1537 * sure how to make the standard DB functions do that yet.
1540 privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
1541 ret = privctx->dbenv->set_lk_detect(privctx->dbenv, DB_LOCK_DEFAULT);
1543 logthing(LOGTHING_CRITICAL,
1544 "db_env_create: %s", db_strerror(ret));
1549 ret = privctx->dbenv->open(privctx->dbenv, dbcfg->location,
1550 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
1554 #ifdef DB_VERSION_MISMATCH
1555 if (ret == DB_VERSION_MISMATCH) {
1556 privctx->dbenv->close(privctx->dbenv, 0);
1557 privctx->dbenv = NULL;
1558 ret = db4_upgradedb(dbctx);
1560 ret = db_env_create(&privctx->dbenv, 0);
1563 privctx->dbenv->set_errcall(privctx->dbenv,
1565 privctx->dbenv->set_lk_detect(privctx->dbenv,
1567 ret = privctx->dbenv->open(privctx->dbenv,
1569 DB_INIT_LOG | DB_INIT_MPOOL |
1570 DB_INIT_LOCK | DB_INIT_TXN |
1571 DB_CREATE | DB_RECOVER,
1575 privctx->dbenv->txn_checkpoint(
1585 logthing(LOGTHING_CRITICAL,
1586 "Error opening db environment: %s (%s)",
1589 if (privctx->dbenv != NULL) {
1590 privctx->dbenv->close(privctx->dbenv, 0);
1591 privctx->dbenv = NULL;
1597 db4_starttrans(dbctx);
1599 for (i = 0; !ret && i < privctx->numdbs; i++) {
1600 ret = db_create(&privctx->dbconns[i],
1603 logthing(LOGTHING_CRITICAL,
1604 "db_create: %s", db_strerror(ret));
1608 snprintf(buf, 1023, "keydb.%d.db", i);
1613 ret = privctx->dbconns[i]->open(
1614 privctx->dbconns[i],
1622 logthing(LOGTHING_CRITICAL,
1623 "Error opening key database:"
1633 ret = db_create(&privctx->worddb, privctx->dbenv, 0);
1635 logthing(LOGTHING_CRITICAL, "db_create: %s",
1641 ret = privctx->worddb->set_flags(privctx->worddb, DB_DUP);
1645 ret = privctx->worddb->open(privctx->worddb, privctx->txn,
1646 "worddb", "worddb", DB_BTREE,
1650 logthing(LOGTHING_CRITICAL,
1651 "Error opening word database: %s (%s)",
1658 ret = db_create(&privctx->id32db, privctx->dbenv, 0);
1660 logthing(LOGTHING_CRITICAL, "db_create: %s",
1666 ret = privctx->id32db->set_flags(privctx->id32db, DB_DUP);
1670 ret = privctx->id32db->open(privctx->id32db, privctx->txn,
1671 "id32db", "id32db", DB_HASH,
1675 logthing(LOGTHING_CRITICAL,
1676 "Error opening id32 database: %s (%s)",
1683 ret = db_create(&privctx->id64db, privctx->dbenv, 0);
1685 logthing(LOGTHING_CRITICAL, "db_create: %s",
1691 ret = privctx->id64db->set_flags(privctx->id64db, DB_DUP);
1695 ret = privctx->id64db->open(privctx->id64db, privctx->txn,
1696 "id64db", "id64db", DB_HASH,
1700 logthing(LOGTHING_CRITICAL,
1701 "Error opening id64 database: %s (%s)",
1708 ret = db_create(&privctx->skshashdb, privctx->dbenv, 0);
1710 logthing(LOGTHING_CRITICAL, "db_create: %s",
1716 ret = privctx->skshashdb->open(privctx->skshashdb, privctx->txn,
1718 "skshashdb", DB_HASH,
1722 logthing(LOGTHING_CRITICAL,
1723 "Error opening skshash database: %s (%s)",
1730 ret = db_create(&privctx->subkeydb, privctx->dbenv, 0);
1732 logthing(LOGTHING_CRITICAL, "db_create: %s",
1738 ret = privctx->subkeydb->open(privctx->subkeydb, privctx->txn,
1739 "subkeydb", "subkeydb",
1744 logthing(LOGTHING_CRITICAL,
1745 "Error opening subkey database: %s (%s)",
1751 if (privctx->txn != NULL) {
1752 db4_endtrans(dbctx);
1756 db4_cleanupdb(dbctx);
1757 logthing(LOGTHING_CRITICAL,
1758 "Error opening database; exiting");
1762 dbctx->cleanupdb = db4_cleanupdb;
1763 dbctx->starttrans = db4_starttrans;
1764 dbctx->endtrans = db4_endtrans;
1765 dbctx->fetch_key = db4_fetch_key;
1766 dbctx->fetch_key_fp = db4_fetch_key_fp;
1767 dbctx->fetch_key_id = db4_fetch_key_id;
1768 dbctx->fetch_key_text = db4_fetch_key_text;
1769 dbctx->fetch_key_skshash = db4_fetch_key_skshash;
1770 dbctx->store_key = db4_store_key;
1771 dbctx->update_keys = generic_update_keys;
1772 dbctx->delete_key = db4_delete_key;
1773 dbctx->getkeysigs = generic_getkeysigs;
1774 dbctx->cached_getkeysigs = generic_cached_getkeysigs;
1775 dbctx->keyid2uid = generic_keyid2uid;
1776 dbctx->iterate_keys = db4_iterate_keys;