[tor-commits] [tor/master] prop224: Link rendezvous circuit to edge connection

nickm at torproject.org nickm at torproject.org
Wed Aug 9 00:36:37 UTC 2017


commit 30b5c6a95ec9932f17f0b171c60229c1d77f829d
Author: David Goulet <dgoulet at torproject.org>
Date:   Wed May 10 15:04:40 2017 -0400

    prop224: Link rendezvous circuit to edge connection
    
    This commit refactors the handle_hs_exit_conn() function introduced at a prior
    commit that connects the rendezvous circuit to the edge connection used to
    connect to the service virtual port requested in a BEGIN cell.
    
    The refactor adds the support for prop224 adding the
    hs_service_set_conn_addr_port() function that has the same purpose has
    rend_service_set_connection_addr_port() from the legacy code.
    
    The rend_service_set_connection_addr_port() has also been a bit refactored so
    the common code can be shared between the two HS subsystems (legacy and
    prop224).
    
    In terms of functionallity, nothing has changed, we still close the circuits
    in case of failure for the same reasons as the legacy system currently does.
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 src/or/connection_edge.c |  87 +++++++++++++++++++++++++-------------
 src/or/hs_common.c       | 106 +++++++++++++++++++++++++++++++++++++++++++++++
 src/or/hs_common.h       |   2 +
 src/or/hs_service.c      |  85 +++++++++++++++++++++++++++++++++++++
 src/or/hs_service.h      |   2 +
 src/or/rendservice.c     |  95 ++----------------------------------------
 6 files changed, 257 insertions(+), 120 deletions(-)

diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 8e447131f..41e5f88ab 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -76,6 +76,7 @@
 #include "dirserv.h"
 #include "hibernate.h"
 #include "hs_common.h"
+#include "hs_circuit.h"
 #include "main.h"
 #include "nodelist.h"
 #include "policies.h"
@@ -3066,58 +3067,88 @@ begin_cell_parse(const cell_t *cell, begin_cell_t *bcell,
   return 0;
 }
 
-/** For the given <b>circ</b> and the edge connection <b>n_stream</b>, setup
- * the the connection, attach it to the circ and connect it. Return 0 on
- * success or END_CIRC_AT_ORIGIN if we can't find the requested hidden
- * service port where the caller should close the circuit. */
+/** For the given <b>circ</b> and the edge connection <b>conn</b>, setup the
+ * connection, attach it to the circ and connect it. Return 0 on success
+ * or END_CIRC_AT_ORIGIN if we can't find the requested hidden service port
+ * where the caller should close the circuit. */
 static int
