commit fca2f64e2f563c07e2d5467adc49914bc4545e36 Author: David Goulet dgoulet@torproject.org Date: Fri Jul 21 17:48:18 2017 -0400
prop224: Handle INTRODUCE_ACK cell
The client is now able to handle an INTRODUCE_ACK cell and do the appropriate actions.
An intro point failure cache is missing and a way to close all intro point that were launched in parallel. Some notes are in the comment for that.
Signed-off-by: David Goulet dgoulet@torproject.org --- src/or/circuitlist.c | 27 ++++++++++++ src/or/circuitlist.h | 3 ++ src/or/hs_cell.c | 12 +++++ src/or/hs_cell.h | 3 ++ src/or/hs_client.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/or/hs_client.h | 4 ++ src/or/rendclient.c | 12 ----- src/or/rendcommon.c | 2 +- 8 files changed, 171 insertions(+), 13 deletions(-)
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index d891c89f3..c2d947bea 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -1498,6 +1498,33 @@ circuit_get_ready_rend_circ_by_rend_data(const rend_data_t *rend_data) return NULL; }
+/* Return an origin circuit such that: + * - Identifier identity key matches, + * - Rendezvous cookie matches + * - Circuit is not marked for close + * - Circuit has purpose CIRCUIT_PURPOSE_C_REND_READY. + * + * Return NULL if no such circuit exits. */ +origin_circuit_t * +circuit_get_ready_rend_by_hs_ident(const hs_ident_circuit_t *ident) +{ + SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) { + if (!circ->marked_for_close && + circ->purpose == CIRCUIT_PURPOSE_C_REND_READY) { + origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); + if (ocirc->hs_ident && + ed25519_pubkey_eq(&ident->identity_pk, + ô->hs_ident->identity_pk) && + tor_memeq(ident->rendezvous_cookie, + ocirc->hs_ident->rendezvous_cookie, + HS_REND_COOKIE_LEN)) { + return ocirc; + } + } + } SMARTLIST_FOREACH_END(circ); + return NULL; +} + /** Return the first service introduction circuit originating from the global * circuit list after <b>start</b> or at the start of the list if <b>start</b> * is NULL. Return NULL if no circuit is found. diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h index 048cd5f76..f2a3c4313 100644 --- a/src/or/circuitlist.h +++ b/src/or/circuitlist.h @@ -13,6 +13,7 @@ #define TOR_CIRCUITLIST_H
#include "testsupport.h" +#include "hs_ident.h"
MOCK_DECL(smartlist_t *, circuit_get_global_list, (void)); smartlist_t *circuit_get_global_origin_circuit_list(void); @@ -45,6 +46,8 @@ void circuit_unlink_all_from_channel(channel_t *chan, int reason); origin_circuit_t *circuit_get_by_global_id(uint32_t id); origin_circuit_t *circuit_get_ready_rend_circ_by_rend_data( const rend_data_t *rend_data); +origin_circuit_t *circuit_get_ready_rend_by_hs_ident( + const hs_ident_circuit_t *ident); origin_circuit_t *circuit_get_next_by_pk_and_purpose(origin_circuit_t *start, const uint8_t *digest, uint8_t purpose); origin_circuit_t *circuit_get_next_service_intro_circ(origin_circuit_t *start); diff --git a/src/or/hs_cell.c b/src/or/hs_cell.c index 1f9df825f..c5ea6e4db 100644 --- a/src/or/hs_cell.c +++ b/src/or/hs_cell.c @@ -875,6 +875,18 @@ hs_cell_parse_introduce_ack(const uint8_t *payload, size_t payload_len)
tor_assert(payload);
+ /* If it is a legacy IP, rend-spec.txt specifies that a ACK is 0 byte and a + * NACK is 1 byte. We can't use the legacy function for this so we have to + * do a special case. */ + if (payload_len <= 1) { + if (payload_len == 0) { + ret = HS_CELL_INTRO_ACK_SUCCESS; + } else { + ret = HS_CELL_INTRO_ACK_FAILURE; + } + goto end; + } + if (trn_cell_introduce_ack_parse(&cell, payload, payload_len) < 0) { log_info(LD_REND, "Invalid INTRODUCE_ACK cell. Unable to parse it."); goto end; diff --git a/src/or/hs_cell.h b/src/or/hs_cell.h index 606a08dd6..14dd31c9d 100644 --- a/src/or/hs_cell.h +++ b/src/or/hs_cell.h @@ -111,6 +111,9 @@ ssize_t hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data, const origin_circuit_t *circ, const hs_service_t *service); int hs_cell_parse_introduce_ack(const uint8_t *payload, size_t payload_len); +int hs_cell_parse_rendezvous2(const uint8_t *payload, size_t payload_len, + uint8_t *handshake_info, + size_t handshake_info_len);
#endif /* TOR_HS_CELL_H */
diff --git a/src/or/hs_client.c b/src/or/hs_client.c index a593999f2..ff3d12748 100644 --- a/src/or/hs_client.c +++ b/src/or/hs_client.c @@ -491,6 +491,99 @@ client_get_random_intro(const ed25519_public_key_t *service_pk) return ei; }
+/* Called when we get an INTRODUCE_ACK success status code. Do the appropriate + * actions for the rendezvous point and finally close intro_circ. */ +static void +handle_introduce_ack_success(origin_circuit_t *intro_circ) +{ + origin_circuit_t *rend_circ = NULL; + + tor_assert(intro_circ); + + log_info(LD_REND, "Received INTRODUCE_ACK ack! Informing rendezvous"); + + /* Get the rendezvous circuit matching this intro point circuit. + * XXX Replace this by our hs circuitmap to support client? */ + rend_circ = circuit_get_ready_rend_by_hs_ident(intro_circ->hs_ident); + if (rend_circ == NULL) { + log_warn(LD_REND, "Can't find any rendezvous circuit. Stopping"); + goto end; + } + + assert_circ_anonymity_ok(rend_circ, get_options()); + circuit_change_purpose(TO_CIRCUIT(rend_circ), + CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED); + /* Set timestamp_dirty, because circuit_expire_building expects it to + * specify when a circuit entered the + * CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED state. */ + TO_CIRCUIT(rend_circ)->timestamp_dirty = time(NULL); + + end: + /* We don't need the intro circuit anymore. It did what it had to do! */ + circuit_change_purpose(TO_CIRCUIT(intro_circ), + CIRCUIT_PURPOSE_C_INTRODUCE_ACKED); + circuit_mark_for_close(TO_CIRCUIT(intro_circ), END_CIRC_REASON_FINISHED); + + /* XXX: Close pending intro circuits we might have in parallel. */ + return; +} + +/* Called when we get an INTRODUCE_ACK failure status code. Depending on our + * failure cache status, either close the circuit or re-extend to a new + * introduction point. */ +static void +handle_introduce_ack_bad(origin_circuit_t *circ, int status) +{ + tor_assert(circ); + + log_info(LD_REND, "Received INTRODUCE_ACK nack by %s. Reason: %u", + safe_str_client(extend_info_describe(circ->build_state->chosen_exit)), + status); + + /* It's a NAK. The introduction point didn't relay our request. */ + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_INTRODUCING); + + /* XXX: Report this failure for the intro point failure cache. Depending on + * how many times we've tried this intro point, close it or reextend. */ +} + +/* Called when we get an INTRODUCE_ACK on the intro circuit circ. The encoded + * cell is in payload of length payload_len. Return 0 on success else a + * negative value. The circuit is either close or reuse to re-extend to a new + * introduction point. */ +static int +handle_introduce_ack(origin_circuit_t *circ, const uint8_t *payload, + size_t payload_len) +{ + int status, ret = -1; + + tor_assert(circ); + tor_assert(circ->build_state); + tor_assert(circ->build_state->chosen_exit); + assert_circ_anonymity_ok(circ, get_options()); + tor_assert(payload); + + status = hs_cell_parse_introduce_ack(payload, payload_len); + switch (status) { + case HS_CELL_INTRO_ACK_SUCCESS: + ret = 0; + handle_introduce_ack_success(circ); + break; + case HS_CELL_INTRO_ACK_FAILURE: + case HS_CELL_INTRO_ACK_BADFMT: + case HS_CELL_INTRO_ACK_NORELAY: + handle_introduce_ack_bad(circ, status); + break; + default: + log_info(LD_PROTOCOL, "Unknown INTRODUCE_ACK status code %u from %s", + status, + safe_str_client(extend_info_describe(circ->build_state->chosen_exit))); + break; + } + + return ret; +} + /* ========== */ /* Public API */ /* ========== */ @@ -759,4 +852,32 @@ hs_client_get_random_intro_from_edge(const edge_connection_t *edge_conn) client_get_random_intro(&edge_conn->hs_ident->identity_pk) : rend_client_get_random_intro(edge_conn->rend_data); } +/* Called when get an INTRODUCE_ACK cell on the introduction circuit circ. + * Return 0 on success else a negative value is returned. The circuit will be + * closed or reuse to extend again to another intro point. */ +int +hs_client_receive_introduce_ack(origin_circuit_t *circ, + const uint8_t *payload, size_t payload_len) +{ + int ret = -1; + + tor_assert(circ); + tor_assert(payload); + + if (TO_CIRCUIT(circ)->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { + log_warn(LD_PROTOCOL, "Unexpected INTRODUCE_ACK on circuit %u.", + (unsigned int) TO_CIRCUIT(circ)->n_circ_id); + circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); + goto end; + } + + ret = (circ->hs_ident) ? handle_introduce_ack(circ, payload, payload_len) : + rend_client_introduction_acked(circ, payload, + payload_len); + /* For path bias: This circuit was used successfully. NACK or ACK counts. */ + pathbias_mark_use_success(circ); + + end: + return ret; +}
diff --git a/src/or/hs_client.h b/src/or/hs_client.h index c41baa2ce..1c0f8a4d2 100644 --- a/src/or/hs_client.h +++ b/src/or/hs_client.h @@ -31,6 +31,10 @@ void hs_client_circuit_has_opened(origin_circuit_t *circ); int hs_client_receive_rendezvous_acked(origin_circuit_t *circ, const uint8_t *payload, size_t payload_len); +int hs_client_receive_introduce_ack(origin_circuit_t *circ, + const uint8_t *payload, + size_t payload_len); + void hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident);
extend_info_t *hs_client_get_random_intro_from_edge( diff --git a/src/or/rendclient.c b/src/or/rendclient.c index a6fb88624..683a3916e 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -391,23 +391,11 @@ rend_client_introduction_acked(origin_circuit_t *circ, origin_circuit_t *rendcirc; (void) request; // XXXX Use this.
- if (circ->base_.purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { - log_warn(LD_PROTOCOL, - "Received REND_INTRODUCE_ACK on unexpected circuit %u.", - (unsigned)circ->base_.n_circ_id); - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); - return -1; - } - tor_assert(circ->build_state); tor_assert(circ->build_state->chosen_exit); assert_circ_anonymity_ok(circ, options); tor_assert(circ->rend_data);
- /* For path bias: This circuit was used successfully. Valid - * nacks and acks count. */ - pathbias_mark_use_success(circ); - if (request_len == 0) { /* It's an ACK; the introduction point relayed our introduction request. */ /* Locate the rend circ which is waiting to hear about this ack, diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 7e5ba6b6f..86dc913cd 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -782,7 +782,7 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint, break; case RELAY_COMMAND_INTRODUCE_ACK: if (origin_circ) - r = rend_client_introduction_acked(origin_circ,payload,length); + r = hs_client_receive_introduce_ack(origin_circ,payload,length); break; case RELAY_COMMAND_RENDEZVOUS1: if (or_circ)
tor-commits@lists.torproject.org