From a9be768959c189846178723d5fe44d3b59b0d983 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 31 May 2017 18:33:38 -0400 Subject: [PATCH 1/9] Bugfix: Regenerate more certificates when appropriate Previously we could sometimes change our signing key, but not regenerate the certificates (signing->link and signing->auth) that were signed with it. Also, we would regularly replace our TLS x.509 link certificate (by rotating our TLS context) but not replace our signing->link ed25519 certificate. In both cases, the resulting inconsistency would make other relays reject our link handshakes. Fixes two cases of bug 22460; bugfix on 0.3.0.1-alpha. --- changes/bug22460_case1 | 10 ++++++++++ src/or/main.c | 17 +++++++++++----- src/or/router.c | 5 +++-- src/or/routerkeys.c | 37 +++++++++++++++++++++++++++++------ src/or/routerkeys.h | 2 +- src/test/test_routerkeys.c | 20 +++++++++---------- src/test/test_shared_random.c | 4 ++-- 7 files changed, 69 insertions(+), 26 deletions(-) create mode 100644 changes/bug22460_case1 diff --git a/changes/bug22460_case1 b/changes/bug22460_case1 new file mode 100644 index 000000000..9aef46b21 --- /dev/null +++ b/changes/bug22460_case1 @@ -0,0 +1,10 @@ + o Major bugfixes (relays, key management): + - Regenerate link and authentication certificates whenever the key that + signs them changes; also, regenerate link certificates whenever the + signed key changes. Previously, these processes were only weakly + coupled, and we relays could (for minutes to hours) wind up with an + inconsistent set of keys and certificates, which other relays + would not accept. Fixes two cases of bug 22460; bugfix on + 0.3.0.1-alpha. + + diff --git a/src/or/main.c b/src/or/main.c index bc7b3db2b..3139381f3 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1506,8 +1506,9 @@ check_ed_keys_callback(time_t now, const or_options_t *options) { if (server_mode(options)) { if (should_make_new_ed_keys(options, now)) { - if (load_ed_keys(options, now) < 0 || - generate_ed_link_cert(options, now)) { + int new_signing_key = load_ed_keys(options, now); + if (new_signing_key < 0 || + generate_ed_link_cert(options, now, new_signing_key > 0)) { log_err(LD_OR, "Unable to update Ed25519 keys! Exiting."); tor_cleanup(); exit(0); @@ -1559,6 +1560,11 @@ rotate_x509_certificate_callback(time_t now, const or_options_t *options) log_err(LD_BUG, "Error reinitializing TLS context"); tor_assert_unreached(); } + if (generate_ed_link_cert(options, now, 1)) { + log_err(LD_OR, "Unable to update Ed25519->TLS link certificate for " + "new TLS context."); + tor_assert_unreached(); + } /* We also make sure to rotate the TLS connections themselves if they've * been up for too long -- but that's done via is_bad_for_new_circs in @@ -2298,8 +2304,9 @@ do_hup(void) /* Maybe we've been given a new ed25519 key or certificate? */ time_t now = approx_time(); - if (load_ed_keys(options, now) < 0 || - generate_ed_link_cert(options, now)) { + int new_signing_key = load_ed_keys(options, now); + if (new_signing_key < 0 || + generate_ed_link_cert(options, now, new_signing_key > 0)) { log_warn(LD_OR, "Problem reloading Ed25519 keys; still using old keys."); } @@ -3627,7 +3634,7 @@ tor_main(int argc, char *argv[]) result = do_main_loop(); break; case CMD_KEYGEN: - result = load_ed_keys(get_options(), time(NULL)); + result = load_ed_keys(get_options(), time(NULL)) < 0; break; case CMD_LIST_FINGERPRINT: result = do_list_fingerprint(); diff --git a/src/or/router.c b/src/or/router.c index e4fa72a28..f6b03cde2 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -906,7 +906,8 @@ init_keys(void) } /* 1d. Load all ed25519 keys */ - if (load_ed_keys(options,now) < 0) + const int new_signing_key = load_ed_keys(options,now); + if (new_signing_key < 0) return -1; /* 2. Read onion key. Make it if none is found. */ @@ -976,7 +977,7 @@ init_keys(void) /* 3b. Get an ed25519 link certificate. Note that we need to do this * after we set up the TLS context */ - if (generate_ed_link_cert(options, now) < 0) { + if (generate_ed_link_cert(options, now, new_signing_key > 0) < 0) { log_err(LD_GENERAL,"Couldn't make link cert"); return -1; } diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 6259e3f5a..1f0f82a18 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -672,6 +672,9 @@ static size_t rsa_ed_crosscert_len = 0; /** * Running as a server: load, reload, or refresh our ed25519 keys and * certificates, creating and saving new ones as needed. + * + * Return -1 on failure; 0 on success if the signing key was not replaced; + * and 1 on success if the signing key was replaced. */ int load_ed_keys(const or_options_t *options, time_t now) @@ -684,6 +687,7 @@ load_ed_keys(const or_options_t *options, time_t now) const tor_cert_t *check_signing_cert = NULL; tor_cert_t *sign_cert = NULL; tor_cert_t *auth_cert = NULL; + int signing_key_changed = 0; #define FAIL(msg) do { \ log_warn(LD_OR, (msg)); \ @@ -719,7 +723,23 @@ load_ed_keys(const or_options_t *options, time_t now) use_signing = sign; } + if (use_signing) { + /* We loaded a signing key with its certificate. */ + if (! master_signing_key) { + /* We didn't know one before! */ + signing_key_changed = 1; + } else if (! ed25519_pubkey_eq(&use_signing->pubkey, + &master_signing_key->pubkey) || + ! tor_memeq(use_signing->seckey.seckey, + master_signing_key->seckey.seckey, + ED25519_SECKEY_LEN)) { + /* We loaded a different signing key than the one we knew before. */ + signing_key_changed = 1; + } + } + if (!use_signing && master_signing_key) { + /* We couldn't load a signing key, but we already had one loaded */ check_signing_cert = signing_key_cert; use_signing = master_signing_key; } @@ -879,6 +899,7 @@ load_ed_keys(const or_options_t *options, time_t now) if (!sign) FAIL("Missing signing key"); use_signing = sign; + signing_key_changed = 1; tor_assert(sign_cert->signing_key_included); tor_assert(ed25519_pubkey_eq(&sign_cert->signing_key, &id->pubkey)); @@ -910,6 +931,7 @@ load_ed_keys(const or_options_t *options, time_t now) } if (!current_auth_key || + signing_key_changed || EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop)) { auth = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT, now, @@ -937,7 +959,7 @@ load_ed_keys(const or_options_t *options, time_t now) SET_CERT(auth_key_cert, auth_cert); } - return 0; + return signing_key_changed; err: ed25519_keypair_free(id); ed25519_keypair_free(sign); @@ -951,16 +973,18 @@ load_ed_keys(const or_options_t *options, time_t now) * Retrieve our currently-in-use Ed25519 link certificate and id certificate, * and, if they would expire soon (based on the time now, generate new * certificates (without embedding the public part of the signing key inside). + * If force is true, always generate a new certificate. * - * The signed_key from the expiring certificate will be used to sign the new - * key within newly generated X509 certificate. + * The signed_key from the current id->signing certificate will be used to + * sign the new key within newly generated X509 certificate. * * Returns -1 upon error. Otherwise, returns 0 upon success (either when the * current certificate is still valid, or when a new certificate was * successfully generated). */ int -generate_ed_link_cert(const or_options_t *options, time_t now) +generate_ed_link_cert(const or_options_t *options, time_t now, + int force) { const tor_x509_cert_t *link_ = NULL, *id = NULL; tor_cert_t *link_cert = NULL; @@ -972,7 +996,8 @@ generate_ed_link_cert(const or_options_t *options, time_t now) const common_digests_t *digests = tor_x509_cert_get_cert_digests(link_); - if (link_cert_cert && + if (force == 0 && + link_cert_cert && ! EXPIRES_SOON(link_cert_cert, options->TestingLinkKeySlop) && fast_memeq(digests->d[DIGEST_SHA256], link_cert_cert->signed_key.pubkey, DIGEST256_LEN)) { @@ -1073,7 +1098,7 @@ init_mock_ed_keys(const crypto_pk_t *rsa_identity_key) MAKECERT(auth_key_cert, master_signing_key, current_auth_key, CERT_TYPE_SIGNING_AUTH, 0); - if (generate_ed_link_cert(get_options(), time(NULL)) < 0) { + if (generate_ed_link_cert(get_options(), time(NULL), 0) < 0) { log_warn(LD_BUG, "Couldn't make link certificate"); goto err; } diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h index d2027f4bb..845abb4c7 100644 --- a/src/or/routerkeys.h +++ b/src/or/routerkeys.h @@ -66,7 +66,7 @@ MOCK_DECL(int, check_tap_onion_key_crosscert,(const uint8_t *crosscert, int load_ed_keys(const or_options_t *options, time_t now); int should_make_new_ed_keys(const or_options_t *options, const time_t now); -int generate_ed_link_cert(const or_options_t *options, time_t now); +int generate_ed_link_cert(const or_options_t *options, time_t now, int force); int read_encrypted_secret_key(ed25519_secret_key_t *out, const char *fname); diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 64692d28a..13059267a 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -450,8 +450,8 @@ test_routerkeys_ed_keys_init_all(void *arg) options->DataDirectory = dir; - tt_int_op(0, ==, load_ed_keys(options, now)); - tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_int_op(1, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now, 0)); tt_assert(get_master_identity_key()); tt_assert(get_master_identity_key()); tt_assert(get_master_signing_keypair()); @@ -466,7 +466,7 @@ test_routerkeys_ed_keys_init_all(void *arg) /* Call load_ed_keys again, but nothing has changed. */ tt_int_op(0, ==, load_ed_keys(options, now)); - tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); tt_mem_op(&auth, ==, get_current_auth_keypair(), sizeof(auth)); @@ -474,8 +474,8 @@ test_routerkeys_ed_keys_init_all(void *arg) /* Force a reload: we make new link/auth keys. */ routerkeys_free_all(); - tt_int_op(0, ==, load_ed_keys(options, now)); - tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_int_op(1, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); tt_assert(tor_cert_eq(link_cert, get_current_link_cert_cert())); @@ -489,7 +489,7 @@ test_routerkeys_ed_keys_init_all(void *arg) /* Force a link/auth-key regeneration by advancing time. */ tt_int_op(0, ==, load_ed_keys(options, now+3*86400)); - tt_int_op(0, ==, generate_ed_link_cert(options, now+3*86400)); + tt_int_op(0, ==, generate_ed_link_cert(options, now+3*86400, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert())); @@ -502,8 +502,8 @@ test_routerkeys_ed_keys_init_all(void *arg) memcpy(&auth, get_current_auth_keypair(), sizeof(auth)); /* Force a signing-key regeneration by advancing time. */ - tt_int_op(0, ==, load_ed_keys(options, now+100*86400)); - tt_int_op(0, ==, generate_ed_link_cert(options, now+100*86400)); + tt_int_op(1, ==, load_ed_keys(options, now+100*86400)); + tt_int_op(0, ==, generate_ed_link_cert(options, now+100*86400, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, !=, get_master_signing_keypair(), sizeof(sign)); tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert())); @@ -520,8 +520,8 @@ test_routerkeys_ed_keys_init_all(void *arg) routerkeys_free_all(); unlink(get_fname("test_ed_keys_init_all/keys/" "ed25519_master_id_secret_key")); - tt_int_op(0, ==, load_ed_keys(options, now)); - tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_int_op(1, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert())); diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c index d511f163e..026a0f382 100644 --- a/src/test/test_shared_random.c +++ b/src/test/test_shared_random.c @@ -48,7 +48,7 @@ init_authority_state(void) mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL); tt_assert(mock_cert); options->AuthoritativeDir = 1; - tt_int_op(0, ==, load_ed_keys(options, time(NULL))); + tt_int_op(load_ed_keys(options, time(NULL)), OP_GE, 0); sr_state_init(0, 0); /* It's possible a commit has been generated in our state depending on * the phase we are currently in which uses "now" as the starting @@ -286,7 +286,7 @@ test_sr_commit(void *arg) tt_assert(auth_cert); options->AuthoritativeDir = 1; - tt_int_op(0, ==, load_ed_keys(options, now)); + tt_int_op(load_ed_keys(options, time(NULL)), OP_GE, 0); } /* Generate our commit object and validate it has the appropriate field From 34a6755b94015fcbc838b46b54667899c238ac04 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 1 Jun 2017 09:26:24 -0400 Subject: [PATCH 2/9] Fix ed25519 link certificate race on tls context rotation Whenever we rotate our TLS context, we change our Ed25519 Signing->Link certificate. But if we've already started a TLS connection, then we've already sent the old X509 link certificate, so the new Ed25519 Signing->Link certificate won't match it. To fix this, we now store a copy of the Signing->Link certificate when we initialize the handshake state, and send that certificate as part of our CERTS cell. Fixes one case of bug22460; bugfix on 0.3.0.1-alpha. --- changes/bug22460_case1 | 6 ++++++ src/or/connection_or.c | 6 +++++- src/or/or.h | 6 ++++++ src/test/test_link_handshake.c | 7 +++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/changes/bug22460_case1 b/changes/bug22460_case1 index 9aef46b21..cfe78ad79 100644 --- a/changes/bug22460_case1 +++ b/changes/bug22460_case1 @@ -6,5 +6,11 @@ inconsistent set of keys and certificates, which other relays would not accept. Fixes two cases of bug 22460; bugfix on 0.3.0.1-alpha. + - When sending an Ed25519 signing->link certificate in a CERTS cell, + send the certificate that matches the x509 certificate that we used + on the TLS connection. Previously, there was a race condition if + the TLS context rotated after we began the TLS handshake but + before we sent the CERTS cell. Fixes a case of bug 22460; bugfix + on 0.3.0.1-alpha. diff --git a/src/or/connection_or.c b/src/or/connection_or.c index cefe42c4d..0966ec8ac 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1855,6 +1855,9 @@ connection_init_or_handshake_state(or_connection_t *conn, int started_here) s->started_here = started_here ? 1 : 0; s->digest_sent_data = 1; s->digest_received_data = 1; + if (! started_here && get_current_link_cert_cert()) { + s->own_link_cert = tor_cert_dup(get_current_link_cert_cert()); + } s->certs = or_handshake_certs_new(); s->certs->started_here = s->started_here; return 0; @@ -1869,6 +1872,7 @@ or_handshake_state_free(or_handshake_state_t *state) crypto_digest_free(state->digest_sent); crypto_digest_free(state->digest_received); or_handshake_certs_free(state->certs); + tor_cert_free(state->own_link_cert); memwipe(state, 0xBE, sizeof(or_handshake_state_t)); tor_free(state); } @@ -2311,7 +2315,7 @@ connection_or_send_certs_cell(or_connection_t *conn) if (conn_in_server_mode) { add_ed25519_cert(certs_cell, CERTTYPE_ED_SIGN_LINK, - get_current_link_cert_cert()); + conn->handshake_state->own_link_cert); } else { add_ed25519_cert(certs_cell, CERTTYPE_ED_SIGN_AUTH, diff --git a/src/or/or.h b/src/or/or.h index 0db9f2360..50e6e3e71 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1449,6 +1449,12 @@ typedef struct or_handshake_state_t { /* True iff we have sent a netinfo cell */ unsigned int sent_netinfo : 1; + /** The signing->ed25519 link certificate corresponding to the x509 + * certificate we used on the TLS connection (if this is a server-side + * connection). We make a copy of this here to prevent a race condition + * caused by TLS context rotation. */ + struct tor_cert_st *own_link_cert; + /** True iff we should feed outgoing cells into digest_sent and * digest_received respectively. * diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index 421f3aaed..d1b9c65af 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -892,6 +892,11 @@ test_link_handshake_send_authchallenge(void *arg) or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET); var_cell_t *cell1=NULL, *cell2=NULL; + crypto_pk_t *rsa0 = pk_generate(0), *rsa1 = pk_generate(1); + tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + rsa0, rsa1, 86400), ==, 0); + init_mock_ed_keys(rsa0); + MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); tt_int_op(connection_init_or_handshake_state(c1, 0), ==, 0); @@ -917,6 +922,8 @@ test_link_handshake_send_authchallenge(void *arg) connection_free_(TO_CONN(c1)); tor_free(cell1); tor_free(cell2); + crypto_pk_free(rsa0); + crypto_pk_free(rsa1); } typedef struct authchallenge_data_s { From e5bdfd66cf03e8410c511f6dcf309142c64c85f5 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 5 Jun 2017 09:35:55 -0400 Subject: [PATCH 3/9] Make code more clear about own_link_cert safety It's okay to call add_ed25519_cert with a NULL argument: so, document that. Also, add a tor_assert_nonfatal() to catch any case where we have failed to set own_link_cert when conn_in_server_mode. --- src/or/connection_or.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 0966ec8ac..9c806d1be 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -2238,7 +2238,8 @@ add_certs_cell_cert_helper(certs_cell_t *certs_cell, /** Add an encoded X509 cert (stored as cert_len bytes at * cert_encoded) to the trunnel certs_cell_t object that we are - * building in certs_cell. Set its type field to cert_type. */ + * building in certs_cell. Set its type field to cert_type. + * (If cert is NULL, take no action.) */ static void add_x509_cert(certs_cell_t *certs_cell, uint8_t cert_type, @@ -2256,7 +2257,7 @@ add_x509_cert(certs_cell_t *certs_cell, /** Add an Ed25519 cert from cert to the trunnel certs_cell_t object * that we are building in certs_cell. Set its type field to - * cert_type. */ + * cert_type. (If cert is NULL, take no action.) */ static void add_ed25519_cert(certs_cell_t *certs_cell, uint8_t cert_type, @@ -2313,6 +2314,7 @@ connection_or_send_certs_cell(or_connection_t *conn) CERTTYPE_ED_ID_SIGN, get_master_signing_key_cert()); if (conn_in_server_mode) { + tor_assert_nonfatal(conn->handshake_state->own_link_cert); add_ed25519_cert(certs_cell, CERTTYPE_ED_SIGN_LINK, conn->handshake_state->own_link_cert); From 50facb40bb42e070b858ca052edccd0f3a5b83b5 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 31 May 2017 19:12:32 -0400 Subject: [PATCH 4/9] On v3 link handshake, send the correct link certificate Previously we'd send the _current_ link certificate, which would cause a handshaking failure when the TLS context rotated. --- src/common/tortls.c | 18 ++++++++++++++++++ src/common/tortls.h | 1 + src/or/connection_or.c | 30 ++++++++++++++++++------------ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index 62ed5be34..055b4686e 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -2021,6 +2021,24 @@ tor_tls_get_peer_cert,(tor_tls_t *tls)) return tor_x509_cert_new(cert); } +/** Return the cerficate we used on the connection, or NULL if somehow + * we didn't use one. */ +tor_x509_cert_t * +tor_tls_get_own_cert(tor_tls_t *tls) +{ + X509 *cert = SSL_get_certificate(tls->ssl); + tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, + "getting own-connection certificate"); + if (!cert) + return NULL; + /* Fun inconsistency: SSL_get_peer_certificate increments the reference + * count, but SSL_get_certificate does not. */ + X509 *duplicate = X509_dup(cert); + if (BUG(duplicate == NULL)) + return NULL; + return tor_x509_cert_new(duplicate); +} + /** Warn that a certificate lifetime extends through a certain range. */ static void log_cert_lifetime(int severity, const X509 *cert, const char *problem) diff --git a/src/common/tortls.h b/src/common/tortls.h index 7c035a2cd..ae11b7ab6 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -198,6 +198,7 @@ int tor_tls_is_server(tor_tls_t *tls); void tor_tls_free(tor_tls_t *tls); int tor_tls_peer_has_cert(tor_tls_t *tls); MOCK_DECL(tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls)); +tor_x509_cert_t *tor_tls_get_own_cert(tor_tls_t *tls); int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity); int tor_tls_check_lifetime(int severity, tor_tls_t *tls, int past_tolerance, diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 267c32dda..3b35d5e34 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -2137,7 +2137,9 @@ connection_or_send_netinfo,(or_connection_t *conn)) int connection_or_send_certs_cell(or_connection_t *conn) { - const tor_x509_cert_t *link_cert = NULL, *id_cert = NULL; + const tor_x509_cert_t *global_link_cert = NULL, *id_cert = NULL, + *using_link_cert = NULL; + tor_x509_cert_t *own_link_cert = NULL; const uint8_t *link_encoded = NULL, *id_encoded = NULL; size_t link_len, id_len; var_cell_t *cell; @@ -2149,9 +2151,15 @@ connection_or_send_certs_cell(or_connection_t *conn) if (! conn->handshake_state) return -1; const int conn_in_server_mode = ! conn->handshake_state->started_here; - if (tor_tls_get_my_certs(conn_in_server_mode, &link_cert, &id_cert) < 0) + if (tor_tls_get_my_certs(conn_in_server_mode, + &global_link_cert, &id_cert) < 0) return -1; - tor_x509_cert_get_der(link_cert, &link_encoded, &link_len); + if (conn_in_server_mode) { + using_link_cert = own_link_cert = tor_tls_get_own_cert(conn->tls); + } else { + using_link_cert = global_link_cert; + } + tor_x509_cert_get_der(using_link_cert, &link_encoded, &link_len); tor_x509_cert_get_der(id_cert, &id_encoded, &id_len); cell_len = 1 /* 1 byte: num certs in cell */ + @@ -2179,6 +2187,7 @@ connection_or_send_certs_cell(or_connection_t *conn) connection_or_write_var_cell_to_buf(cell, conn); var_cell_free(cell); + tor_x509_cert_free(own_link_cert); return 0; } @@ -2258,10 +2267,10 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, memcpy(auth1_getarray_type(auth), "AUTH0001", 8); { - const tor_x509_cert_t *id_cert=NULL, *link_cert=NULL; + const tor_x509_cert_t *id_cert=NULL; const common_digests_t *my_digests, *their_digests; const uint8_t *my_id, *their_id, *client_id, *server_id; - if (tor_tls_get_my_certs(server, &link_cert, &id_cert)) + if (tor_tls_get_my_certs(server, NULL, &id_cert)) goto err; my_digests = tor_x509_cert_get_id_digests(id_cert); their_digests = @@ -2300,13 +2309,11 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, { /* Digest of cert used on TLS link : 32 octets. */ - const tor_x509_cert_t *cert = NULL; - tor_x509_cert_t *freecert = NULL; + tor_x509_cert_t *cert = NULL; if (server) { - tor_tls_get_my_certs(1, &cert, NULL); + cert = tor_tls_get_own_cert(conn->tls); } else { - freecert = tor_tls_get_peer_cert(conn->tls); - cert = freecert; + cert = tor_tls_get_peer_cert(conn->tls); } if (!cert) { log_warn(LD_OR, "Unable to find cert when making AUTH1 data."); @@ -2316,8 +2323,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, memcpy(auth->scert, tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); - if (freecert) - tor_x509_cert_free(freecert); + tor_x509_cert_free(cert); } /* HMAC of clientrandom and serverrandom using master key : 32 octets */ From 39b7e89c28efda628d57640302276b7f04b2c0de Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 1 Jun 2017 08:55:54 -0400 Subject: [PATCH 5/9] Test prerequisites: function to dup a cert, make get_own_cert mockable. --- src/common/tortls.c | 13 +++++++++++-- src/common/tortls.h | 3 ++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index 055b4686e..1661b7ef5 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -677,6 +677,15 @@ MOCK_IMPL(STATIC tor_x509_cert_t *, return cert; } +/** Return a new copy of cert. */ +tor_x509_cert_t * +tor_x509_cert_dup(const tor_x509_cert_t *cert) +{ + tor_assert(cert); + X509 *x509 = cert->cert; + return tor_x509_cert_new(X509_dup(x509)); +} + /** Read a DER-encoded X509 cert, of length exactly certificate_len, * from a certificate. Return a newly allocated tor_x509_cert_t on * success and NULL on failure. */ @@ -2023,8 +2032,8 @@ tor_tls_get_peer_cert,(tor_tls_t *tls)) /** Return the cerficate we used on the connection, or NULL if somehow * we didn't use one. */ -tor_x509_cert_t * -tor_tls_get_own_cert(tor_tls_t *tls) +MOCK_IMPL(tor_x509_cert_t *, +tor_tls_get_own_cert,(tor_tls_t *tls)) { X509 *cert = SSL_get_certificate(tls->ssl); tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, diff --git a/src/common/tortls.h b/src/common/tortls.h index ae11b7ab6..f018c45c8 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -197,8 +197,9 @@ void tor_tls_set_renegotiate_callback(tor_tls_t *tls, int tor_tls_is_server(tor_tls_t *tls); void tor_tls_free(tor_tls_t *tls); int tor_tls_peer_has_cert(tor_tls_t *tls); +tor_x509_cert_t *tor_x509_cert_dup(const tor_x509_cert_t *cert); MOCK_DECL(tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls)); -tor_x509_cert_t *tor_tls_get_own_cert(tor_tls_t *tls); +MOCK_DECL(tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls)); int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity); int tor_tls_check_lifetime(int severity, tor_tls_t *tls, int past_tolerance, From 8e9392c2670d949517f688d2d7c6320251bf6117 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 1 Jun 2017 08:56:10 -0400 Subject: [PATCH 6/9] Repair link_handshake unit tests to mock tor_tls_get_own_cert() The tests previously assumed that the link handshake code would be calling get_my_certs() -- when I changed it to call get_own_cert() instead for the (case 2) 22460 fix, the tests failed, since the tls connection wasn't really there. This change makes us start mocking out the tor_tls_get_own_cert() function too. It also corrects the behavior of the mock_get_peer_cert() function -- it should have been returning a newly allocated copy. --- src/test/test_link_handshake.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index 6c0567098..b97948596 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -66,6 +66,14 @@ mock_send_authenticate(or_connection_t *conn, int type) return 0; } +static tor_x509_cert_t *mock_own_cert = NULL; +static tor_x509_cert_t * +mock_get_own_cert(tor_tls_t *tls) +{ + (void)tls; + return tor_x509_cert_dup(mock_own_cert); +} + /* Test good certs cells */ static void test_link_handshake_certs_ok(void *arg) @@ -84,6 +92,7 @@ test_link_handshake_certs_ok(void *arg) MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key); MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); MOCK(connection_or_send_netinfo, mock_send_netinfo); + MOCK(tor_tls_get_own_cert, mock_get_own_cert); key1 = pk_generate(2); key2 = pk_generate(3); @@ -94,6 +103,12 @@ test_link_handshake_certs_ok(void *arg) tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, key1, key2, 86400), ==, 0); + { + const tor_x509_cert_t *link = NULL; + tt_assert(!tor_tls_get_my_certs(1, &link, NULL)); + mock_own_cert = tor_x509_cert_dup(link); + } + c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; c1->link_proto = 3; tt_int_op(connection_init_or_handshake_state(c1, 1), ==, 0); @@ -174,6 +189,9 @@ test_link_handshake_certs_ok(void *arg) UNMOCK(tor_tls_cert_matches_key); UNMOCK(connection_or_write_var_cell_to_buf); UNMOCK(connection_or_send_netinfo); + UNMOCK(tor_tls_get_own_cert); + tor_x509_cert_free(mock_own_cert); + mock_own_cert = NULL; memset(c1->identity_digest, 0, sizeof(c1->identity_digest)); memset(c2->identity_digest, 0, sizeof(c2->identity_digest)); connection_free_(TO_CONN(c1)); @@ -656,11 +674,12 @@ AUTHCHALLENGE_FAIL(nonzero_circid, d->cell->circ_id = 1337) static tor_x509_cert_t *mock_peer_cert = NULL; + static tor_x509_cert_t * mock_get_peer_cert(tor_tls_t *tls) { (void)tls; - return mock_peer_cert; + return tor_x509_cert_dup(mock_peer_cert); } static int @@ -694,6 +713,7 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg) (void) test; UNMOCK(connection_or_write_var_cell_to_buf); UNMOCK(tor_tls_get_peer_cert); + UNMOCK(tor_tls_get_own_cert); UNMOCK(tor_tls_get_tlssecrets); UNMOCK(connection_or_close_for_error); UNMOCK(channel_set_circid_type); @@ -710,7 +730,10 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg) crypto_pk_free(d->key2); tor_free(d); } + tor_x509_cert_free(mock_peer_cert); + tor_x509_cert_free(mock_own_cert); mock_peer_cert = NULL; + mock_own_cert = NULL; return 1; } @@ -724,6 +747,7 @@ authenticate_data_setup(const struct testcase_t *test) MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); MOCK(tor_tls_get_peer_cert, mock_get_peer_cert); + MOCK(tor_tls_get_own_cert, mock_get_own_cert); MOCK(tor_tls_get_tlssecrets, mock_get_tlssecrets); MOCK(connection_or_close_for_error, mock_close_for_err); MOCK(channel_set_circid_type, mock_set_circid_type); @@ -773,6 +797,8 @@ authenticate_data_setup(const struct testcase_t *test) tor_x509_cert_get_der(link_cert, &der, &sz); mock_peer_cert = tor_x509_cert_decode(der, sz); tt_assert(mock_peer_cert); + mock_own_cert = tor_x509_cert_decode(der, sz); + tt_assert(mock_own_cert); tt_assert(! tor_tls_get_my_certs(0, &auth_cert, &id_cert)); tor_x509_cert_get_der(auth_cert, &der, &sz); d->c2->handshake_state->auth_cert = tor_x509_cert_decode(der, sz); From 01878fa3095a949575d922da6fc8342eeaad2afb Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 1 Jun 2017 09:03:13 -0400 Subject: [PATCH 7/9] Changes file for the x509 link certificate case of bug22460 --- changes/bug22460_case2 | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 changes/bug22460_case2 diff --git a/changes/bug22460_case2 b/changes/bug22460_case2 new file mode 100644 index 000000000..0a1175983 --- /dev/null +++ b/changes/bug22460_case2 @@ -0,0 +1,8 @@ + o Major bugfixes (relay, link handshake): + + - When performing the v3 link handshake on a TLS connection, report that + we have the x509 certificate that we actually used on that connection, + even if we have changed certificates since that connection was first + opened. Previously, we would claim to have used our most recent x509 + link certificate, which would sometimes make the link handshake fail. + Fixes one case of bug 22460; bugfix on 0.2.3.6-alpha. From ec84fc1d8ecb56fde887eb01d3bca1a031bd1e89 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 5 Jun 2017 09:42:02 -0400 Subject: [PATCH 8/9] Improve documentation on get_{peer,own}_certificate() Make it clear that we're returning a newly allocated copy. --- src/common/tortls.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index 1661b7ef5..d61cc2e58 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -2018,7 +2018,8 @@ tor_tls_peer_has_cert(tor_tls_t *tls) return 1; } -/** Return the peer certificate, or NULL if there isn't one. */ +/** Return a newly allocated copy of the peer certificate, or NULL if there + * isn't one. */ MOCK_IMPL(tor_x509_cert_t *, tor_tls_get_peer_cert,(tor_tls_t *tls)) { @@ -2030,8 +2031,8 @@ tor_tls_get_peer_cert,(tor_tls_t *tls)) return tor_x509_cert_new(cert); } -/** Return the cerficate we used on the connection, or NULL if somehow - * we didn't use one. */ +/** Return a newly allocated copy of the cerficate we used on the connection, + * or NULL if somehow we didn't use one. */ MOCK_IMPL(tor_x509_cert_t *, tor_tls_get_own_cert,(tor_tls_t *tls)) { From 91f49bc0f0759d0e0a794fbfe8cce5a9bb07e607 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 5 Jun 2017 15:51:11 -0400 Subject: [PATCH 9/9] Fix unit tests to work after own_link_cert assertion The assert_nonfatal() I had added was triggered by some of the code that tested the pre-ed case of CERTS cell generation. --- src/or/connection_or.c | 9 ++++++++- src/or/connection_or.h | 4 ++++ src/test/test_link_handshake.c | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 1e2d731f5..6eb62a99d 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -2270,6 +2270,12 @@ add_ed25519_cert(certs_cell_t *certs_cell, cert->encoded, cert->encoded_len); } +#ifdef TOR_UNIT_TESTS +int certs_cell_ed25519_disabled_for_testing = 0; +#else +#define certs_cell_ed25519_disabled_for_testing 0 +#endif + /** Send a CERTS cell on the connection conn. Return 0 on success, -1 * on failure. */ int @@ -2320,7 +2326,8 @@ connection_or_send_certs_cell(or_connection_t *conn) CERTTYPE_ED_ID_SIGN, get_master_signing_key_cert()); if (conn_in_server_mode) { - tor_assert_nonfatal(conn->handshake_state->own_link_cert); + tor_assert_nonfatal(conn->handshake_state->own_link_cert || + certs_cell_ed25519_disabled_for_testing); add_ed25519_cert(certs_cell, CERTTYPE_ED_SIGN_LINK, conn->handshake_state->own_link_cert); diff --git a/src/or/connection_or.h b/src/or/connection_or.h index 80a5bddb1..514a0fd00 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -112,5 +112,9 @@ void var_cell_free(var_cell_t *cell); void connection_or_group_set_badness_(smartlist_t *group, int force); +#ifdef TOR_UNIT_TESTS +extern int certs_cell_ed25519_disabled_for_testing; +#endif + #endif diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index ade7cf699..66eeb724a 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -149,6 +149,8 @@ test_link_handshake_certs_ok(void *arg) /* If we're making a CERTS cell for an ed handshake, let's make sure we * have some Ed25519 certificates and keys. */ init_mock_ed_keys(key2); + } else { + certs_cell_ed25519_disabled_for_testing = 1; } /* c1 has started_here == 1 */