[tor-commits] [tor/master] prop224: Pick rendezvous point of protover HSRend=2

nickm at torproject.org nickm at torproject.org
Thu Sep 7 12:37:25 UTC 2017


commit c527cde82f74849ec3b183159d20441019b77e2e
Author: David Goulet <dgoulet at torproject.org>
Date:   Wed Aug 30 10:13:22 2017 -0400

    prop224: Pick rendezvous point of protover HSRend=2
    
    Version 3 hidden service needs rendezvous point that have the protocol version
    HSRend >= 2 else the rendezvous cells are rejected.
    
    Fixes #23361
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 src/or/circuitbuild.c | 28 ++++++++++++++++++++++------
 src/or/circuitlist.c  | 32 ++++++++++++++++++++++++++++++++
 src/or/circuituse.c   | 10 ++++++++++
 src/or/circuituse.h   |  3 +++
 src/or/hs_client.c    | 17 +++++++++++++++--
 src/or/nodelist.c     | 22 ++++++++++++++++++++++
 src/or/nodelist.h     |  1 +
 src/or/or.h           | 10 +++++++++-
 src/or/protover.h     |  2 ++
 src/or/routerlist.c   | 16 ++++++++++++----
 src/or/routerparse.c  |  3 +++
 11 files changed, 131 insertions(+), 13 deletions(-)

diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 65cd7bd5d..e5c6767d4 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -71,7 +71,8 @@ static channel_t * channel_connect_for_circuit(const tor_addr_t *addr,
 static int circuit_deliver_create_cell(circuit_t *circ,
                                        const create_cell_t *create_cell,
                                        int relayed);
-static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit);
+static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit,
+                                 int is_hs_v3_rp_circuit);
 static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
 static int onion_extend_cpath(origin_circuit_t *circ);
 static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
