From b75361c5ed717cde787c1b4f36e8fb51ccfddc2b Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 8 Oct 2014 15:39:34 -0400 Subject: [PATCH] Start testing cell encoders/processers for the v3 handshake. An earlier version of these tests was broken; now they're a nicer, more robust, more black-box set of tests. The key is to have each test check a handshake message that is wrong in _one_ way. --- src/common/tortls.c | 4 +- src/common/tortls.h | 4 +- src/or/channeltls.c | 15 +- src/or/channeltls.h | 8 + src/or/connection_or.c | 18 +- src/or/connection_or.h | 12 +- src/test/include.am | 3 + src/test/test.c | 2 + src/test/test_link_handshake.c | 637 +++++++++++++++++++++++++++++++++ 9 files changed, 675 insertions(+), 28 deletions(-) create mode 100644 src/test/test_link_handshake.c diff --git a/src/common/tortls.c b/src/common/tortls.c index 97dca4d478..62e32c5dae 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -1023,8 +1023,8 @@ tor_tls_cert_get_key(tor_x509_cert_t *cert) /** Return true iff the other side of tls has authenticated to us, and * the key certified in cert is the same as the key they used to do it. */ -int -tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert) +MOCK_IMPL(int, +tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert)) { X509 *peercert = SSL_get_peer_certificate(tls->ssl); EVP_PKEY *link_key = NULL, *cert_key = NULL; diff --git a/src/common/tortls.h b/src/common/tortls.h index 28a73e9ae4..11ef09fed4 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -132,8 +132,8 @@ int tor_tls_get_my_certs(int server, const tor_x509_cert_t **id_cert_out); crypto_pk_t *tor_tls_get_my_client_auth_key(void); crypto_pk_t *tor_tls_cert_get_key(tor_x509_cert_t *cert); -int tor_tls_cert_matches_key(const tor_tls_t *tls, - const tor_x509_cert_t *cert); +MOCK_DECL(int,tor_tls_cert_matches_key,(const tor_tls_t *tls, + const tor_x509_cert_t *cert)); int tor_tls_cert_is_valid(int severity, const tor_x509_cert_t *cert, const tor_x509_cert_t *signing_cert, diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 10fcb281db..af7f474349 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -13,6 +13,8 @@ #define TOR_CHANNEL_INTERNAL_ +#define CHANNELTLS_PRIVATE + #include "or.h" #include "channel.h" #include "channeltls.h" @@ -47,9 +49,6 @@ uint64_t stats_n_authorize_cells_processed = 0; /** Active listener, if any */ channel_listener_t *channel_tls_listener = NULL; -/* Utility function declarations */ -static void channel_tls_common_init(channel_tls_t *tlschan); - /* channel_tls_t method declarations */ static void channel_tls_close_method(channel_t *chan); @@ -91,10 +90,6 @@ static void channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *tlschan); static void channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *tlschan); -static void channel_tls_process_certs_cell(var_cell_t *cell, - channel_tls_t *tlschan); -static void channel_tls_process_auth_challenge_cell(var_cell_t *cell, - channel_tls_t *tlschan); static void channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *tlschan); static int command_allowed_before_handshake(uint8_t command); @@ -106,7 +101,7 @@ static int enter_v3_handshake_with_cell(var_cell_t *cell, * and channel_tls_handle_incoming(). */ -static void +STATIC void channel_tls_common_init(channel_tls_t *tlschan) { channel_t *chan; @@ -1744,7 +1739,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) * If it's the server side, wait for an AUTHENTICATE cell. */ -static void +STATIC void channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) { tor_x509_cert_t *link_cert = NULL; @@ -1946,7 +1941,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) * want to authenticate, send an AUTHENTICATE cell and then a NETINFO cell. */ -static void +STATIC void channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) { int n_types, i, use_type = -1; diff --git a/src/or/channeltls.h b/src/or/channeltls.h index 507429420b..69f6e629c4 100644 --- a/src/or/channeltls.h +++ b/src/or/channeltls.h @@ -52,5 +52,13 @@ void channel_tls_update_marks(or_connection_t *conn); /* Cleanup at shutdown */ void channel_tls_free_all(void); +#ifdef CHANNELTLS_PRIVATE +STATIC void channel_tls_process_certs_cell(var_cell_t *cell, + channel_tls_t *tlschan); +STATIC void channel_tls_process_auth_challenge_cell(var_cell_t *cell, + channel_tls_t *tlschan); +STATIC void channel_tls_common_init(channel_tls_t *tlschan); +#endif + #endif diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 4ab47f0aa8..8602bcbcf4 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1318,8 +1318,8 @@ connection_or_close_normally(or_connection_t *orconn, int flush) * the error state. */ -void -connection_or_close_for_error(or_connection_t *orconn, int flush) +MOCK_IMPL(void, +connection_or_close_for_error,(or_connection_t *orconn, int flush)) { channel_t *chan = NULL; @@ -2012,9 +2012,9 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn) * conn's outbuf. Right now, this DOES NOT support cells that * affect a circuit. */ -void -connection_or_write_var_cell_to_buf(const var_cell_t *cell, - or_connection_t *conn) +MOCK_IMPL(void, +connection_or_write_var_cell_to_buf,(const var_cell_t *cell, + or_connection_t *conn)) { int n; char hdr[VAR_CELL_MAX_HEADER_SIZE]; @@ -2157,8 +2157,8 @@ connection_or_send_versions(or_connection_t *conn, int v3_plus) /** Send a NETINFO cell on conn, telling the other server what we know * about their address, our address, and the current time. */ -int -connection_or_send_netinfo(or_connection_t *conn) +MOCK_IMPL(int, +connection_or_send_netinfo,(or_connection_t *conn)) { cell_t cell; time_t now = time(NULL); @@ -2443,8 +2443,8 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, /** Send an AUTHENTICATE cell on the connection conn. Return 0 on * success, -1 on failure */ -int -connection_or_send_authenticate_cell(or_connection_t *conn, int authtype) +MOCK_IMPL(int, +connection_or_send_authenticate_cell,(or_connection_t *conn, int authtype)) { var_cell_t *cell; crypto_pk_t *pk = tor_tls_get_my_client_auth_key(); diff --git a/src/or/connection_or.h b/src/or/connection_or.h index fc261c6bac..3877fd5a13 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -43,7 +43,8 @@ MOCK_DECL(or_connection_t *, const char *id_digest, channel_tls_t *chan)); void connection_or_close_normally(or_connection_t *orconn, int flush); -void connection_or_close_for_error(or_connection_t *orconn, int flush); +MOCK_DECL(void,connection_or_close_for_error, + (or_connection_t *orconn, int flush)); void connection_or_report_broken_states(int severity, int domain); @@ -77,17 +78,18 @@ void or_handshake_state_record_var_cell(or_connection_t *conn, int connection_or_set_state_open(or_connection_t *conn); void connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn); -void connection_or_write_var_cell_to_buf(const var_cell_t *cell, - or_connection_t *conn); +MOCK_DECL(void,connection_or_write_var_cell_to_buf,(const var_cell_t *cell, + or_connection_t *conn)); int connection_or_send_versions(or_connection_t *conn, int v3_plus); -int connection_or_send_netinfo(or_connection_t *conn); +MOCK_DECL(int,connection_or_send_netinfo,(or_connection_t *conn)); int connection_or_send_certs_cell(or_connection_t *conn); int connection_or_send_auth_challenge_cell(or_connection_t *conn); int connection_or_compute_authenticate_cell_body(or_connection_t *conn, uint8_t *out, size_t outlen, crypto_pk_t *signing_key, int server); -int connection_or_send_authenticate_cell(or_connection_t *conn, int type); +MOCK_DECL(int,connection_or_send_authenticate_cell, + (or_connection_t *conn, int type)); int is_or_protocol_version_known(uint16_t version); diff --git a/src/test/include.am b/src/test/include.am index b433fc50a9..2a0c5437ad 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -13,6 +13,8 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ -DLOCALSTATEDIR="\"$(localstatedir)\"" \ -DBINDIR="\"$(bindir)\"" \ -I"$(top_srcdir)/src/or" -I"$(top_srcdir)/src/ext" \ + -I"$(top_srcdir)/src/trunnel" \ + -I"$(top_srcdir)/src/ext/trunnel" \ -DTOR_UNIT_TESTS # -L flags need to go in LDFLAGS. -l flags need to go in LDADD. @@ -45,6 +47,7 @@ src_test_test_SOURCES = \ src/test/test_hs.c \ src/test/test_introduce.c \ src/test/test_keypin.c \ + src/test/test_link_handshake.c \ src/test/test_logging.c \ src/test/test_microdesc.c \ src/test/test_nodelist.c \ diff --git a/src/test/test.c b/src/test/test.c index cd8aa3f014..dee5c001eb 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1137,6 +1137,7 @@ extern struct testcase_t extorport_tests[]; extern struct testcase_t hs_tests[]; extern struct testcase_t introduce_tests[]; extern struct testcase_t keypin_tests[]; +extern struct testcase_t link_handshake_tests[]; extern struct testcase_t logging_tests[]; extern struct testcase_t microdesc_tests[]; extern struct testcase_t nodelist_tests[]; @@ -1183,6 +1184,7 @@ struct testgroup_t testgroups[] = { { "hs/", hs_tests }, { "introduce/", introduce_tests }, { "keypin/", keypin_tests }, + { "link-handshake/", link_handshake_tests }, { "nodelist/", nodelist_tests }, { "oom/", oom_tests }, { "options/", options_tests }, diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c new file mode 100644 index 0000000000..eb72a0c91a --- /dev/null +++ b/src/test/test_link_handshake.c @@ -0,0 +1,637 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#define CHANNELTLS_PRIVATE +#define CONNECTION_PRIVATE +#define TOR_CHANNEL_INTERNAL_ +#include "or.h" +#include "config.h" +#include "connection.h" +#include "connection_or.h" +#include "channeltls.h" +#include "link_handshake.h" + +#include "test.h" + +var_cell_t *mock_got_var_cell = NULL; + +static void +mock_write_var_cell(const var_cell_t *vc, or_connection_t *conn) +{ + (void)conn; + + var_cell_t *newcell = var_cell_new(vc->payload_len); + memcpy(newcell, vc, sizeof(var_cell_t)); + memcpy(newcell->payload, vc->payload, vc->payload_len); + + mock_got_var_cell = newcell; +} +static int +mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert) +{ + (void) tls; + (void) cert; // XXXX look at this. + return 1; +} + +static int mock_send_netinfo_called = 0; +static int +mock_send_netinfo(or_connection_t *conn) +{ + (void) conn; + ++mock_send_netinfo_called;// XXX check_this + return 0; +} + +static int mock_close_called = 0; +static void +mock_close_for_err(or_connection_t *orconn, int flush) +{ + (void)orconn; + (void)flush; + ++mock_close_called; +} + +static int mock_send_authenticate_called = 0; +static int +mock_send_authenticate(or_connection_t *conn, int type) +{ + (void) conn; + (void) type; + ++mock_send_authenticate_called;// XXX check_this + return 0; +} + +/* Test good certs cells */ +static void +test_link_handshake_certs_ok(void *arg) +{ + (void) arg; + + or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET); + or_connection_t *c2 = or_connection_new(CONN_TYPE_OR, AF_INET); + var_cell_t *cell1 = NULL, *cell2 = NULL; + certs_cell_t *cc1 = NULL, *cc2 = NULL; + channel_tls_t *chan1 = NULL, *chan2 = NULL; + crypto_pk_t *key1 = NULL, *key2 = NULL; + + scheduler_init(); + + 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); + + key1 = pk_generate(2); + key2 = pk_generate(3); + + /* We need to make sure that our TLS certificates are set up before we can + * actually generate a CERTS cell. + */ + tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + key1, key2, 86400), ==, 0); + + c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + c1->link_proto = 3; + tt_int_op(connection_init_or_handshake_state(c1, 1), ==, 0); + + c2->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + c2->link_proto = 3; + tt_int_op(connection_init_or_handshake_state(c2, 0), ==, 0); + + tt_int_op(0, ==, connection_or_send_certs_cell(c1)); + tt_assert(mock_got_var_cell); + cell1 = mock_got_var_cell; + + tt_int_op(0, ==, connection_or_send_certs_cell(c2)); + tt_assert(mock_got_var_cell); + cell2 = mock_got_var_cell; + + tt_int_op(cell1->command, ==, CELL_CERTS); + tt_int_op(cell1->payload_len, >, 1); + + tt_int_op(cell2->command, ==, CELL_CERTS); + tt_int_op(cell2->payload_len, >, 1); + + tt_int_op(cell1->payload_len, ==, + certs_cell_parse(&cc1, cell1->payload, cell1->payload_len)); + tt_int_op(cell2->payload_len, ==, + certs_cell_parse(&cc2, cell2->payload, cell2->payload_len)); + + tt_int_op(2, ==, cc1->n_certs); + tt_int_op(2, ==, cc2->n_certs); + + tt_int_op(certs_cell_get_certs(cc1, 0)->cert_type, ==, + CERTTYPE_RSA1024_ID_AUTH); + tt_int_op(certs_cell_get_certs(cc1, 1)->cert_type, ==, + CERTTYPE_RSA1024_ID_ID); + + tt_int_op(certs_cell_get_certs(cc2, 0)->cert_type, ==, + CERTTYPE_RSA1024_ID_LINK); + tt_int_op(certs_cell_get_certs(cc2, 1)->cert_type, ==, + CERTTYPE_RSA1024_ID_ID); + + chan1 = tor_malloc_zero(sizeof(*chan1)); + channel_tls_common_init(chan1); + c1->chan = chan1; + chan1->conn = c1; + c1->base_.address = tor_strdup("C1"); + c1->tls = tor_tls_new(-1, 0); + c1->link_proto = 4; + c1->base_.conn_array_index = -1; + crypto_pk_get_digest(key2, c1->identity_digest); + + channel_tls_process_certs_cell(cell2, chan1); + + tt_assert(c1->handshake_state->received_certs_cell); + tt_assert(c1->handshake_state->auth_cert == NULL); + tt_assert(c1->handshake_state->id_cert); + tt_assert(! tor_mem_is_zero( + (char*)c1->handshake_state->authenticated_peer_id, 20)); + + chan2 = tor_malloc_zero(sizeof(*chan2)); + channel_tls_common_init(chan2); + c2->chan = chan2; + chan2->conn = c2; + c2->base_.address = tor_strdup("C2"); + c2->tls = tor_tls_new(-1, 1); + c2->link_proto = 4; + c2->base_.conn_array_index = -1; + crypto_pk_get_digest(key1, c2->identity_digest); + + channel_tls_process_certs_cell(cell1, chan2); + + tt_assert(c2->handshake_state->received_certs_cell); + tt_assert(c2->handshake_state->auth_cert); + tt_assert(c2->handshake_state->id_cert); + tt_assert(tor_mem_is_zero( + (char*)c2->handshake_state->authenticated_peer_id, 20)); + + done: + UNMOCK(tor_tls_cert_matches_key); + UNMOCK(connection_or_write_var_cell_to_buf); + UNMOCK(connection_or_send_netinfo); + connection_free_(TO_CONN(c1)); + connection_free_(TO_CONN(c2)); + tor_free(cell1); + tor_free(cell2); + certs_cell_free(cc1); + certs_cell_free(cc2); + tor_free(chan1); + tor_free(chan2); +} + +typedef struct certs_data_s { + or_connection_t *c; + channel_tls_t *chan; + certs_cell_t *ccell; + var_cell_t *cell; +} certs_data_t; + + +static int +recv_certs_cleanup(const struct testcase_t *test, void *obj) +{ + (void)test; + certs_data_t *d = obj; + UNMOCK(tor_tls_cert_matches_key); + UNMOCK(connection_or_send_netinfo); + UNMOCK(connection_or_close_for_error); + + if (d) { + tor_free(d->cell); + certs_cell_free(d->ccell); + connection_free_(TO_CONN(d->c)); + tor_free(d->chan); + tor_free(d); + } + return 1; +} + +static void * +recv_certs_setup(const struct testcase_t *test) +{ + (void)test; + certs_data_t *d = tor_malloc_zero(sizeof(*d)); + certs_cell_cert_t *ccc1 = NULL; + certs_cell_cert_t *ccc2 = NULL; + ssize_t n; + + d->c = or_connection_new(CONN_TYPE_OR, AF_INET); + d->chan = tor_malloc_zero(sizeof(*d->chan)); + d->c->chan = d->chan; + d->c->base_.address = tor_strdup("HaveAnAddress"); + d->c->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + d->chan->conn = d->c; + tt_int_op(connection_init_or_handshake_state(d->c, 1), ==, 0); + d->c->link_proto = 4; + + crypto_pk_t *key1 = NULL, *key2 = NULL; + key1 = pk_generate(2); + key2 = pk_generate(3); + + tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + key1, key2, 86400), ==, 0); + d->ccell = certs_cell_new(); + ccc1 = certs_cell_cert_new(); + certs_cell_add_certs(d->ccell, ccc1); + ccc2 = certs_cell_cert_new(); + certs_cell_add_certs(d->ccell, ccc2); + d->ccell->n_certs = 2; + ccc1->cert_type = 1; + ccc2->cert_type = 2; + + const tor_x509_cert_t *a,*b; + const uint8_t *enca, *encb; + size_t lena, lenb; + tor_tls_get_my_certs(1, &a, &b); + tor_x509_cert_get_der(a, &enca, &lena); + tor_x509_cert_get_der(b, &encb, &lenb); + certs_cell_cert_setlen_body(ccc1, lena); + ccc1->cert_len = lena; + certs_cell_cert_setlen_body(ccc2, lenb); + ccc2->cert_len = lenb; + + memcpy(certs_cell_cert_getarray_body(ccc1), enca, lena); + memcpy(certs_cell_cert_getarray_body(ccc2), encb, lenb); + + d->cell = var_cell_new(4096); + d->cell->command = CELL_CERTS; + + n = certs_cell_encode(d->cell->payload, 4096, d->ccell); + tt_int_op(n, >, 0); + d->cell->payload_len = n; + + MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key); + MOCK(connection_or_send_netinfo, mock_send_netinfo); + MOCK(connection_or_close_for_error, mock_close_for_err); + + tt_int_op(0, ==, d->c->handshake_state->received_certs_cell); + tt_int_op(0, ==, mock_send_authenticate_called); + tt_int_op(0, ==, mock_send_netinfo_called); + + return d; + done: + recv_certs_cleanup(test, d); + return NULL; +} + +static struct testcase_setup_t setup_recv_certs = { + .setup_fn = recv_certs_setup, + .cleanup_fn = recv_certs_cleanup +}; + +static void +test_link_handshake_recv_certs_ok(void *arg) +{ + certs_data_t *d = arg; + channel_tls_process_certs_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(d->c->handshake_state->authenticated, ==, 1); + tt_int_op(d->c->handshake_state->received_certs_cell, ==, 1); + tt_assert(d->c->handshake_state->id_cert != NULL); + tt_assert(d->c->handshake_state->auth_cert == NULL); + + done: + ; +} + +static void +test_link_handshake_recv_certs_ok_server(void *arg) +{ + certs_data_t *d = arg; + d->c->handshake_state->started_here = 0; + certs_cell_get_certs(d->ccell, 0)->cert_type = 3; + certs_cell_get_certs(d->ccell, 1)->cert_type = 2; + ssize_t n = certs_cell_encode(d->cell->payload, 2048, d->ccell); + tt_int_op(n, >, 0); + d->cell->payload_len = n; + channel_tls_process_certs_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(d->c->handshake_state->authenticated, ==, 0); + tt_int_op(d->c->handshake_state->received_certs_cell, ==, 1); + tt_assert(d->c->handshake_state->id_cert != NULL); + tt_assert(d->c->handshake_state->auth_cert != NULL); + + done: + ; +} + +#define CERTS_FAIL(name, code) \ + static void \ + test_link_handshake_recv_certs_ ## name (void *arg) \ + { \ + certs_data_t *d = arg; \ + { code ; } \ + channel_tls_process_certs_cell(d->cell, d->chan); \ + tt_int_op(1, ==, mock_close_called); \ + tt_int_op(0, ==, mock_send_authenticate_called); \ + tt_int_op(0, ==, mock_send_netinfo_called); \ + done: \ + ; \ + } + +CERTS_FAIL(badstate, d->c->base_.state = OR_CONN_STATE_CONNECTING) +CERTS_FAIL(badproto, d->c->link_proto = 2) +CERTS_FAIL(duplicate, d->c->handshake_state->received_certs_cell = 1) +CERTS_FAIL(already_authenticated, + d->c->handshake_state->authenticated = 1) +CERTS_FAIL(empty, d->cell->payload_len = 0) +CERTS_FAIL(bad_circid, d->cell->circ_id = 1) +CERTS_FAIL(truncated_1, d->cell->payload[0] = 5) +CERTS_FAIL(truncated_2, { + d->cell->payload_len = 4; + memcpy(d->cell->payload, "\x01\x01\x00\x05", 4);}) +CERTS_FAIL(truncated_3, { + d->cell->payload_len = 7; + memcpy(d->cell->payload, "\x01\x01\x00\x05""abc", 7);}) +#define REENCODE() do { \ + ssize_t n = certs_cell_encode(d->cell->payload, 4096, d->ccell); \ + tt_int_op(n, >, 0); \ + d->cell->payload_len = n; \ + } while (0) + +CERTS_FAIL(not_x509, { + certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 0), 3); + certs_cell_get_certs(d->ccell, 0)->cert_len = 3; + REENCODE(); + }) +CERTS_FAIL(both_link, { + certs_cell_get_certs(d->ccell, 0)->cert_type = 1; + certs_cell_get_certs(d->ccell, 1)->cert_type = 1; + REENCODE(); + }) +CERTS_FAIL(both_id_rsa, { + certs_cell_get_certs(d->ccell, 0)->cert_type = 2; + certs_cell_get_certs(d->ccell, 1)->cert_type = 2; + REENCODE(); + }) +CERTS_FAIL(both_auth, { + certs_cell_get_certs(d->ccell, 0)->cert_type = 3; + certs_cell_get_certs(d->ccell, 1)->cert_type = 3; + REENCODE(); + }) +CERTS_FAIL(wrong_labels_1, { + certs_cell_get_certs(d->ccell, 0)->cert_type = 2; + certs_cell_get_certs(d->ccell, 1)->cert_type = 1; + REENCODE(); + }) +CERTS_FAIL(wrong_labels_2, { + const tor_x509_cert_t *a; + const tor_x509_cert_t *b; + const uint8_t *enca; + size_t lena; + tor_tls_get_my_certs(1, &a, &b); + tor_x509_cert_get_der(a, &enca, &lena); + certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 1), lena); + memcpy(certs_cell_cert_getarray_body(certs_cell_get_certs(d->ccell, 1)), + enca, lena); + certs_cell_get_certs(d->ccell, 1)->cert_len = lena; + REENCODE(); + }) +CERTS_FAIL(wrong_labels_3, { + certs_cell_get_certs(d->ccell, 0)->cert_type = 2; + certs_cell_get_certs(d->ccell, 1)->cert_type = 3; + REENCODE(); + }) +CERTS_FAIL(server_missing_certs, { + d->c->handshake_state->started_here = 0; + }) +CERTS_FAIL(server_wrong_labels_1, { + d->c->handshake_state->started_here = 0; + certs_cell_get_certs(d->ccell, 0)->cert_type = 2; + certs_cell_get_certs(d->ccell, 1)->cert_type = 3; + REENCODE(); + }) + +static void +test_link_handshake_send_authchallenge(void *arg) +{ + (void)arg; + + or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET); + var_cell_t *cell1=NULL, *cell2=NULL; + + MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); + + tt_int_op(connection_init_or_handshake_state(c1, 0), ==, 0); + c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + tt_assert(! mock_got_var_cell); + tt_int_op(0, ==, connection_or_send_auth_challenge_cell(c1)); + cell1 = mock_got_var_cell; + tt_int_op(0, ==, connection_or_send_auth_challenge_cell(c1)); + cell2 = mock_got_var_cell; + tt_int_op(36, ==, cell1->payload_len); + tt_int_op(36, ==, cell2->payload_len); + tt_int_op(0, ==, cell1->circ_id); + tt_int_op(0, ==, cell2->circ_id); + tt_int_op(CELL_AUTH_CHALLENGE, ==, cell1->command); + tt_int_op(CELL_AUTH_CHALLENGE, ==, cell2->command); + + tt_mem_op("\x00\x01\x00\x01", ==, cell1->payload + 32, 4); + tt_mem_op("\x00\x01\x00\x01", ==, cell2->payload + 32, 4); + tt_mem_op(cell1->payload, !=, cell2->payload, 32); + + done: + UNMOCK(connection_or_write_var_cell_to_buf); + connection_free_(TO_CONN(c1)); + tor_free(cell1); + tor_free(cell2); +} + +typedef struct authchallenge_data_s { + or_connection_t *c; + channel_tls_t *chan; + var_cell_t *cell; +} authchallenge_data_t; + +static int +recv_authchallenge_cleanup(const struct testcase_t *test, void *obj) +{ + (void)test; + authchallenge_data_t *d = obj; + + UNMOCK(connection_or_send_netinfo); + UNMOCK(connection_or_close_for_error); + UNMOCK(connection_or_send_authenticate_cell); + + if (d) { + tor_free(d->cell); + connection_free_(TO_CONN(d->c)); + tor_free(d->chan); + tor_free(d); + } + return 1; +} + +static void * +recv_authchallenge_setup(const struct testcase_t *test) +{ + (void)test; + authchallenge_data_t *d = tor_malloc_zero(sizeof(*d)); + d->c = or_connection_new(CONN_TYPE_OR, AF_INET); + d->chan = tor_malloc_zero(sizeof(*d->chan)); + d->c->chan = d->chan; + d->c->base_.address = tor_strdup("HaveAnAddress"); + d->c->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + d->chan->conn = d->c; + tt_int_op(connection_init_or_handshake_state(d->c, 1), ==, 0); + d->c->link_proto = 4; + d->c->handshake_state->received_certs_cell = 1; + d->cell = var_cell_new(128); + d->cell->payload_len = 38; + d->cell->payload[33] = 2; + d->cell->payload[35] = 7; + d->cell->payload[37] = 1; + d->cell->command = CELL_AUTH_CHALLENGE; + + get_options_mutable()->ORPort_set = 1; + + MOCK(connection_or_close_for_error, mock_close_for_err); + MOCK(connection_or_send_netinfo, mock_send_netinfo); + MOCK(connection_or_send_authenticate_cell, mock_send_authenticate); + + tt_int_op(0, ==, d->c->handshake_state->received_auth_challenge); + tt_int_op(0, ==, mock_send_authenticate_called); + tt_int_op(0, ==, mock_send_netinfo_called); + + return d; + done: + recv_authchallenge_cleanup(test, d); + return NULL; +} + +static struct testcase_setup_t setup_recv_authchallenge = { + .setup_fn = recv_authchallenge_setup, + .cleanup_fn = recv_authchallenge_cleanup +}; + +static void +test_link_handshake_recv_authchallenge_ok(void *arg) +{ + authchallenge_data_t *d = arg; + + channel_tls_process_auth_challenge_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge); + tt_int_op(1, ==, mock_send_authenticate_called); + tt_int_op(1, ==, mock_send_netinfo_called); + done: + ; +} + +static void +test_link_handshake_recv_authchallenge_ok_noserver(void *arg) +{ + authchallenge_data_t *d = arg; + get_options_mutable()->ORPort_set = 0; + + channel_tls_process_auth_challenge_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge); + tt_int_op(0, ==, mock_send_authenticate_called); + tt_int_op(0, ==, mock_send_netinfo_called); + done: + ; +} + +static void +test_link_handshake_recv_authchallenge_ok_unrecognized(void *arg) +{ + authchallenge_data_t *d = arg; + d->cell->payload[37] = 99; + + channel_tls_process_auth_challenge_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge); + tt_int_op(0, ==, mock_send_authenticate_called); + tt_int_op(1, ==, mock_send_netinfo_called); + done: + ; +} + +#define AUTHCHALLENGE_FAIL(name, code) \ + static void \ + test_link_handshake_recv_authchallenge_ ## name (void *arg) \ + { \ + authchallenge_data_t *d = arg; \ + { code ; } \ + channel_tls_process_auth_challenge_cell(d->cell, d->chan); \ + tt_int_op(1, ==, mock_close_called); \ + tt_int_op(0, ==, mock_send_authenticate_called); \ + tt_int_op(0, ==, mock_send_netinfo_called); \ + done: \ + ; \ + } + +AUTHCHALLENGE_FAIL(badstate, + d->c->base_.state = OR_CONN_STATE_CONNECTING) +AUTHCHALLENGE_FAIL(badproto, + d->c->link_proto = 2) +AUTHCHALLENGE_FAIL(as_server, + d->c->handshake_state->started_here = 0;) +AUTHCHALLENGE_FAIL(duplicate, + d->c->handshake_state->received_auth_challenge = 1) +AUTHCHALLENGE_FAIL(nocerts, + d->c->handshake_state->received_certs_cell = 0) +AUTHCHALLENGE_FAIL(tooshort, + d->cell->payload_len = 33) +AUTHCHALLENGE_FAIL(truncated, + d->cell->payload_len = 34) +AUTHCHALLENGE_FAIL(nonzero_circid, + d->cell->circ_id = 1337) + +#define TEST(name, flags) \ + { #name , test_link_handshake_ ## name, (flags), NULL, NULL } + +#define TEST_RCV_AUTHCHALLENGE(name) \ + { "recv_authchallenge/" #name , \ + test_link_handshake_recv_authchallenge_ ## name, TT_FORK, \ + &setup_recv_authchallenge, NULL } + +#define TEST_RCV_CERTS(name) \ + { "recv_certs/" #name , \ + test_link_handshake_recv_certs_ ## name, TT_FORK, \ + &setup_recv_certs, NULL } + +struct testcase_t link_handshake_tests[] = { + TEST(certs_ok, TT_FORK), + //TEST(certs_bad, TT_FORK), + TEST_RCV_CERTS(ok), + TEST_RCV_CERTS(ok_server), + TEST_RCV_CERTS(badstate), + TEST_RCV_CERTS(badproto), + TEST_RCV_CERTS(duplicate), + TEST_RCV_CERTS(already_authenticated), + TEST_RCV_CERTS(empty), + TEST_RCV_CERTS(bad_circid), + TEST_RCV_CERTS(truncated_1), + TEST_RCV_CERTS(truncated_2), + TEST_RCV_CERTS(truncated_3), + TEST_RCV_CERTS(not_x509), + TEST_RCV_CERTS(both_link), + TEST_RCV_CERTS(both_id_rsa), + TEST_RCV_CERTS(both_auth), + TEST_RCV_CERTS(wrong_labels_1), + TEST_RCV_CERTS(wrong_labels_2), + TEST_RCV_CERTS(wrong_labels_3), + TEST_RCV_CERTS(server_missing_certs), + TEST_RCV_CERTS(server_wrong_labels_1), + + TEST(send_authchallenge, TT_FORK), + TEST_RCV_AUTHCHALLENGE(ok), + TEST_RCV_AUTHCHALLENGE(ok_noserver), + TEST_RCV_AUTHCHALLENGE(ok_unrecognized), + TEST_RCV_AUTHCHALLENGE(badstate), + TEST_RCV_AUTHCHALLENGE(badproto), + TEST_RCV_AUTHCHALLENGE(as_server), + TEST_RCV_AUTHCHALLENGE(duplicate), + TEST_RCV_AUTHCHALLENGE(nocerts), + TEST_RCV_AUTHCHALLENGE(tooshort), + TEST_RCV_AUTHCHALLENGE(truncated), + TEST_RCV_AUTHCHALLENGE(nonzero_circid), + + END_OF_TESTCASES +}; +