+ if (!deadlock) {
+ subkeyids = keysubkeys(publickey);
+ i = 0;
+ while (subkeyids != NULL && subkeyids[i].length != 0) {
+ /* Store the subkey ID -> main key fp mapping */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = subkeyids[i].fp;
+ key.size = subkeyids[i].length;
+ data.data = fingerprint.fp;
+ data.size = fingerprint.length;
+
+ ret = privctx->subkeydb->put(privctx->subkeydb,
+ privctx->txn,
+ &key,
+ &data,
+ 0);
+ if (ret != 0) {
+ logthing(LOGTHING_ERROR,
+ "Problem storing subkey keyid: %s",
+ db_strerror(ret));
+ if (ret == DB_LOCK_DEADLOCK) {
+ deadlock = true;
+ }
+ }
+
+ /* Store the 64 bit subkey ID -> main key fp mapping */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ keyid = fingerprint2keyid(&subkeyids[i]);
+ key.data = &keyid;
+ key.size = sizeof(keyid);
+ data.data = fingerprint.fp;
+ data.size = fingerprint.length;
+
+ ret = privctx->id64db->put(privctx->id64db,
+ privctx->txn,
+ &key,
+ &data,
+ 0);
+ if (ret != 0) {
+ logthing(LOGTHING_ERROR,
+ "Problem storing keyid: %s",
+ db_strerror(ret));
+ if (ret == DB_LOCK_DEADLOCK) {
+ deadlock = true;
+ }
+ }
+
+ /* Store the short subkey ID -> main key fp mapping */
+ shortkeyid = keyid & 0xFFFFFFFF;
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = &shortkeyid;
+ key.size = sizeof(shortkeyid);
+ data.data = fingerprint.fp;
+ data.size = fingerprint.length;
+
+ ret = privctx->id32db->put(privctx->id32db,
+ privctx->txn,
+ &key,
+ &data,
+ 0);
+ if (ret != 0) {
+ logthing(LOGTHING_ERROR,
+ "Problem storing short keyid: %s",
+ db_strerror(ret));
+ if (ret == DB_LOCK_DEADLOCK) {
+ deadlock = true;
+ }
+ }
+ i++;
+ }
+ if (subkeyids != NULL) {
+ free(subkeyids);
+ subkeyids = NULL;
+ }
+ }
+
+ if (!deadlock) {
+ get_skshash(publickey, &hash);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hash.hash;
+ key.size = sizeof(hash.hash);
+ data.data = fingerprint.fp;
+ data.size = fingerprint.length;
+
+ ret = privctx->skshashdb->put(privctx->skshashdb,
+ privctx->txn,
+ &key,
+ &data,
+ 0);
+ if (ret != 0) {
+ logthing(LOGTHING_ERROR,
+ "Problem storing SKS hash: %s",
+ db_strerror(ret));
+ if (ret == DB_LOCK_DEADLOCK) {
+ deadlock = true;
+ }
+ }
+ }
+
+ if (!intrans) {
+ db4_endtrans(dbctx);
+ }
+
+ return deadlock ? -1 : 0 ;
+}
+
+/**
+ * iterate_keys - call a function once for each key in the db.
+ * @iterfunc: The function to call.
+ * @ctx: A context pointer
+ *
+ * Calls iterfunc once for each key in the database. ctx is passed
+ * unaltered to iterfunc. This function is intended to aid database dumps
+ * and statistic calculations.
+ *
+ * Returns the number of keys we iterated over.
+ */
+static int db4_iterate_keys(struct onak_dbctx *dbctx,
+ void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
+ void *ctx)
+{
+ struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+ DBT dbkey, data;
+ DBC *cursor = NULL;
+ int ret = 0;
+ int i = 0;
+ int numkeys = 0;
+ struct buffer_ctx fetchbuf;
+ struct openpgp_packet_list *packets = NULL;
+ struct openpgp_publickey *key = NULL;
+
+ for (i = 0; i < privctx->numdbs; i++) {
+ ret = privctx->dbconns[i]->cursor(privctx->dbconns[i],
+ NULL,
+ &cursor,
+ 0); /* flags */
+
+ if (ret != 0) {
+ continue;
+ }
+
+ memset(&dbkey, 0, sizeof(dbkey));
+ memset(&data, 0, sizeof(data));
+ ret = cursor->c_get(cursor, &dbkey, &data, DB_NEXT);
+ while (ret == 0) {
+ fetchbuf.buffer = data.data;
+ fetchbuf.offset = 0;
+ fetchbuf.size = data.size;
+ read_openpgp_stream(buffer_fetchchar, &fetchbuf,
+ &packets, 0);
+ parse_keys(packets, &key);
+
+ iterfunc(ctx, key);
+
+ free_publickey(key);
+ key = NULL;
+ free_packet_list(packets);
+ packets = NULL;
+
+ memset(&dbkey, 0, sizeof(dbkey));
+ memset(&data, 0, sizeof(data));
+ ret = cursor->c_get(cursor, &dbkey, &data,
+ DB_NEXT);
+ numkeys++;
+ }
+ if (ret != DB_NOTFOUND) {
+ logthing(LOGTHING_ERROR,
+ "Problem reading key: %s",
+ db_strerror(ret));
+ }
+
+ cursor->c_close(cursor);
+ cursor = NULL;
+ }
+
+ return numkeys;
+}
+
+/*
+ * Include the basic keydb routines.
+ */
+#define NEED_GETKEYSIGS 1
+#define NEED_KEYID2UID 1
+#define NEED_UPDATEKEYS 1
+#include "keydb.c"
+
+/**
+ * cleanupdb - De-initialize the key database.
+ *
+ * This function should be called upon program exit to allow the DB to
+ * cleanup after itself.
+ */
+static void db4_cleanupdb(struct onak_dbctx *dbctx)
+{
+ struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+ int i = 0;
+
+ if (privctx->dbenv != NULL) {
+ privctx->dbenv->txn_checkpoint(privctx->dbenv, 0, 0, 0);
+ if (privctx->subkeydb != NULL) {
+ privctx->subkeydb->close(privctx->subkeydb, 0);
+ privctx->subkeydb = NULL;
+ }
+ if (privctx->skshashdb != NULL) {
+ privctx->skshashdb->close(privctx->skshashdb, 0);
+ privctx->skshashdb = NULL;
+ }
+ if (privctx->id64db != NULL) {
+ privctx->id64db->close(privctx->id64db, 0);
+ privctx->id64db = NULL;
+ }
+ if (privctx->id32db != NULL) {
+ privctx->id32db->close(privctx->id32db, 0);
+ privctx->id32db = NULL;
+ }
+ if (privctx->worddb != NULL) {
+ privctx->worddb->close(privctx->worddb, 0);
+ privctx->worddb = NULL;
+ }
+ for (i = 0; i < privctx->numdbs; i++) {
+ if (privctx->dbconns[i] != NULL) {
+ privctx->dbconns[i]->close(privctx->dbconns[i],
+ 0);
+ privctx->dbconns[i] = NULL;
+ }
+ }
+ free(privctx->dbconns);
+ privctx->dbconns = NULL;
+ privctx->dbenv->close(privctx->dbenv, 0);
+ privctx->dbenv = NULL;
+ }
+
+ free(privctx);
+ dbctx->priv = NULL;
+ free(dbctx);
+}
+
+/**
+ * initdb - Initialize the key database.
+ *
+ * This function should be called before any of the other functions in
+ * this file are called in order to allow the DB to be initialized ready
+ * for access.
+ */
+struct onak_dbctx *keydb_db4_init(struct onak_db_config *dbcfg, bool readonly)
+{
+ char buf[1024];
+ FILE *numdb = NULL;
+ int ret = 0;
+ int i = 0;
+ uint32_t flags = 0;
+ struct stat statbuf;
+ int maxlocks;
+ struct onak_dbctx *dbctx;
+ struct onak_db4_dbctx *privctx;
+
+ dbctx = malloc(sizeof(*dbctx));
+ if (dbctx == NULL) {
+ return NULL;
+ }
+ dbctx->config = dbcfg;
+ dbctx->priv = privctx = calloc(1, sizeof(*privctx));
+ if (privctx == NULL) {
+ free(dbctx);
+ return NULL;
+ }
+
+ /* Default to 16 key data DBs */
+ privctx->numdbs = 16;
+
+ snprintf(buf, sizeof(buf) - 1, "%s/%s", dbcfg->location,
+ DB4_UPGRADE_FILE);
+ ret = stat(buf, &statbuf);
+ while ((ret == 0) || (errno != ENOENT)) {
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade "
+ "lock file: %s (%d)", strerror(errno), ret);
+ exit(1);
+ }
+ logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting.");
+ sleep(5);
+ ret = stat(buf, &statbuf);
+ }
+ ret = 0;
+
+ snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", dbcfg->location);
+ numdb = fopen(buf, "r");
+ if (numdb != NULL) {
+ if (fgets(buf, sizeof(buf), numdb) != NULL) {
+ privctx->numdbs = atoi(buf);
+ }
+ fclose(numdb);
+ } else if (!readonly) {
+ logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
+ strerror(errno));
+ numdb = fopen(buf, "w");
+ if (numdb != NULL) {
+ fprintf(numdb, "%d", privctx->numdbs);
+ fclose(numdb);
+ } else {
+ logthing(LOGTHING_ERROR,
+ "Couldn't write num_keydb: %s",
+ strerror(errno));
+ }
+ }
+
+ privctx->dbconns = calloc(privctx->numdbs, sizeof (DB *));
+ if (privctx->dbconns == NULL) {
+ logthing(LOGTHING_CRITICAL,
+ "Couldn't allocate memory for dbconns");
+ ret = 1;
+ }
+
+ if (ret == 0) {
+ ret = db_env_create(&privctx->dbenv, 0);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "db_env_create: %s", db_strerror(ret));
+ }
+ }
+
+ /*
+ * Up the number of locks we're allowed at once. We base this on
+ * the maximum number of keys we're going to return.
+ */
+ if (ret == 0) {
+ maxlocks = config.maxkeys * 16;
+ if (maxlocks < 1000) {
+ maxlocks = 1000;
+ }
+ privctx->dbenv->set_lk_max_locks(privctx->dbenv, maxlocks);
+ privctx->dbenv->set_lk_max_objects(privctx->dbenv, maxlocks);
+ }
+
+ /*
+ * Enable deadlock detection so that we don't block indefinitely on
+ * anything. What we really want is simple 2 state locks, but I'm not
+ * sure how to make the standard DB functions do that yet.
+ */
+ if (ret == 0) {
+ privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
+ ret = privctx->dbenv->set_lk_detect(privctx->dbenv, DB_LOCK_DEFAULT);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "db_env_create: %s", db_strerror(ret));
+ }
+ }
+
+ if (ret == 0) {
+ ret = privctx->dbenv->open(privctx->dbenv, dbcfg->location,
+ DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
+ DB_INIT_TXN |
+ DB_CREATE,
+ 0);
+#ifdef DB_VERSION_MISMATCH
+ if (ret == DB_VERSION_MISMATCH) {
+ privctx->dbenv->close(privctx->dbenv, 0);
+ privctx->dbenv = NULL;
+ ret = db4_upgradedb(dbctx);
+ if (ret == 0) {
+ ret = db_env_create(&privctx->dbenv, 0);
+ }
+ if (ret == 0) {
+ privctx->dbenv->set_errcall(privctx->dbenv,
+ &db4_errfunc);
+ privctx->dbenv->set_lk_detect(privctx->dbenv,
+ DB_LOCK_DEFAULT);
+ ret = privctx->dbenv->open(privctx->dbenv,
+ dbcfg->location,
+ DB_INIT_LOG | DB_INIT_MPOOL |
+ DB_INIT_LOCK | DB_INIT_TXN |
+ DB_CREATE | DB_RECOVER,
+ 0);
+
+ if (ret == 0) {
+ privctx->dbenv->txn_checkpoint(
+ privctx->dbenv,
+ 0,
+ 0,
+ DB_FORCE);
+ }
+ }
+ }
+#endif
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "Error opening db environment: %s (%s)",
+ dbcfg->location,
+ db_strerror(ret));
+ if (privctx->dbenv != NULL) {
+ privctx->dbenv->close(privctx->dbenv, 0);
+ privctx->dbenv = NULL;
+ }
+ }
+ }
+
+ if (ret == 0) {
+ db4_starttrans(dbctx);
+
+ for (i = 0; !ret && i < privctx->numdbs; i++) {
+ ret = db_create(&privctx->dbconns[i],
+ privctx->dbenv, 0);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "db_create: %s", db_strerror(ret));
+ }
+
+ if (ret == 0) {
+ snprintf(buf, 1023, "keydb.%d.db", i);
+ flags = DB_CREATE;
+ if (readonly) {
+ flags = DB_RDONLY;
+ }
+ ret = privctx->dbconns[i]->open(
+ privctx->dbconns[i],
+ privctx->txn,
+ buf,
+ "keydb",
+ DB_HASH,
+ flags,
+ 0664);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "Error opening key database:"
+ " %s (%s)",
+ buf,
+ db_strerror(ret));
+ }
+ }
+ }
+ }
+
+ if (ret == 0) {
+ ret = db_create(&privctx->worddb, privctx->dbenv, 0);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL, "db_create: %s",
+ db_strerror(ret));
+ }
+ }
+
+ if (ret == 0) {
+ ret = privctx->worddb->set_flags(privctx->worddb, DB_DUP);
+ }
+
+ if (ret == 0) {
+ ret = privctx->worddb->open(privctx->worddb, privctx->txn,
+ "worddb", "worddb", DB_BTREE,
+ flags,
+ 0664);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "Error opening word database: %s (%s)",
+ "worddb",
+ db_strerror(ret));
+ }
+ }
+
+ if (ret == 0) {
+ ret = db_create(&privctx->id32db, privctx->dbenv, 0);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL, "db_create: %s",
+ db_strerror(ret));
+ }
+ }
+
+ if (ret == 0) {
+ ret = privctx->id32db->set_flags(privctx->id32db, DB_DUP);
+ }
+
+ if (ret == 0) {
+ ret = privctx->id32db->open(privctx->id32db, privctx->txn,
+ "id32db", "id32db", DB_HASH,
+ flags,
+ 0664);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "Error opening id32 database: %s (%s)",
+ "id32db",
+ db_strerror(ret));
+ }
+ }
+
+ if (ret == 0) {
+ ret = db_create(&privctx->id64db, privctx->dbenv, 0);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL, "db_create: %s",
+ db_strerror(ret));
+ }
+ }
+
+ if (ret == 0) {
+ ret = privctx->id64db->set_flags(privctx->id64db, DB_DUP);
+ }
+
+ if (ret == 0) {
+ ret = privctx->id64db->open(privctx->id64db, privctx->txn,
+ "id64db", "id64db", DB_HASH,
+ flags,
+ 0664);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "Error opening id64 database: %s (%s)",
+ "id64db",
+ db_strerror(ret));
+ }
+ }
+
+ if (ret == 0) {
+ ret = db_create(&privctx->skshashdb, privctx->dbenv, 0);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL, "db_create: %s",
+ db_strerror(ret));
+ }
+ }
+
+ if (ret == 0) {
+ ret = privctx->skshashdb->open(privctx->skshashdb, privctx->txn,
+ "skshashdb",
+ "skshashdb", DB_HASH,
+ flags,
+ 0664);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "Error opening skshash database: %s (%s)",
+ "skshashdb",
+ db_strerror(ret));
+ }
+ }
+
+ if (ret == 0) {
+ ret = db_create(&privctx->subkeydb, privctx->dbenv, 0);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL, "db_create: %s",
+ db_strerror(ret));
+ }
+ }
+
+ if (ret == 0) {
+ ret = privctx->subkeydb->open(privctx->subkeydb, privctx->txn,
+ "subkeydb", "subkeydb",
+ DB_HASH,
+ flags,
+ 0664);
+ if (ret != 0) {
+ logthing(LOGTHING_CRITICAL,
+ "Error opening subkey database: %s (%s)",
+ "subkeydb",
+ db_strerror(ret));
+ }
+ }
+
+ if (privctx->txn != NULL) {
+ db4_endtrans(dbctx);
+ }
+
+ if (ret != 0) {
+ db4_cleanupdb(dbctx);
+ logthing(LOGTHING_CRITICAL,
+ "Error opening database; exiting");
+ exit(EXIT_FAILURE);
+ }
+
+ dbctx->cleanupdb = db4_cleanupdb;
+ dbctx->starttrans = db4_starttrans;
+ dbctx->endtrans = db4_endtrans;
+ dbctx->fetch_key_id = db4_fetch_key_id;
+ dbctx->fetch_key_fp = db4_fetch_key_fp;
+ dbctx->fetch_key_text = db4_fetch_key_text;
+ dbctx->fetch_key_skshash = db4_fetch_key_skshash;
+ dbctx->store_key = db4_store_key;
+ dbctx->update_keys = generic_update_keys;
+ dbctx->delete_key = db4_delete_key;
+ dbctx->getkeysigs = generic_getkeysigs;
+ dbctx->cached_getkeysigs = generic_cached_getkeysigs;
+ dbctx->keyid2uid = generic_keyid2uid;
+ dbctx->iterate_keys = db4_iterate_keys;
+
+ return dbctx;
+}