Add support for key retrieval by SKS hash
authorJonathan McDowell <noodles@earth.li>
Mon, 25 Apr 2011 03:11:42 +0000 (20:11 -0700)
committerJonathan McDowell <noodles@earth.li>
Mon, 25 Apr 2011 03:11:42 +0000 (20:11 -0700)
  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.

keyd.c
keyd.h
keydb.h
keydb_db4.c
keydb_dynamic.c
keydb_fs.c
keydb_keyd.c

diff --git a/keyd.c b/keyd.c
index 5d5b7ba8c883b68326361f7789bbb228e6ac1593..4534a570d263e0840101a3f7f8695fdee8e91a8e 100644 (file)
--- 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 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.
 
        /*
         * Get the command from the client.
@@ -357,6 +358,53 @@ int sock_do(int fd)
                        write(fd, stats,
                                sizeof(*stats));
                        break;
                        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);
                default:
                        logthing(LOGTHING_ERROR, "Got unknown command: %d",
                                        cmd);
diff --git a/keyd.h b/keyd.h
index ce19d8dd13f0c78fda9911fd8e9456ea627da9a6..4313a4f005dd4a96c33e887b425ef5d15d6981b9 100644 (file)
--- a/keyd.h
+++ b/keyd.h
@@ -25,6 +25,7 @@ enum keyd_ops {
        KEYD_CMD_CLOSE,
        KEYD_CMD_QUIT,
        KEYD_CMD_STATS,
        KEYD_CMD_CLOSE,
        KEYD_CMD_QUIT,
        KEYD_CMD_STATS,
+       KEYD_CMD_GETSKSHASH,
        KEYD_CMD_LAST                   /* Placeholder */
 };
 
        KEYD_CMD_LAST                   /* Placeholder */
 };
 
@@ -33,7 +34,7 @@ enum keyd_reply {
        KEYD_REPLY_UNKNOWN_CMD = 1
 };
 
        KEYD_REPLY_UNKNOWN_CMD = 1
 };
 
-static uint32_t keyd_version = 2;
+static uint32_t keyd_version = 3;
 
 struct keyd_stats {
        time_t started;
 
 struct keyd_stats {
        time_t started;
diff --git a/keydb.h b/keydb.h
index b3be902ff3fb4bd37b1d15f84dea03f725b5a4ac..aaca12201aea48bc8233e71c5854d6046dfb5a97 100644 (file)
--- a/keydb.h
+++ b/keydb.h
@@ -104,6 +104,17 @@ struct dbfuncs {
        int (*fetch_key_text)(const char *search,
                        struct openpgp_publickey **publickey);
 
        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.
 /**
  *     update_keys - Takes a list of public keys and updates them in the DB.
  *     @keys: The keys to update in the DB.
index 6fbf9da5392798159cac6e13682ea087f154ed16..eb41f86652fea31ba0ab3384cecb97883e447bc9 100644 (file)
@@ -56,6 +56,11 @@ static DB *worddb = NULL;
  */
 static DB *id32db = NULL;
 
  */
 static DB *id32db = NULL;
 
+/**
+ *     skshashdb - our connection to the SKS hash database.
+ */
+static DB *skshashdb = NULL;
+
 /**
  *     txn - our current transaction id.
  */
 /**
  *     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 (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;
                if (id32db != NULL) {
                        id32db->close(id32db, 0);
                        id32db = NULL;
@@ -255,6 +264,18 @@ static int db4_upgradedb(int numdb)
                        db_strerror(ret));
        }
 
                        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);
        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();
        }
        if (txn != NULL) {
                db4_endtrans();
        }
@@ -722,6 +764,45 @@ static int db4_fetch_key_text(const char *search,
        return (numkeys);
 }
 
        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.
 /**
  *     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 ll *wordlist = NULL;
        struct ll *curword  = NULL;
        bool deadlock = false;
+       struct skshash hash;
 
        if (!intrans) {
                db4_starttrans();
 
        if (!intrans) {
                db4_starttrans();
@@ -912,6 +994,38 @@ static int db4_delete_key(uint64_t keyid, bool intrans)
                        free(subkeyids);
                        subkeyids = NULL;
                }
                        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;
 
                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 ll *wordlist = NULL;
        struct ll *curword  = NULL;
        bool       deadlock = false;
+       struct skshash hash;
 
        keyid = get_keyid(publickey);
 
 
        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();
        }
        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,
        .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,
        .store_key              = db4_store_key,
        .update_keys            = generic_update_keys,
        .delete_key             = db4_delete_key,
index fa4b13b9fb6f9c01039e3246ad27360c7aad8c9f..5c2d3d58f5484e0941026aad01769037789532bc 100644 (file)
@@ -198,6 +198,23 @@ static int dynamic_fetch_key_text(const char *search,
        return -1;
 }
 
        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)
 {
 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,
        .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,
        .store_key              = dynamic_store_key,
        .update_keys            = dynamic_update_keys,
        .delete_key             = dynamic_delete_key,
index 41d55cf07677c531bb004f591e7162916b4d3f0d..02adf6bae1972b069c393581f92fcea3fde1ee1d 100644 (file)
@@ -126,6 +126,19 @@ static void subkeypath(char *buffer, size_t length, uint64_t subkey,
                 keyid);
 }
 
                 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",
 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 openpgp_publickey *next = NULL;
        uint64_t keyid = get_keyid(publickey);
        struct ll *wordlist = NULL, *wl = NULL;
+       struct skshash hash;
        uint64_t *subkeyids = NULL;
        uint64_t *subkeyids = NULL;
+       uint32_t hashid;
        int i = 0;
 
 
        int i = 0;
 
 
@@ -365,6 +380,13 @@ static int fs_store_key(struct openpgp_publickey *publickey, bool intrans,
                        free(subkeyids);
                        subkeyids = NULL;
                }
                        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)
        }
 
        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;
        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;
        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;
                }
 
                        subkeyids = NULL;
                }
 
+               get_skshash(pk, &hash);
+               skshashpath(buffer, sizeof(buffer), &hash);
+               unlink(buffer);
        }
 
        keypath(buffer, sizeof(buffer), keyid);
        }
 
        keypath(buffer, sizeof(buffer), keyid);
@@ -536,6 +562,32 @@ static int fs_fetch_key_text(const char *search,
        return addedkeys;
 }
 
        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.
 /**
  *     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,
        .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,
        .store_key              = fs_store_key,
        .update_keys            = generic_update_keys,
        .delete_key             = fs_delete_key,
index 2a9faafa40cc72c9a21d0fcb698819e94a6c5345..ceedd87a84de1337c36524d4a8012fd4464557b8 100644 (file)
@@ -352,6 +352,49 @@ static int keyd_fetch_key_text(const char *search,
        return 0;
 }
 
        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.
 /**
  *     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,
        .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,
        .store_key              = keyd_store_key,
        .update_keys            = generic_update_keys,
        .delete_key             = keyd_delete_key,