From: Jonathan McDowell Date: Mon, 25 Apr 2011 03:11:42 +0000 (-0700) Subject: Add support for key retrieval by SKS hash X-Git-Url: https://git.sommitrealweird.co.uk/onak.git/commitdiff_plain/7ca3c239d76ae7112c166f29c35e11620ba93d9f Add support for key retrieval by SKS hash Add a new backend DB function fetch_key_skshash and implement it for the fs/db4/keyd & dynamic backends. This allows us to retrieve a key using the SKS hash, which will be necessary to implement the gossip protocol. --- diff --git a/keyd.c b/keyd.c index 5d5b7ba..4534a57 100644 --- a/keyd.c +++ b/keyd.c @@ -140,6 +140,7 @@ int sock_do(int fd) struct openpgp_packet_list *packets = NULL; struct openpgp_packet_list *list_end = NULL; struct buffer_ctx storebuf; + struct skshash hash; /* * Get the command from the client. @@ -357,6 +358,53 @@ int sock_do(int fd) write(fd, stats, sizeof(*stats)); break; + case KEYD_CMD_GETSKSHASH: + cmd = KEYD_REPLY_OK; + write(fd, &cmd, sizeof(cmd)); + bytes = read(fd, hash.hash, sizeof(hash.hash)); + if (bytes != sizeof(hash.hash)) { + ret = 1; + } + storebuf.offset = 0; + if (ret == 0) { + logthing(LOGTHING_INFO, + "Fetching by hash" + ", result: %d", + config.dbbackend-> + fetch_key_skshash(&hash, + &key)); + if (key != NULL) { + storebuf.size = 8192; + storebuf.buffer = malloc(8192); + + flatten_publickey(key, + &packets, + &list_end); + write_openpgp_stream(buffer_putchar, + &storebuf, + packets); + logthing(LOGTHING_TRACE, + "Sending %d bytes.", + storebuf.offset); + write(fd, &storebuf.offset, + sizeof(storebuf.offset)); + write(fd, storebuf.buffer, + storebuf.offset); + + free(storebuf.buffer); + storebuf.buffer = NULL; + storebuf.size = storebuf.offset = 0; + free_packet_list(packets); + packets = list_end = NULL; + free_publickey(key); + key = NULL; + } else { + write(fd, &storebuf.offset, + sizeof(storebuf.offset)); + } + } + break; + default: logthing(LOGTHING_ERROR, "Got unknown command: %d", cmd); diff --git a/keyd.h b/keyd.h index ce19d8d..4313a4f 100644 --- a/keyd.h +++ b/keyd.h @@ -25,6 +25,7 @@ enum keyd_ops { KEYD_CMD_CLOSE, KEYD_CMD_QUIT, KEYD_CMD_STATS, + KEYD_CMD_GETSKSHASH, KEYD_CMD_LAST /* Placeholder */ }; @@ -33,7 +34,7 @@ enum keyd_reply { KEYD_REPLY_UNKNOWN_CMD = 1 }; -static uint32_t keyd_version = 2; +static uint32_t keyd_version = 3; struct keyd_stats { time_t started; diff --git a/keydb.h b/keydb.h index b3be902..aaca122 100644 --- a/keydb.h +++ b/keydb.h @@ -104,6 +104,17 @@ struct dbfuncs { int (*fetch_key_text)(const char *search, struct openpgp_publickey **publickey); +/** + * fetch_key_skshash - Tries to find the keys from an SKS hash + * @hash: The hash to search for. + * @publickey: A pointer to a structure to return the key in. + * + * This function looks for the key that is referenced by the supplied + * SKS hash and returns it. + */ + int (*fetch_key_skshash)(const struct skshash *hash, + struct openpgp_publickey **publickey); + /** * update_keys - Takes a list of public keys and updates them in the DB. * @keys: The keys to update in the DB. diff --git a/keydb_db4.c b/keydb_db4.c index 6fbf9da..eb41f86 100644 --- a/keydb_db4.c +++ b/keydb_db4.c @@ -56,6 +56,11 @@ static DB *worddb = NULL; */ static DB *id32db = NULL; +/** + * skshashdb - our connection to the SKS hash database. + */ +static DB *skshashdb = NULL; + /** * txn - our current transaction id. */ @@ -157,6 +162,10 @@ static void db4_cleanupdb(void) if (dbenv != NULL) { dbenv->txn_checkpoint(dbenv, 0, 0, 0); + if (skshashdb != NULL) { + skshashdb->close(skshashdb, 0); + skshashdb = NULL; + } if (id32db != NULL) { id32db->close(id32db, 0); id32db = NULL; @@ -255,6 +264,18 @@ static int db4_upgradedb(int numdb) db_strerror(ret)); } + ret = db_create(&curdb, NULL, 0); + if (ret == 0) { + snprintf(buf, sizeof(buf) - 1, "%s/skshashdb", config.db_dir); + logthing(LOGTHING_DEBUG, "Upgrading %s", buf); + ret = curdb->upgrade(curdb, buf, 0); + curdb->close(curdb, 0); + } else { + logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s", + buf, + db_strerror(ret)); + } + snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir, DB4_UPGRADE_FILE); unlink(buf); @@ -480,6 +501,27 @@ static void db4_initdb(bool readonly) } } + if (ret == 0) { + ret = db_create(&skshashdb, dbenv, 0); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, "db_create: %s", + db_strerror(ret)); + } + } + + if (ret == 0) { + ret = skshashdb->open(skshashdb, txn, "skshashdb", + "skshashdb", DB_HASH, + flags, + 0664); + if (ret != 0) { + logthing(LOGTHING_CRITICAL, + "Error opening skshash database: %s (%s)", + "skshashdb", + db_strerror(ret)); + } + } + if (txn != NULL) { db4_endtrans(); } @@ -722,6 +764,45 @@ static int db4_fetch_key_text(const char *search, return (numkeys); } +static int db4_fetch_key_skshash(const struct skshash *hash, + struct openpgp_publickey **publickey) +{ + DBT key, data; + DBC *cursor = NULL; + uint64_t keyid = 0; + int ret = 0; + + ret = skshashdb->cursor(skshashdb, + txn, + &cursor, + 0); /* flags */ + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + key.data = (void *) hash->hash; + key.size = sizeof(hash->hash); + data.flags = DB_DBT_MALLOC; + + ret = cursor->c_get(cursor, + &key, + &data, + DB_SET); + + if (ret == 0) { + keyid = *(uint64_t *) data.data; + + if (data.data != NULL) { + free(data.data); + data.data = NULL; + } + } + + ret = cursor->c_close(cursor); + cursor = NULL; + + return db4_fetch_key(keyid, publickey, false); +} + /** * delete_key - Given a keyid delete the key from storage. * @keyid: The keyid to delete. @@ -745,6 +826,7 @@ static int db4_delete_key(uint64_t keyid, bool intrans) struct ll *wordlist = NULL; struct ll *curword = NULL; bool deadlock = false; + struct skshash hash; if (!intrans) { db4_starttrans(); @@ -912,6 +994,38 @@ static int db4_delete_key(uint64_t keyid, bool intrans) free(subkeyids); subkeyids = NULL; } + ret = cursor->c_close(cursor); + cursor = 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 = &keyid; + data.size = sizeof(keyid); + + ret = cursor->c_get(cursor, + &key, + &data, + DB_GET_BOTH); + + if (ret == 0) { + ret = cursor->c_del(cursor, 0); + } + + if (ret != 0) { + logthing(LOGTHING_ERROR, + "Problem deleting skshash: %s", + db_strerror(ret)); + if (ret == DB_LOCK_DEADLOCK) { + deadlock = true; + } + } ret = cursor->c_close(cursor); cursor = NULL; @@ -966,6 +1080,7 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, struct ll *wordlist = NULL; struct ll *curword = NULL; bool deadlock = false; + struct skshash hash; keyid = get_keyid(publickey); @@ -1159,6 +1274,30 @@ static int db4_store_key(struct openpgp_publickey *publickey, bool intrans, } } + 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 = &keyid; + data.size = sizeof(keyid); + + ret = skshashdb->put(skshashdb, + 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(); } @@ -1247,6 +1386,7 @@ struct dbfuncs keydb_db4_funcs = { .endtrans = db4_endtrans, .fetch_key = db4_fetch_key, .fetch_key_text = db4_fetch_key_text, + .fetch_key_skshash = db4_fetch_key_skshash, .store_key = db4_store_key, .update_keys = generic_update_keys, .delete_key = db4_delete_key, diff --git a/keydb_dynamic.c b/keydb_dynamic.c index fa4b13b..5c2d3d5 100644 --- a/keydb_dynamic.c +++ b/keydb_dynamic.c @@ -198,6 +198,23 @@ static int dynamic_fetch_key_text(const char *search, return -1; } +static int dynamic_fetch_key_skshash(const struct skshash *hash, + struct openpgp_publickey **publickey) +{ + if (loaded_backend == NULL) { + load_backend(); + } + + if (loaded_backend != NULL) { + if (loaded_backend->fetch_key_skshash != NULL) { + return loaded_backend->fetch_key_skshash(hash, + publickey); + } + } + + return -1; +} + static int dynamic_iterate_keys(void (*iterfunc)(void *ctx, struct openpgp_publickey *key), void *ctx) { @@ -487,6 +504,7 @@ struct dbfuncs keydb_dynamic_funcs = { .endtrans = dynamic_endtrans, .fetch_key = dynamic_fetch_key, .fetch_key_text = dynamic_fetch_key_text, + .fetch_key_skshash = dynamic_fetch_key_skshash, .store_key = dynamic_store_key, .update_keys = dynamic_update_keys, .delete_key = dynamic_delete_key, diff --git a/keydb_fs.c b/keydb_fs.c index 41d55cf..02adf6b 100644 --- a/keydb_fs.c +++ b/keydb_fs.c @@ -126,6 +126,19 @@ static void subkeypath(char *buffer, size_t length, uint64_t subkey, keyid); } +static void skshashpath(char *buffer, size_t length, + const struct skshash *hash) +{ + snprintf(buffer, length, "%s/skshash/%02X/%02X/%02X%02X%02X%02X/" + "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + config.db_dir, + hash->hash[0], hash->hash[1], + hash->hash[0], hash->hash[1], hash->hash[2], hash->hash[3], + hash->hash[4], hash->hash[5], hash->hash[6], hash->hash[7], + hash->hash[8], hash->hash[9], hash->hash[10], hash->hash[11], + hash->hash[12], hash->hash[13], hash->hash[14], + hash->hash[15]); +} static void subkeydir(char *buffer, size_t length, uint64_t subkey) { snprintf(buffer, length, "%s/subkeys/%02X/%02X/%08X", @@ -307,7 +320,9 @@ static int fs_store_key(struct openpgp_publickey *publickey, bool intrans, struct openpgp_publickey *next = NULL; uint64_t keyid = get_keyid(publickey); struct ll *wordlist = NULL, *wl = NULL; + struct skshash hash; uint64_t *subkeyids = NULL; + uint32_t hashid; int i = 0; @@ -365,6 +380,13 @@ static int fs_store_key(struct openpgp_publickey *publickey, bool intrans, free(subkeyids); subkeyids = NULL; } + + get_skshash(publickey, &hash); + hashid = (hash.hash[0] << 24) + (hash.hash[1] << 16) + + (hash.hash[2] << 8) + hash.hash[3]; + prove_path_to(hashid, "skshash"); + skshashpath(wbuffer, sizeof(wbuffer), &hash); + link(buffer, wbuffer); } if (!intrans) @@ -382,6 +404,7 @@ static int fs_delete_key(uint64_t keyid, bool intrans) static char buffer[PATH_MAX]; int ret; struct openpgp_publickey *pk = NULL; + struct skshash hash; struct ll *wordlist = NULL, *wl = NULL; uint64_t *subkeyids = NULL; int i = 0; @@ -427,6 +450,9 @@ static int fs_delete_key(uint64_t keyid, bool intrans) subkeyids = NULL; } + get_skshash(pk, &hash); + skshashpath(buffer, sizeof(buffer), &hash); + unlink(buffer); } keypath(buffer, sizeof(buffer), keyid); @@ -536,6 +562,32 @@ static int fs_fetch_key_text(const char *search, return addedkeys; } +/** + * fetch_key_skshash - Given an SKS hash fetch the key from storage. + * @hash: The hash to fetch. + * @publickey: A pointer to a structure to return the key in. + * @intrans: If we're already in a transaction. + */ +static int fs_fetch_key_skshash(const struct skshash *hash, + struct openpgp_publickey **publickey) +{ + static char buffer[PATH_MAX]; + int ret = 0, fd; + struct openpgp_packet_list *packets = NULL; + + skshashpath(buffer, sizeof(buffer), hash); + if ((fd = open(buffer, O_RDONLY)) != -1) { + read_openpgp_stream(file_fetchchar, &fd, &packets, 0); + parse_keys(packets, publickey); + free_packet_list(packets); + packets = NULL; + close(fd); + ret = 1; + } + + return ret; +} + /** * iterate_keys - call a function once for each key in the db. * @iterfunc: The function to call. @@ -568,6 +620,7 @@ struct dbfuncs keydb_fs_funcs = { .endtrans = fs_endtrans, .fetch_key = fs_fetch_key, .fetch_key_text = fs_fetch_key_text, + .fetch_key_skshash = fs_fetch_key_skshash, .store_key = fs_store_key, .update_keys = generic_update_keys, .delete_key = fs_delete_key, diff --git a/keydb_keyd.c b/keydb_keyd.c index 2a9faaf..ceedd87 100644 --- a/keydb_keyd.c +++ b/keydb_keyd.c @@ -352,6 +352,49 @@ static int keyd_fetch_key_text(const char *search, return 0; } +static int keyd_fetch_key_skshash(const struct skshash *hash, + struct openpgp_publickey **publickey) +{ + struct buffer_ctx keybuf; + struct openpgp_packet_list *packets = NULL; + uint32_t cmd = KEYD_CMD_GETSKSHASH; + ssize_t bytes = 0; + ssize_t count = 0; + + write(keyd_fd, &cmd, sizeof(cmd)); + read(keyd_fd, &cmd, sizeof(cmd)); + if (cmd == KEYD_REPLY_OK) { + write(keyd_fd, hash->hash, sizeof(hash->hash)); + keybuf.offset = 0; + read(keyd_fd, &keybuf.size, sizeof(keybuf.size)); + if (keybuf.size > 0) { + keybuf.buffer = malloc(keybuf.size); + bytes = count = 0; + logthing(LOGTHING_TRACE, + "Getting %d bytes of key data.", + keybuf.size); + while (bytes >= 0 && count < keybuf.size) { + bytes = read(keyd_fd, &keybuf.buffer[count], + keybuf.size - count); + logthing(LOGTHING_TRACE, + "Read %d bytes.", bytes); + count += bytes; + } + read_openpgp_stream(buffer_fetchchar, &keybuf, + &packets, 0); + parse_keys(packets, publickey); + free_packet_list(packets); + packets = NULL; + free(keybuf.buffer); + keybuf.buffer = NULL; + keybuf.size = 0; + } + } + + return (count > 0) ? 1 : 0; +} + + /** * getfullkeyid - Maps a 32bit key id to a 64bit one. * @keyid: The 32bit keyid. @@ -454,6 +497,7 @@ struct dbfuncs keydb_keyd_funcs = { .endtrans = keyd_endtrans, .fetch_key = keyd_fetch_key, .fetch_key_text = keyd_fetch_key_text, + .fetch_key_skshash = keyd_fetch_key_skshash, .store_key = keyd_store_key, .update_keys = generic_update_keys, .delete_key = keyd_delete_key,