diff options
author | David Goulet <dgoulet@torproject.org> | 2016-11-15 15:09:27 -0500 |
---|---|---|
committer | David Goulet <dgoulet@torproject.org> | 2017-01-18 16:58:54 -0500 |
commit | 50cfc9834094acf33bd5afff6a2a36f7f17e70e0 (patch) | |
tree | d9505ce3462b861efd67fcbc9554e667af922366 | |
parent | 5208085be1deff4711b634b108052970aed6a127 (diff) | |
download | tor-50cfc9834094acf33bd5afff6a2a36f7f17e70e0.tar.gz tor-50cfc9834094acf33bd5afff6a2a36f7f17e70e0.zip |
prop224: Add unit tests for INTRODUCE1 support
Signed-off-by: David Goulet <dgoulet@torproject.org>
-rw-r--r-- | src/or/hs_intropoint.c | 14 | ||||
-rw-r--r-- | src/or/hs_intropoint.h | 9 | ||||
-rw-r--r-- | src/or/relay.c | 10 | ||||
-rw-r--r-- | src/or/relay.h | 5 | ||||
-rw-r--r-- | src/test/test_hs_intropoint.c | 328 |
5 files changed, 354 insertions, 12 deletions
diff --git a/src/or/hs_intropoint.c b/src/or/hs_intropoint.c index b7dae7d3c4..25721e93c6 100644 --- a/src/or/hs_intropoint.c +++ b/src/or/hs_intropoint.c @@ -58,7 +58,7 @@ get_auth_key_from_cell(ed25519_public_key_t *auth_key_out, default: /* Getting here is really bad as it means we got a unknown cell type from * this file where every call has an hardcoded value. */ - tor_assert(0); + tor_assert(0); /* LCOV_EXCL_LINE */ } tor_assert(key_array); tor_assert(auth_key_len == sizeof(auth_key_out->pubkey)); @@ -371,7 +371,7 @@ send_introduce_ack_cell(or_circuit_t *circ, hs_intro_ack_status_t status) /* Validate a parsed INTRODUCE1 <b>cell</b>. Return 0 if valid or else a * negative value for an invalid cell that should be NACKed. */ -static int +STATIC int validate_introduce1_parsed_cell(const hs_cell_introduce1_t *cell) { size_t legacy_key_id_len; @@ -420,7 +420,7 @@ validate_introduce1_parsed_cell(const hs_cell_introduce1_t *cell) * everything went well, or -1 if an error occured. This function is in charge * of sending back an INTRODUCE_ACK cell and will close client_circ on error. */ -static int +STATIC int handle_introduce1(or_circuit_t *client_circ, const uint8_t *request, size_t request_len) { @@ -476,10 +476,12 @@ handle_introduce1(or_circuit_t *client_circ, const uint8_t *request, if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(service_circ), RELAY_COMMAND_INTRODUCE2, (char *) request, request_len, NULL)) { + /* LCOV_EXCL_START */ log_warn(LD_REND, "Unable to send INTRODUCE2 cell to the service."); /* Inform the client that we can't relay the cell. */ status = HS_INTRO_ACK_STATUS_CANT_RELAY; goto send_ack; + /* LCOV_EXCL_STOP */ } /* Success! Send an INTRODUCE_ACK success status onto the client circuit. */ @@ -489,10 +491,12 @@ handle_introduce1(or_circuit_t *client_circ, const uint8_t *request, send_ack: /* Send the INTRODUCE_ACK cell to the client with a specific status. */ if (send_introduce_ack_cell(client_circ, status) < 0) { + /* LCOV_EXCL_START */ log_warn(LD_REND, "Unable to send an INTRODUCE ACK status %d to client.", status); /* Circuit has been closed on failure of transmission. */ goto done; + /* LCOV_EXCL_STOP */ } if (status != HS_INTRO_ACK_STATUS_SUCCESS) { /* We just sent a NACK that is a non success status code so close the @@ -507,7 +511,7 @@ handle_introduce1(or_circuit_t *client_circ, const uint8_t *request, /* Identify if the encoded cell we just received is a legacy one or not. The * <b>request</b> should be at least DIGEST_LEN bytes long. */ -static int +STATIC int introduce1_cell_is_legacy(const uint8_t *request) { tor_assert(request); @@ -524,7 +528,7 @@ introduce1_cell_is_legacy(const uint8_t *request) /* Return true iff the circuit <b>circ</b> is suitable for receiving an * INTRODUCE1 cell. */ -static int +STATIC int circuit_is_suitable_for_introduce1(const or_circuit_t *circ) { tor_assert(circ); diff --git a/src/or/hs_intropoint.h b/src/or/hs_intropoint.h index 08b3470bca..e6024a858f 100644 --- a/src/or/hs_intropoint.h +++ b/src/or/hs_intropoint.h @@ -37,6 +37,9 @@ int hs_intro_circuit_is_suitable_for_establish_intro(const or_circuit_t *circ); #ifdef HS_INTROPOINT_PRIVATE +#include "hs/cell_establish_intro.h" +#include "hs/cell_introduce1.h" + STATIC int verify_establish_intro_cell(const hs_cell_establish_intro_t *out, const uint8_t *circuit_key_material, @@ -46,6 +49,12 @@ STATIC void get_auth_key_from_cell(ed25519_public_key_t *auth_key_out, unsigned int cell_type, const void *cell); +STATIC int introduce1_cell_is_legacy(const uint8_t *request); +STATIC int handle_introduce1(or_circuit_t *client_circ, + const uint8_t *request, size_t request_len); +STATIC int validate_introduce1_parsed_cell(const hs_cell_introduce1_t *cell); +STATIC int circuit_is_suitable_for_introduce1(const or_circuit_t *circ); + #endif /* HS_INTROPOINT_PRIVATE */ #endif /* TOR_HS_INTRO_H */ diff --git a/src/or/relay.c b/src/or/relay.c index 8d48239e47..2e76a8ec36 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -595,11 +595,11 @@ relay_command_to_string(uint8_t command) * If you can't send the cell, mark the circuit for close and return -1. Else * return 0. */ -int -relay_send_command_from_edge_(streamid_t stream_id, circuit_t *circ, - uint8_t relay_command, const char *payload, - size_t payload_len, crypt_path_t *cpath_layer, - const char *filename, int lineno) +MOCK_IMPL(int, +relay_send_command_from_edge_,(streamid_t stream_id, circuit_t *circ, + uint8_t relay_command, const char *payload, + size_t payload_len, crypt_path_t *cpath_layer, + const char *filename, int lineno)) { cell_t cell; relay_header_t rh; diff --git a/src/or/relay.h b/src/or/relay.h index 26b594ece6..3acf3ee0e3 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -20,10 +20,11 @@ int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, void relay_header_pack(uint8_t *dest, const relay_header_t *src); void relay_header_unpack(relay_header_t *dest, const uint8_t *src); -int relay_send_command_from_edge_(streamid_t stream_id, circuit_t *circ, +MOCK_DECL(int, +relay_send_command_from_edge_,(streamid_t stream_id, circuit_t *circ, uint8_t relay_command, const char *payload, size_t payload_len, crypt_path_t *cpath_layer, - const char *filename, int lineno); + const char *filename, int lineno)); /* Indicates to relay_send_command_from_edge() that it is a control cell. */ #define CONTROL_CELL_ID 0 #define relay_send_command_from_edge(stream_id, circ, relay_command, payload, \ diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c index 7741cffa62..f8f4460e4a 100644 --- a/src/test/test_hs_intropoint.c +++ b/src/test/test_hs_intropoint.c @@ -13,11 +13,16 @@ #include "test.h" #include "crypto.h" +#include "log_test_helpers.h" #include "or.h" #include "ht.h" +/* Trunnel. */ #include "hs/cell_establish_intro.h" +#include "hs/cell_introduce1.h" +#include "hs/cell_common.h" + #include "hs_service.h" #include "hs_circuitmap.h" #include "hs_intropoint.h" @@ -25,6 +30,7 @@ #include "circuitlist.h" #include "circuituse.h" #include "rendservice.h" +#include "relay.h" /* Mock function to avoid networking in unittests */ static int @@ -34,6 +40,80 @@ mock_send_intro_established_cell(or_circuit_t *circ) return 0; } +static int +mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ, + uint8_t relay_command, const char *payload, + size_t payload_len, crypt_path_t *cpath_layer, + const char *filename, int lineno) +{ + (void) stream_id; + (void) circ; + (void) relay_command; + (void) payload; + (void) payload_len; + (void) cpath_layer; + (void) filename; + (void) lineno; + return 0; +} + +static or_circuit_t * +helper_create_intro_circuit(void) +{ + or_circuit_t *circ = or_circuit_new(0, NULL); + tt_assert(circ); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + done: + return circ; +} + +static hs_cell_introduce1_t * +helper_create_introduce1_cell(void) +{ + hs_cell_introduce1_t *cell = NULL; + ed25519_keypair_t auth_key_kp; + + /* Generate the auth_key of the cell. */ + if (ed25519_keypair_generate(&auth_key_kp, 0) < 0) { + goto err; + } + + cell = hs_cell_introduce1_new(); + tt_assert(cell); + + /* Set the auth key. */ + { + size_t auth_key_len = sizeof(auth_key_kp.pubkey); + hs_cell_introduce1_set_auth_key_type(cell, + HS_INTRO_AUTH_KEY_TYPE_ED25519); + hs_cell_introduce1_set_auth_key_len(cell, auth_key_len); + hs_cell_introduce1_setlen_auth_key(cell, auth_key_len); + uint8_t *auth_key_ptr = hs_cell_introduce1_getarray_auth_key(cell); + memcpy(auth_key_ptr, auth_key_kp.pubkey.pubkey, auth_key_len); + } + + /* Set the cell extentions to none. */ + { + cell_extension_t *ext = cell_extension_new(); + cell_extension_set_num(ext, 0); + hs_cell_introduce1_set_extensions(cell, ext); + } + + /* Set the encrypted section to some data. */ + { + size_t enc_len = 128; + hs_cell_introduce1_setlen_encrypted(cell, enc_len); + uint8_t *enc_ptr = hs_cell_introduce1_getarray_encrypted(cell); + memset(enc_ptr, 'a', enc_len); + } + + return cell; + err: + done: + hs_cell_introduce1_free(cell); + return NULL; +} + /* Try sending an ESTABLISH_INTRO cell on a circuit that is already an intro * point. Should fail. */ static void @@ -341,6 +421,242 @@ test_intro_point_registration(void *arg) UNMOCK(hs_intro_send_intro_established_cell); } +static void +test_introduce1_suitable_circuit(void *arg) +{ + int ret; + or_circuit_t *circ = NULL; + + (void) arg; + + /* Valid suitable circuit. */ + { + circ = or_circuit_new(0, NULL); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + ret = circuit_is_suitable_for_introduce1(circ); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 1); + } + + /* Test if the circuit purpose safeguard works correctly. */ + { + circ = or_circuit_new(0, NULL); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); + ret = circuit_is_suitable_for_introduce1(circ); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 0); + } + + /* Test the non-edge circuit safeguard works correctly. */ + { + circ = or_circuit_new(0, NULL); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + /* Bogus pointer, the check is against NULL on n_chan. */ + circ->base_.n_chan = (channel_t *) circ; + ret = circuit_is_suitable_for_introduce1(circ); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 0); + } + + /* Mangle the circuit a bit more so see if our only one INTRODUCE1 cell + * limit works correctly. */ + { + circ = or_circuit_new(0, NULL); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + circ->already_received_introduce1 = 1; + ret = circuit_is_suitable_for_introduce1(circ); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 0); + } + + done: + ; +} + +static void +test_introduce1_is_legacy(void *arg) +{ + int ret; + uint8_t request[256]; + + (void) arg; + + /* For a cell to be considered legacy, according to the specification, the + * first 20 bytes MUST BE non-zero else it's a v3 cell. */ + memset(request, 'a', DIGEST_LEN); + memset(request + DIGEST_LEN, 0, sizeof(request) - DIGEST_LEN); + ret = introduce1_cell_is_legacy(request); + tt_int_op(ret, OP_EQ, 1); + + /* This is a NON legacy cell. */ + memset(request, 0, DIGEST_LEN); + memset(request + DIGEST_LEN, 'a', sizeof(request) - DIGEST_LEN); + ret = introduce1_cell_is_legacy(request); + tt_int_op(ret, OP_EQ, 0); + + done: + ; +} + +static void +test_introduce1_validation(void *arg) +{ + int ret; + hs_cell_introduce1_t *cell = NULL; + + (void) arg; + + /* Create our decoy cell that we'll modify as we go to test the validation + * function of that parsed cell. */ + cell = helper_create_introduce1_cell(); + + /* It should NOT be a legacy cell which will trigger a BUG(). */ + memset(cell->legacy_key_id, 'a', sizeof(cell->legacy_key_id)); + tor_capture_bugs_(1); + ret = validate_introduce1_parsed_cell(cell); + tor_end_capture_bugs_(); + tt_int_op(ret, OP_EQ, -1); + /* Reset legacy ID and make sure it's correct. */ + memset(cell->legacy_key_id, 0, sizeof(cell->legacy_key_id)); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + + /* Non existing auth key type. */ + cell->auth_key_type = 42; + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + /* Reset is to correct value and make sure it's correct. */ + cell->auth_key_type = HS_INTRO_AUTH_KEY_TYPE_ED25519; + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + + /* Really bad key length. */ + cell->auth_key_len = 0; + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + cell->auth_key_len = UINT16_MAX; + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + /* Correct size, let's try that. */ + cell->auth_key_len = sizeof(ed25519_public_key_t); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + /* Set an invalid size of the auth key buffer. */ + hs_cell_introduce1_setlen_auth_key(cell, 3); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + /* Reset auth key buffer and make sure it works. */ + hs_cell_introduce1_setlen_auth_key(cell, sizeof(ed25519_public_key_t)); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + + /* Empty encrypted section. */ + hs_cell_introduce1_setlen_encrypted(cell, 0); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + /* Reset it to some non zero bytes and validate. */ + hs_cell_introduce1_setlen_encrypted(cell, 1); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + + done: + hs_cell_introduce1_free(cell); +} + +static void +test_received_introduce1_handling(void *arg) +{ + int ret; + uint8_t *request = NULL, buf[128]; + hs_cell_introduce1_t *cell = NULL; + or_circuit_t *circ = NULL; + + (void) arg; + + MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge); + + hs_circuitmap_init(); + + /* Too small request length. An INTRODUCE1 expect at the very least a + * DIGEST_LEN size. */ + { + circ = helper_create_intro_circuit(); + ret = hs_intro_received_introduce1(circ, buf, DIGEST_LEN - 1); + tt_int_op(ret, OP_EQ, -1); + circuit_free(TO_CIRCUIT(circ)); + } + + /* We have a unit test only for the suitability of a circuit to receive an + * INTRODUCE1 cell so from now on we'll only test the handling of a cell. */ + + /* Bad request. */ + { + circ = helper_create_intro_circuit(); + uint8_t test[2]; /* Too small request. */ + ret = handle_introduce1(circ, test, sizeof(test)); + tor_free(circ->p_chan); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, -1); + } + + /* Valid case. */ + { + cell = helper_create_introduce1_cell(); + size_t request_len = hs_cell_introduce1_encoded_len(cell); + tt_size_op(request_len, OP_GT, 0); + request = tor_malloc_zero(request_len); + ssize_t encoded_len = hs_cell_introduce1_encode(request, request_len, cell); + tt_size_op(encoded_len, OP_GT, 0); + + circ = helper_create_intro_circuit(); + or_circuit_t *service_circ = helper_create_intro_circuit(); + circuit_change_purpose(TO_CIRCUIT(service_circ), CIRCUIT_PURPOSE_INTRO_POINT); + /* Register the circuit in the map for the auth key of the cell. */ + ed25519_public_key_t auth_key; + const uint8_t *cell_auth_key = + hs_cell_introduce1_getconstarray_auth_key(cell); + memcpy(auth_key.pubkey, cell_auth_key, ED25519_PUBKEY_LEN); + hs_circuitmap_register_intro_circ_v3(service_circ, &auth_key); + ret = hs_intro_received_introduce1(circ, request, request_len); + circuit_free(TO_CIRCUIT(circ)); + circuit_free(TO_CIRCUIT(service_circ)); + tt_int_op(ret, OP_EQ, 0); + } + + /* Valid legacy cell. */ + { + tor_free(request); + hs_cell_introduce1_free(cell); + cell = helper_create_introduce1_cell(); + uint8_t *legacy_key_id = hs_cell_introduce1_getarray_legacy_key_id(cell); + memset(legacy_key_id, 'a', DIGEST_LEN); + /* Add an arbitrary amount of data for the payload of a v2 cell. */ + size_t request_len = hs_cell_introduce1_encoded_len(cell) + 256; + tt_size_op(request_len, OP_GT, 0); + request = tor_malloc_zero(request_len + 256); + ssize_t encoded_len = hs_cell_introduce1_encode(request, request_len, cell); + tt_size_op(encoded_len, OP_GT, 0); + + circ = helper_create_intro_circuit(); + or_circuit_t *service_circ = helper_create_intro_circuit(); + circuit_change_purpose(TO_CIRCUIT(service_circ), CIRCUIT_PURPOSE_INTRO_POINT); + /* Register the circuit in the map for the auth key of the cell. */ + uint8_t token[REND_TOKEN_LEN]; + memcpy(token, legacy_key_id, sizeof(token)); + hs_circuitmap_register_intro_circ_v2(service_circ, token); + ret = hs_intro_received_introduce1(circ, request, request_len); + circuit_free(TO_CIRCUIT(circ)); + circuit_free(TO_CIRCUIT(service_circ)); + tt_int_op(ret, OP_EQ, 0); + } + + done: + hs_cell_introduce1_free(cell); + tor_free(request); + hs_circuitmap_free_all(); + UNMOCK(relay_send_command_from_edge_); +} + struct testcase_t hs_intropoint_tests[] = { { "intro_point_registration", test_intro_point_registration, TT_FORK, NULL, NULL }, @@ -357,6 +673,18 @@ struct testcase_t hs_intropoint_tests[] = { { "receive_establish_intro_wrong_sig", test_establish_intro_wrong_sig, TT_FORK, NULL, NULL }, + { "introduce1_suitable_circuit", + test_introduce1_suitable_circuit, TT_FORK, NULL, NULL }, + + { "introduce1_is_legacy", + test_introduce1_is_legacy, TT_FORK, NULL, NULL }, + + { "introduce1_validation", + test_introduce1_validation, TT_FORK, NULL, NULL }, + + { "received_introduce1_handling", + test_received_introduce1_handling, TT_FORK, NULL, NULL }, + END_OF_TESTCASES }; |