-handle_hs_exit_conn(circuit_t *circ, edge_connection_t *n_stream)
+handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn)
 {
-  origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
-  log_info(LD_REND,"begin is for rendezvous. configuring stream.");
-  n_stream->base_.address = tor_strdup("(rendezvous)");
-  n_stream->base_.state = EXIT_CONN_STATE_CONNECTING;
-  n_stream->rend_data = rend_data_dup(origin_circ->rend_data);
-  tor_assert(connection_edge_is_rendezvous_stream(n_stream));
+  int ret;
+  origin_circuit_t *origin_circ;
+
   assert_circuit_ok(circ);
+  tor_assert(circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
+  tor_assert(conn);
 
-  const int r = rend_service_set_connection_addr_port(n_stream, origin_circ);
-  if (r < 0) {
-    log_info(LD_REND,"Didn't find rendezvous service (port %d)",
-             n_stream->base_.port);
+  log_debug(LD_REND, "Connecting the hidden service rendezvous circuit "
+                     "to the service destination.");
+
+  origin_circ = TO_ORIGIN_CIRCUIT(circ);
+  conn->base_.address = tor_strdup("(rendezvous)");
+  conn->base_.state = EXIT_CONN_STATE_CONNECTING;
+
+  /* The circuit either has an hs identifier for v3+ or a rend_data for legacy
+   * service. */
+  if (origin_circ->rend_data) {
+    conn->rend_data = rend_data_dup(origin_circ->rend_data);
+    tor_assert(connection_edge_is_rendezvous_stream(conn));
+    ret = rend_service_set_connection_addr_port(conn, origin_circ);
+  } else if (origin_circ->hs_ident) {
+    /* Setup the identifier to be the one for the circuit service. */
+    conn->hs_ident =
+      hs_ident_edge_conn_new(&origin_circ->hs_ident->identity_pk);
+    ret = hs_service_set_conn_addr_port(origin_circ, conn);
+  } else {
+    /* We should never get here if the circuit's purpose is rendezvous. */
+    tor_assert(0);
+  }
+  if (ret < 0) {
+    log_info(LD_REND, "Didn't find rendezvous service (addr%s, port %d)",
+             fmt_addr(&TO_CONN(conn)->addr), TO_CONN(conn)->port);
     /* Send back reason DONE because we want to make hidden service port
      * scanning harder thus instead of returning that the exit policy
      * didn't match, which makes it obvious that the port is closed,
      * return DONE and kill the circuit. That way, a user (malicious or
      * not) needs one circuit per bad port unless it matches the policy of
      * the hidden service. */
-    relay_send_end_cell_from_edge(n_stream->stream_id, circ,
+    relay_send_end_cell_from_edge(conn->stream_id, circ,
                                   END_STREAM_REASON_DONE,
                                   origin_circ->cpath->prev);
-    connection_free(TO_CONN(n_stream));
+    connection_free(TO_CONN(conn));
 
     /* Drop the circuit here since it might be someone deliberately
      * scanning the hidden service ports. Note that this mitigates port
      * scanning by adding more work on the attacker side to successfully
      * scan but does not fully solve it. */
-    if (r < -1)
+    if (ret < -1) {
       return END_CIRC_AT_ORIGIN;
-    else
+    } else {
       return 0;
+    }
   }
-  assert_circuit_ok(circ);
-  log_debug(LD_REND,"Finished assigning addr/port");
-  n_stream->cpath_layer = origin_circ->cpath->prev; /* link it */
 
-  /* add it into the linked list of p_streams on this circuit */
-  n_stream->next_stream = origin_circ->p_streams;
-  n_stream->on_circuit = circ;
-  origin_circ->p_streams = n_stream;
+  /* Link the circuit and the connection crypt path. */
+  conn->cpath_layer = origin_circ->cpath->prev;
+
+  /* Add it into the linked list of p_streams on this circuit */
+  conn->next_stream = origin_circ->p_streams;
+  origin_circ->p_streams = conn;
+  conn->on_circuit = circ;
   assert_circuit_ok(circ);
 
-  origin_circ->rend_data->nr_streams++;
+  if (origin_circ->rend_data) {
+    origin_circ->rend_data->nr_streams++;
+  } else if (origin_circ->hs_ident) {
+    origin_circ->hs_ident->num_rdv_streams++;
+  } else {
+    /* The previous if/else at the start of the function guarantee that we'll
+     * never end up in a else situation unless it's freed in between. */
+    tor_assert(0);
+  }
 
-  connection_exit_connect(n_stream);
+  /* Connect tor to the hidden service destination. */
+  connection_exit_connect(conn);
 
   /* For path bias: This circuit was used successfully */
   pathbias_mark_use_success(origin_circ);
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index 4d5417afa..20b53bb91 100644
--- a/src/or/hs_common.c
+++ b/src/or/hs_common.c
@@ -32,6 +32,60 @@ static const char *str_ed25519_basepoint =
   "463168356949264781694283940034751631413"
   "07993866256225615783033603165251855960)";
 
+#ifdef HAVE_SYS_UN_H
+
+/** Given <b>ports</b>, a smarlist containing rend_service_port_config_t,
+ * add the given <b>p</b>, a AF_UNIX port to the list. Return 0 on success
+ * else return -ENOSYS if AF_UNIX is not supported (see function in the
+ * #else statement below). */
+static int
+add_unix_port(smartlist_t *ports, rend_service_port_config_t *p)
+{
+  tor_assert(ports);
+  tor_assert(p);
+  tor_assert(p->is_unix_addr);
+
+  smartlist_add(ports, p);
+  return 0;
+}
+
+/** Given <b>conn</b> set it to use the given port <b>p</b> values. Return 0
+ * on success else return -ENOSYS if AF_UNIX is not supported (see function
+ * in the #else statement below). */
+static int
+set_unix_port(edge_connection_t *conn, rend_service_port_config_t *p)
+{
+  tor_assert(conn);
+  tor_assert(p);
+  tor_assert(p->is_unix_addr);
+
+  conn->base_.socket_family = AF_UNIX;
+  tor_addr_make_unspec(&conn->base_.addr);
+  conn->base_.port = 1;
+  conn->base_.address = tor_strdup(p->unix_addr);
+  return 0;
+}
+
+#else /* defined(HAVE_SYS_UN_H) */
+
+static int
+set_unix_port(edge_connection_t *conn, rend_service_port_config_t *p)
+{
+  (void) conn;
+  (void) p;
+  return -ENOSYS;
+}
+
+static int
+add_unix_port(smartlist_t *ports, rend_service_port_config_t *p)
+{
+  (void) ports;
+  (void) p;
+  return -ENOSYS;
+}
+
+#endif /* HAVE_SYS_UN_H */
+
 /* Helper function: The key is a digest that we compare to a node_t object
  * current hsdir_index. */
 static int
@@ -602,6 +656,58 @@ hs_get_subcredential(const ed25519_public_key_t *identity_pk,
   crypto_digest_free(digest);
 }
 
+/* From the given list of hidden service ports, find the matching one from the
+ * given edge connection conn and set the connection address from the service
+ * port object. Return 0 on success or -1 if none. */
+int
+hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn)
+{
+  rend_service_port_config_t *chosen_port;
+  unsigned int warn_once = 0;
+  smartlist_t *matching_ports;
+
+  tor_assert(ports);
+  tor_assert(conn);
+
+  matching_ports = smartlist_new();
+  SMARTLIST_FOREACH_BEGIN(ports, rend_service_port_config_t *, p) {
+    if (TO_CONN(conn)->port != p->virtual_port) {
+      continue;
+    }
+    if (!(p->is_unix_addr)) {
+      smartlist_add(matching_ports, p);
+    } else {
+      if (add_unix_port(matching_ports, p)) {
+        if (!warn_once) {
+          /* Unix port not supported so warn only once. */
+          log_warn(LD_REND, "Saw AF_UNIX virtual port mapping for port %d "
+                            "which is unsupported on this platform. "
+                            "Ignoring it.",
+                   TO_CONN(conn)->port);
+        }
+        warn_once++;
+      }
+    }
+  } SMARTLIST_FOREACH_END(p);
+
+  chosen_port = smartlist_choose(matching_ports);
+  smartlist_free(matching_ports);
+  if (chosen_port) {
+    if (!(chosen_port->is_unix_addr)) {
+      /* Get a non-AF_UNIX connection ready for connection_exit_connect() */
+      tor_addr_copy(&TO_CONN(conn)->addr, &chosen_port->real_addr);
+      TO_CONN(conn)->port = chosen_port->real_port;
+    } else {
+      if (set_unix_port(conn, chosen_port)) {
+        /* Simply impossible to end up here else we were able to add a Unix
+         * port without AF_UNIX support... ? */
+        tor_assert(0);
+      }
+    }
+  }
+  return (chosen_port) ? 0 : -1;
+}
+
 /* Using a base32 representation of a service address, parse its content into
  * the key_out, checksum_out and version_out. Any out variable can be NULL in
  * case the caller would want only one field. checksum_out MUST at least be 2
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index 695f0b895..3670ff379 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -218,6 +218,8 @@ void hs_get_responsible_hsdirs(const ed25519_public_key_t *blinded_pk,
                                uint64_t time_period_num, int is_next_period,
                                int is_client, smartlist_t *responsible_dirs);
 
+int hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn);
+
 #ifdef HS_COMMON_PRIVATE
 
 #ifdef TOR_UNIT_TESTS
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index 760ba1bc3..293c17f6f 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -2386,6 +2386,91 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list)
 /* Public API */
 /* ========== */
 
+/* Given conn, a rendezvous edge connection acting as an exit stream, look up
+ * the hidden service for the circuit circ, and look up the port and address
+ * based on the connection port. Assign the actual connection address.
+ *
+ * Return 0 on success. Return -1 on failure and the caller should NOT close
+ * the circuit. Return -2 on failure and the caller MUST close the circuit for
+ * security reasons. */
+int
+hs_service_set_conn_addr_port(const origin_circuit_t *circ,
+                              edge_connection_t *conn)
+{
+  hs_service_t *service = NULL;
+
+  tor_assert(circ);
+  tor_assert(conn);
+  tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
+  tor_assert(circ->hs_ident);
+
+  get_objects_from_ident(circ->hs_ident, &service, NULL, NULL);
+
+  if (service == NULL) {
+    log_warn(LD_REND, "Unable to find any hidden service associated "
+                      "identity key %s on rendezvous circuit %u.",
+             ed25519_fmt(&circ->hs_ident->identity_pk),
+             TO_CIRCUIT(circ)->n_circ_id);
+    /* We want the caller to close the circuit because it's not a valid
+     * service so no danger. Attempting to bruteforce the entire key space by
+     * opening circuits to learn which service is being hosted here is
+     * impractical. */
+    goto err_close;
+  }
+
+  /* Enforce the streams-per-circuit limit, and refuse to provide a mapping if
+   * this circuit will exceed the limit. */
+  if (service->config.max_streams_per_rdv_circuit > 0 &&
+      (circ->hs_ident->num_rdv_streams >=
+       service->config.max_streams_per_rdv_circuit)) {
+#define MAX_STREAM_WARN_INTERVAL 600
+    static struct ratelim_t stream_ratelim =
+      RATELIM_INIT(MAX_STREAM_WARN_INTERVAL);
+    log_fn_ratelim(&stream_ratelim, LOG_WARN, LD_REND,
+                   "Maximum streams per circuit limit reached on "
+                   "rendezvous circuit %u for service %s. Circuit has "
+                   "%" PRIu64 " out of %" PRIu64 " streams. %s.",
+                   TO_CIRCUIT(circ)->n_circ_id,
+                   service->onion_address,
+                   circ->hs_ident->num_rdv_streams,
+                   service->config.max_streams_per_rdv_circuit,
+                   service->config.max_streams_close_circuit ?
+                    "Closing circuit" : "Ignoring open stream request");
+    if (service->config.max_streams_close_circuit) {
+      /* Service explicitly configured to close immediately. */
+      goto err_close;
+    }
+    /* Exceeding the limit makes tor silently ignore the stream creation
+     * request and keep the circuit open. */
+    goto err_no_close;
+  }
+
+  /* Find a virtual port of that service mathcing the one in the connection if
+   * succesful, set the address in the connection. */
+  if (hs_set_conn_addr_port(service->config.ports, conn) < 0) {
+    log_info(LD_REND, "No virtual port mapping exists for port %d for "
+                      "hidden service %s.",
+             TO_CONN(conn)->port, service->onion_address);
+    if (service->config.allow_unknown_ports) {
+      /* Service explicitly allow connection to unknown ports so close right
+       * away because we do not care about port mapping. */
+      goto err_close;
+    }
+    /* If the service didn't explicitly allow it, we do NOT close the circuit
+     * here to raise the bar in terms of performance for port mapping. */
+    goto err_no_close;
+  }
+
+  /* Success. */
+  return 0;
+ err_close:
+  /* Indicate the caller that the circuit should be closed. */
+  return -2;
+ err_no_close:
+  /* Indicate the caller to NOT close the circuit. */
+  return -1;
+}
+
 /* Add to file_list every filename used by a configured hidden service, and to
  * dir_list every directory path used by a configured hidden service. This is
  * used by the sandbox subsystem to whitelist those. */
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index 7d026fb35..f678aa2c0 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -256,6 +256,8 @@ void hs_service_stage_services(const smartlist_t *service_list);
 int hs_service_load_all_keys(void);
 void hs_service_lists_fnames_for_sandbox(smartlist_t *file_list,
                                          smartlist_t *dir_list);
+int hs_service_set_conn_addr_port(const origin_circuit_t *circ,
+                                  edge_connection_t *conn);
 
 void hs_service_dir_info_changed(void);
 void hs_service_run_scheduled_events(time_t now);
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 7353a4f99..5f4a7c6a7 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -4246,60 +4246,6 @@ rend_service_dump_stats(int severity)
   }
 }
 
-#ifdef HAVE_SYS_UN_H
-
-/** Given <b>ports</b>, a smarlist containing rend_service_port_config_t,
- * add the given <b>p</b>, a AF_UNIX port to the list. Return 0 on success
- * else return -ENOSYS if AF_UNIX is not supported (see function in the
- * #else statement below). */
-static int
-add_unix_port(smartlist_t *ports, rend_service_port_config_t *p)
-{
-  tor_assert(ports);
-  tor_assert(p);
-  tor_assert(p->is_unix_addr);
-
-  smartlist_add(ports, p);
-  return 0;
-}
-
-/** Given <b>conn</b> set it to use the given port <b>p</b> values. Return 0
- * on success else return -ENOSYS if AF_UNIX is not supported (see function
- * in the #else statement below). */
-static int
-set_unix_port(edge_connection_t *conn, rend_service_port_config_t *p)
-{
-  tor_assert(conn);
-  tor_assert(p);
-  tor_assert(p->is_unix_addr);
-
-  conn->base_.socket_family = AF_UNIX;
-  tor_addr_make_unspec(&conn->base_.addr);
-  conn->base_.port = 1;
-  conn->base_.address = tor_strdup(p->unix_addr);
-  return 0;
-}
-
-#else /* defined(HAVE_SYS_UN_H) */
-
-static int
-set_unix_port(edge_connection_t *conn, rend_service_port_config_t *p)
-{
-  (void) conn;
-  (void) p;
-  return -ENOSYS;
-}
-
-static int
-add_unix_port(smartlist_t *ports, rend_service_port_config_t *p)
-{
-  (void) ports;
-  (void) p;
-  return -ENOSYS;
-}
-
-#endif /* HAVE_SYS_UN_H */
-
 /** Given <b>conn</b>, a rendezvous exit stream, look up the hidden service for
  * 'circ', and look up the port and address based on conn-\>port.
  * Assign the actual conn-\>addr and conn-\>port. Return -2 on failure
@@ -4312,9 +4258,6 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
 {
   rend_service_t *service;
   char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
-  smartlist_t *matching_ports;
-  rend_service_port_config_t *chosen_port;
-  unsigned int warn_once = 0;
   const char *rend_pk_digest;
 
   tor_assert(circ->base_.purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
@@ -4350,41 +4293,9 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
       return service->max_streams_close_circuit ? -2 : -1;
     }
   }
-  matching_ports = smartlist_new();
-  SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p,
-  {
-    if (conn->base_.port != p->virtual_port) {
-      continue;
-    }
-    if (!(p->is_unix_addr)) {
-      smartlist_add(matching_ports, p);
-    } else {
-      if (add_unix_port(matching_ports, p)) {
-        if (!warn_once) {
-         /* Unix port not supported so warn only once. */
-          log_warn(LD_REND,
-              "Saw AF_UNIX virtual port mapping for port %d on service "
-              "%s, which is unsupported on this platform. Ignoring it.",
-              conn->base_.port, serviceid);
-        }
-        warn_once++;
-      }
-    }
-  });
-  chosen_port = smartlist_choose(matching_ports);
-  smartlist_free(matching_ports);
-  if (chosen_port) {
-    if (!(chosen_port->is_unix_addr)) {
-      /* Get a non-AF_UNIX connection ready for connection_exit_connect() */
-      tor_addr_copy(&conn->base_.addr, &chosen_port->real_addr);
-      conn->base_.port = chosen_port->real_port;
-    } else {
-      if (set_unix_port(conn, chosen_port)) {
-        /* Simply impossible to end up here else we were able to add a Unix
-         * port without AF_UNIX support... ? */
-        tor_assert(0);
-      }
-    }
+
+  if (hs_set_conn_addr_port(service->ports, conn) == 0) {
+    /* Successfully set the port to the connection. We are done. */
     return 0;
   }
 





More information about the tor-commits mailing list