From 50facb40bb42e070b858ca052edccd0f3a5b83b5 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 31 May 2017 19:12:32 -0400 Subject: 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 62ed5be344..055b4686e5 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 7c035a2cd5..ae11b7ab60 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 267c32dda4..3b35d5e34c 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 */ -- cgit v1.2.3-54-g00ecf From 39b7e89c28efda628d57640302276b7f04b2c0de Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 1 Jun 2017 08:55:54 -0400 Subject: 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 055b4686e5..1661b7ef53 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 ae11b7ab60..f018c45c82 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, -- cgit v1.2.3-54-g00ecf From 8e9392c2670d949517f688d2d7c6320251bf6117 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 1 Jun 2017 08:56:10 -0400 Subject: 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 6c0567098f..b979485967 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); -- cgit v1.2.3-54-g00ecf From 01878fa3095a949575d922da6fc8342eeaad2afb Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 1 Jun 2017 09:03:13 -0400 Subject: 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 0000000000..0a11759832 --- /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. -- cgit v1.2.3-54-g00ecf From ec84fc1d8ecb56fde887eb01d3bca1a031bd1e89 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 5 Jun 2017 09:42:02 -0400 Subject: 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 1661b7ef53..d61cc2e58a 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)) { -- cgit v1.2.3-54-g00ecf