From 592a43910706a67048c7d05e45d35dc79712820a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 8 Oct 2014 08:32:00 -0400 Subject: [PATCH] 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 e5a5b54303..f26a6bb216 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 router 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 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 87e49cdff6..7b0c0c7dcf 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(); @@ -99,6 +102,28 @@ HT_GENERATE2(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed, 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)); @@ -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 16a0775208..2a5b3f1786 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 8b82a31d7a..70d075f432 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 97c2b8398d..242ec055c6 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; } }