commit 592a43910706a67048c7d05e45d35dc79712820a Author: Nick Mathewson nickm@torproject.org Date: Wed Oct 8 08:32:00 2014 -0400
Tie key-pinning logic into directory authority operation
With this patch: * Authorities load the key-pinning log at startup. * Authorities open a key-pinning log for writing at startup. * Authorities reject any router with an ed25519 key where they have previously seen that ed25519 key with a different RSA key, or vice versa. * Authorities warn about, but *do not* reject, RSA-only descriptors when the RSA key has previously gone along with an Ed25519 key. (We should make this a 'reject' too, but we can't do that until we're sure there's no legit reason to downgrade to 0.2.5.) --- src/or/dirserv.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/or/keypin.c | 28 ++++++++++++++++++++++++ src/or/keypin.h | 2 ++ src/or/main.c | 19 ++++++++++++++++ src/or/router.c | 3 ++- 5 files changed, 115 insertions(+), 1 deletion(-)
diff --git a/src/or/dirserv.c b/src/or/dirserv.c index e5a5b54..f26a6bb 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -18,6 +18,7 @@ #include "dirserv.h" #include "dirvote.h" #include "hibernate.h" +#include "keypin.h" #include "microdesc.h" #include "networkstatus.h" #include "nodelist.h" @@ -27,6 +28,7 @@ #include "routerlist.h" #include "routerparse.h" #include "routerset.h" +#include "torcert.h"
/** * \file dirserv.c @@ -225,6 +227,16 @@ dirserv_load_fingerprint_file(void) return 0; }
+/* If this is set, then we don't allow routers that have advertised an Ed25519 + * identity to stop doing so. This is going to be essential for good identity + * security: otherwise anybody who can attack RSA-1024 but not Ed25519 could + * just sign fake descriptors missing the Ed25519 key. But we won't actually + * be able to prevent that kind of thing until we're confident that there + * isn't actually a legit reason to downgrade to 0.2.5. So for now, we have + * to leave this #undef. + */ +#undef DISABLE_DISABLING_ED25519 + /** Check whether <b>router</b> has a nickname/identity key combination that * we recognize from the fingerprint list, or an IP we automatically act on * according to our configuration. Return the appropriate router status. @@ -243,6 +255,36 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg) return FP_REJECT; }
+ if (router->signing_key_cert) { + /* This has an ed25519 identity key. */ + if (KEYPIN_MISMATCH == + keypin_check((const uint8_t*)router->cache_info.identity_digest, + router->signing_key_cert->signing_key.pubkey)) { + if (msg) { + *msg = "Ed25519 identity key or RSA identity key has changed."; + } + log_warn(LD_DIR, "Router %s uploaded a descriptor with a Ed25519 key " + "but the <rsa,ed25519> keys don't match what they were before.", + router_describe(router)); + return FP_REJECT; + } + } else { + /* No ed25519 key */ + if (KEYPIN_MISMATCH == keypin_check_lone_rsa( + (const uint8_t*)router->cache_info.identity_digest)) { + log_warn(LD_DIR, "Router %s uploaded a descriptor with no Ed25519 key, " + "when we previously knew an Ed25519 for it. Ignoring for now, " + "since Tor 0.2.6 is under development.", + router_describe(router)); +#ifdef DISABLE_DISABLING_ED25519 + if (msg) { + *msg = "Ed25519 identity key has disappeared."; + } + return FP_REJECT; +#endif + } + } + return dirserv_get_status_impl(d, router->nickname, router->addr, router->or_port, router->platform, msg, 1); @@ -578,6 +620,28 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source) return ROUTER_IS_ALREADY_KNOWN; }
+ /* Do keypinning again ... this time, to add the pin if appropriate */ + int keypin_status; + if (ri->signing_key_cert) { + keypin_status = keypin_check_and_add( + (const uint8_t*)ri->cache_info.identity_digest, + ri->signing_key_cert->signing_key.pubkey); + } else { + keypin_status = keypin_check_lone_rsa( + (const uint8_t*)ri->cache_info.identity_digest); +#ifndef DISABLE_DISABLING_ED25519 + if (keypin_status == KEYPIN_MISMATCH) + keypin_status = KEYPIN_NOT_FOUND; +#endif + } + if (keypin_status == KEYPIN_MISMATCH) { + log_info(LD_DIRSERV, "Dropping descriptor from %s (source: %s) because " + "its key did not match an older RSA/Ed25519 keypair", + router_describe(ri), source); + *msg = "Looks like your keypair does not match its older value."; + return ROUTER_AUTHDIR_REJECTS; + } + /* Make a copy of desc, since router_add_to_routerlist might free * ri and its associated signed_descriptor_t. */ desc = tor_strndup(ri->cache_info.signed_descriptor_body, desclen); diff --git a/src/or/keypin.c b/src/or/keypin.c index 87e49cd..7b0c0c7 100644 --- a/src/or/keypin.c +++ b/src/or/keypin.c @@ -44,6 +44,9 @@
static int keypin_journal_append_entry(const uint8_t *rsa_id_digest, const uint8_t *ed25519_id_key); +static int keypin_check_and_add_impl(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key, + int do_not_add);
static HT_HEAD(rsamap, keypin_ent_st) the_rsa_map = HT_INITIALIZER(); static HT_HEAD(edmap, keypin_ent_st) the_ed_map = HT_INITIALIZER(); @@ -100,6 +103,28 @@ int keypin_check_and_add(const uint8_t *rsa_id_digest, const uint8_t *ed25519_id_key) { + return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 0); +} + +/** + * As keypin_check_and_add, but do not add. Return KEYPIN_NOT_FOUND if + * we would add. + */ +int +keypin_check(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key) +{ + return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 1); +} + +/** + * Helper: implements keypin_check and keypin_check_and_add. + */ +static int +keypin_check_and_add_impl(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key, + int do_not_add) +{ keypin_ent_t search, *ent; memset(&search, 0, sizeof(search)); memcpy(search.rsa_id, rsa_id_digest, sizeof(search.rsa_id)); @@ -127,6 +152,9 @@ keypin_check_and_add(const uint8_t *rsa_id_digest, }
/* Okay, this one is new to us. */ + if (do_not_add) + return KEYPIN_NOT_FOUND; + ent = tor_memdup(&search, sizeof(search)); keypin_add_entry_to_map(ent); keypin_journal_append_entry(rsa_id_digest, ed25519_id_key); diff --git a/src/or/keypin.h b/src/or/keypin.h index 16a0775..2a5b3f1 100644 --- a/src/or/keypin.h +++ b/src/or/keypin.h @@ -8,6 +8,8 @@
int keypin_check_and_add(const uint8_t *rsa_id_digest, const uint8_t *ed25519_id_key); +int keypin_check(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key);
int keypin_open_journal(const char *fname); int keypin_close_journal(void); diff --git a/src/or/main.c b/src/or/main.c index 8b82a31..70d075f 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -37,6 +37,7 @@ #include "entrynodes.h" #include "geoip.h" #include "hibernate.h" +#include "keypin.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" @@ -1998,6 +1999,23 @@ do_main_loop(void) /* initialize the bootstrap status events to know we're starting up */ control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0);
+ /* Initialize the keypinning log. */ + if (authdir_mode_v3(get_options())) { + char *fname = get_datadir_fname("key-pinning-entries"); + int r = 0; + if (keypin_load_journal(fname)<0) { + log_err(LD_DIR, "Error loading key-pinning journal: %s",strerror(errno)); + r = -1; + } + if (keypin_open_journal(fname)<0) { + log_err(LD_DIR, "Error opening key-pinning journal: %s",strerror(errno)); + r = -1; + } + tor_free(fname); + if (r) + return r; + } + if (trusted_dirs_reload_certs()) { log_warn(LD_DIR, "Couldn't load all cached v3 certificates. Starting anyway."); @@ -2707,6 +2725,7 @@ tor_cleanup(void) or_state_save(now); if (authdir_mode_tests_reachability(options)) rep_hist_record_mtbf_data(now, 0); + keypin_close_journal(); } #ifdef USE_DMALLOC dmalloc_log_stats(); diff --git a/src/or/router.c b/src/or/router.c index 97c2b83..242ec05 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -2343,7 +2343,8 @@ router_dump_router_to_string(routerinfo_t *router, !ed25519_pubkey_eq(&router->signing_key_cert->signed_key, &signing_keypair->pubkey)) { log_warn(LD_BUG, "Tried to sign a router descriptor with a mismatched " - "ed25519 key chain"); + "ed25519 key chain %d", + router->signing_key_cert->signing_key_included); goto err; } }