@@ -505,10 +506,15 @@ circuit_establish_circuit(uint8_t purpose, extend_info_t *exit_ei, int flags)
 {
   origin_circuit_t *circ;
   int err_reason = 0;
+  int is_hs_v3_rp_circuit = 0;
+
+  if (flags & CIRCLAUNCH_IS_V3_RP) {
+    is_hs_v3_rp_circuit = 1;
+  }
 
   circ = origin_circuit_init(purpose, flags);
 
-  if (onion_pick_cpath_exit(circ, exit_ei) < 0 ||
+  if (onion_pick_cpath_exit(circ, exit_ei, is_hs_v3_rp_circuit) < 0 ||
       onion_populate_cpath(circ) < 0) {
     circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_NOPATH);
     return NULL;
@@ -2156,7 +2162,8 @@ pick_rendezvous_node(router_crn_flags_t flags)
  */
 static const node_t *
 choose_good_exit_server(uint8_t purpose,
-                        int need_uptime, int need_capacity, int is_internal)
+                        int need_uptime, int need_capacity, int is_internal,
+                        int need_hs_v3)
 {
   const or_options_t *options = get_options();
   router_crn_flags_t flags = CRN_NEED_DESC;
@@ -2164,6 +2171,8 @@ choose_good_exit_server(uint8_t purpose,
     flags |= CRN_NEED_UPTIME;
   if (need_capacity)
     flags |= CRN_NEED_CAPACITY;
+  if (need_hs_v3)
+    flags |= CRN_RENDEZVOUS_V3;
 
   switch (purpose) {
     case CIRCUIT_PURPOSE_C_GENERAL:
@@ -2263,9 +2272,15 @@ warn_if_last_router_excluded(origin_circuit_t *circ,
 
 /** Decide a suitable length for circ's cpath, and pick an exit
  * router (or use <b>exit</b> if provided). Store these in the
- * cpath. Return 0 if ok, -1 if circuit should be closed. */
+ * cpath.
+ *
+ * If <b>is_hs_v3_rp_circuit</b> is set, then this exit should be suitable to
+ * be used as an HS v3 rendezvous point.
+ *
+ * Return 0 if ok, -1 if circuit should be closed. */
 static int
-onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
+onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei,
+                      int is_hs_v3_rp_circuit)
 {
   cpath_build_state_t *state = circ->build_state;
 
@@ -2289,7 +2304,8 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
   } else { /* we have to decide one */
     const node_t *node =
       choose_good_exit_server(circ->base_.purpose, state->need_uptime,
-                              state->need_capacity, state->is_internal);
+                              state->need_capacity, state->is_internal,
+                              is_hs_v3_rp_circuit);
     if (!node) {
       log_warn(LD_CIRC,"Failed to choose an exit server");
       return -1;
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 2f3fe327e..774edc90b 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -1609,6 +1609,30 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
   return NULL;
 }
 
+/** We might cannibalize this circuit: Return true if its last hop can be used
+ *  as a v3 rendezvous point. */
+static int
+circuit_can_be_cannibalized_for_v3_rp(const origin_circuit_t *circ)
+{
+  if (!circ->build_state) {
+    return 0;
+  }
+
+  extend_info_t *chosen_exit = circ->build_state->chosen_exit;
+  if (BUG(!chosen_exit)) {
+    return 0;
+  }
+
+  const node_t *rp_node = node_get_by_id(chosen_exit->identity_digest);
+  if (rp_node) {
+    if (node_supports_v3_rendezvous_point(rp_node)) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
 /** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL,
  * has a timestamp_dirty value of 0, has flags matching the CIRCLAUNCH_*
  * flags in <b>flags</b>, and if info is defined, does not already use info
@@ -1691,6 +1715,14 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
             hop = hop->next;
           } while (hop != circ->cpath);
         }
+
+        if ((flags & CIRCLAUNCH_IS_V3_RP) &&
+            !circuit_can_be_cannibalized_for_v3_rp(circ)) {
+          log_debug(LD_GENERAL, "Skipping uncannibalizable circuit for v3 "
+                    "rendezvous point.");
+          goto next;
+        }
+
         if (!best || (best->build_state->need_uptime && !need_uptime))
           best = circ;
       next: ;
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 570b05e57..c229c84d9 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -2290,6 +2290,16 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
       if (want_onehop) flags |= CIRCLAUNCH_ONEHOP_TUNNEL;
       if (need_uptime) flags |= CIRCLAUNCH_NEED_UPTIME;
       if (need_internal) flags |= CIRCLAUNCH_IS_INTERNAL;
+
+      /* If we are about to pick a v3 RP right now, make sure we pick a
+       * rendezvous point that supports the v3 protocol! */
+      if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED &&
+          new_circ_purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND &&
+          ENTRY_TO_EDGE_CONN(conn)->hs_ident) {
+        flags |= CIRCLAUNCH_IS_V3_RP;
+        log_info(LD_GENERAL, "Getting rendezvous circuit to v3 service!");
+      }
+
       circ = circuit_launch_by_extend_info(new_circ_purpose, extend_info,
                                            flags);
     }
diff --git a/src/or/circuituse.h b/src/or/circuituse.h
index e66679586..6c0bcbb35 100644
--- a/src/or/circuituse.h
+++ b/src/or/circuituse.h
@@ -44,6 +44,9 @@ void circuit_build_failed(origin_circuit_t *circ);
 /** Flag to set when the last hop of a circuit doesn't need to be an
  * exit node. */
 #define CIRCLAUNCH_IS_INTERNAL    (1<<3)
+/** Flag to set when we are trying to launch a v3 rendezvous circuit. We need
+ *  to apply some additional filters on the node picked. */
+#define CIRCLAUNCH_IS_V3_RP (1<<4)
 origin_circuit_t *circuit_launch_by_extend_info(uint8_t purpose,
                                                 extend_info_t *info,
                                                 int flags);
diff --git a/src/or/hs_client.c b/src/or/hs_client.c
index 99be058eb..2acc639e4 100644
--- a/src/or/hs_client.c
+++ b/src/or/hs_client.c
@@ -24,6 +24,7 @@
 #include "circuitlist.h"
 #include "circuituse.h"
 #include "connection.h"
+#include "nodelist.h"
 #include "circpathbias.h"
 #include "connection.h"
 #include "hs_ntor.h"
@@ -461,9 +462,21 @@ client_rendezvous_circ_has_opened(origin_circuit_t *circ)
   tor_assert(circ);
   tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
 
+  const extend_info_t *rp_ei = circ->build_state->chosen_exit;
+
+  /* Check that we didn't accidentally choose a node that does not understand
+   * the v3 rendezvous protocol */
+  if (rp_ei) {
+    const node_t *rp_node = node_get_by_id(rp_ei->identity_digest);
+    if (rp_node) {
+      if (BUG(!node_supports_v3_rendezvous_point(rp_node))) {
+        return;
+      }
+    }
+  }
+
   log_info(LD_REND, "Rendezvous circuit has opened to %s.",
-           safe_str_client(
-                extend_info_describe(circ->build_state->chosen_exit)));
+           safe_str_client(extend_info_describe(rp_ei)));
 
   /* Ignore returned value, nothing we can really do. On failure, the circuit
    * will be marked for close. */
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 6acc87f96..f670870f6 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -863,6 +863,28 @@ node_supports_ed25519_hs_intro(const node_t *node)
   return 0;
 }
 
+/** Return true iff <b>node</b> supports to be a rendezvous point for hidden
+ * service version 3 (HSRend=2). */
+int
+node_supports_v3_rendezvous_point(const node_t *node)
+{
+  tor_assert(node);
+
+  if (node->rs) {
+    return node->rs->supports_v3_rendezvous_point;
+  }
+  if (node->ri) {
+    if (node->ri->protocol_list == NULL) {
+      return 0;
+    }
+    return protocol_list_supports_protocol(node->ri->protocol_list,
+                                           PRT_HSREND,
+                                           PROTOVER_HS_RENDEZVOUS_POINT_V3);
+  }
+  tor_assert_nonfatal_unreached_once();
+  return 0;
+}
+
 /** Return the RSA ID key's SHA1 digest for the provided node. */
 const uint8_t *
 node_get_rsa_id_digest(const node_t *node)
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index d16cf0ecf..9676263f7 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -62,6 +62,7 @@ int node_ed25519_id_matches(const node_t *node,
 int node_supports_ed25519_link_authentication(const node_t *node);
 int node_supports_v3_hsdir(const node_t *node);
 int node_supports_ed25519_hs_intro(const node_t *node);
+int node_supports_v3_rendezvous_point(const node_t *node);
 const uint8_t *node_get_rsa_id_digest(const node_t *node);
 
 int node_has_ipv6_addr(const node_t *node);
diff --git a/src/or/or.h b/src/or/or.h
index 5d55094a0..a2707b4d4 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2321,6 +2321,11 @@ typedef struct routerstatus_t {
    * requires HSDir=2. */
   unsigned int supports_v3_hsdir : 1;
 
+  /** True iff this router has a protocol list that allows it to be an hidden
+   * service rendezvous point supporting version 3 as seen in proposal 224.
+   * This requires HSRend=2. */
+  unsigned int supports_v3_rendezvous_point: 1;
+
   unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */
   unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */
   unsigned int bw_is_unmeasured:1; /**< This is a consensus entry, with
@@ -5381,7 +5386,10 @@ typedef enum {
   CRN_PREF_ADDR = 1<<7,
   /* On clients, only provide nodes that we can connect to directly, based on
    * our firewall rules */
-  CRN_DIRECT_CONN = 1<<8
+  CRN_DIRECT_CONN = 1<<8,
+  /* On clients, only provide nodes with HSRend >= 2 protocol version which
+   * is required for hidden service version >= 3. */
+  CRN_RENDEZVOUS_V3 = 1<<9,
 } router_crn_flags_t;
 
 /** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */
diff --git a/src/or/protover.h b/src/or/protover.h
index 2066aeec7..ec8da1a0d 100644
--- a/src/or/protover.h
+++ b/src/or/protover.h
@@ -21,6 +21,8 @@
 #define PROTOVER_HSDIR_V3 2
 /** The protover version number that signifies HSv3 intro point support */
 #define PROTOVER_HS_INTRO_V3 4
+/** The protover version number that signifies HSv3 rendezvous point support */
+#define PROTOVER_HS_RENDEZVOUS_POINT_V3 2
 
 /** List of recognized subprotocols. */
 typedef enum protocol_type_t {
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 989401947..da2dff600 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -2799,6 +2799,7 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
   const int need_desc = (flags & CRN_NEED_DESC) != 0;
   const int pref_addr = (flags & CRN_PREF_ADDR) != 0;
   const int direct_conn = (flags & CRN_DIRECT_CONN) != 0;
+  const int rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0;
 
   smartlist_t *sl=smartlist_new(),
     *excludednodes=smartlist_new();
@@ -2810,12 +2811,19 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
   rule = weight_for_exit ? WEIGHT_FOR_EXIT :
     (need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID);
 
-  /* Exclude relays that allow single hop exit circuits. This is an obsolete
-   * option since 0.2.9.2-alpha and done by default in 0.3.1.0-alpha. */
-  SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
+  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
     if (node_allows_single_hop_exits(node)) {
+      /* Exclude relays that allow single hop exit circuits. This is an
+       * obsolete option since 0.2.9.2-alpha and done by default in
+       * 0.3.1.0-alpha. */
       smartlist_add(excludednodes, node);
-    });
+    } else if (rendezvous_v3 &&
+               !node_supports_v3_rendezvous_point(node)) {
+      /* Exclude relays that do not support to rendezvous for a hidden service
+       * version 3. */
+      smartlist_add(excludednodes, node);
+    }
+  } SMARTLIST_FOREACH_END(node);
 
   if ((r = routerlist_find_my_routerinfo()))
     routerlist_add_node_and_family(excludednodes, r);
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 9c11f4d07..08c3fc0a0 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -2708,6 +2708,9 @@ routerstatus_parse_entry_from_string(memarea_t *area,
     rs->supports_v3_hsdir =
       protocol_list_supports_protocol(tok->args[0], PRT_HSDIR,
                                       PROTOVER_HSDIR_V3);
+    rs->supports_v3_rendezvous_point =
+      protocol_list_supports_protocol(tok->args[0], PRT_HSDIR,
+                                      PROTOVER_HS_RENDEZVOUS_POINT_V3);
   }
   if ((tok = find_opt_by_keyword(tokens, K_V))) {
     tor_assert(tok->n_args == 1);





More information about the tor-commits mailing list