[tor-commits] [tor/master] prop224: Handle INTRODUCE_ACK cell

nickm at torproject.org nickm at torproject.org
Thu Aug 24 19:13:52 UTC 2017


commit fca2f64e2f563c07e2d5467adc49914bc4545e36
Author: David Goulet <dgoulet at 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 at 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,
+                            &ocirc->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)





More information about the tor-commits mailing list