[tor-commits] [tor/master] prop224: Establish rendezvous circuit for service

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


commit acc7c4ee9578e37a66dff6a09c86bee5777f782d
Author: David Goulet <dgoulet at torproject.org>
Date:   Wed Mar 8 17:31:36 2017 -0500

    prop224: Establish rendezvous circuit for service
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 src/or/hs_cell.c     |   5 +-
 src/or/hs_circuit.c  | 274 +++++++++++++++++++++++++++++++++++++++++++++++----
 src/or/hs_common.c   |  16 +++
 src/or/hs_common.h   |  16 +++
 src/or/rendservice.c |  36 +------
 src/or/rendservice.h |   1 -
 6 files changed, 292 insertions(+), 56 deletions(-)

diff --git a/src/or/hs_cell.c b/src/or/hs_cell.c
index aff6ee04e..18d15fe0a 100644
--- a/src/or/hs_cell.c
+++ b/src/or/hs_cell.c
@@ -363,7 +363,6 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
   uint8_t *decrypted = NULL;
   size_t encrypted_section_len;
   const uint8_t *encrypted_section;
-  curve25519_public_key_t client_pk;
   trn_cell_introduce1_t *cell = NULL;
   trn_cell_introduce_encrypted_t *enc_cell = NULL;
   hs_ntor_intro_cell_keys_t *intro_keys = NULL;
@@ -404,7 +403,8 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
   /* Build the key material out of the key material found in the cell. */
   intro_keys = get_introduce2_key_material(data->auth_pk, data->enc_kp,
                                            data->subcredential,
-                                           encrypted_section, &client_pk);
+                                           encrypted_section,
+                                           &data->client_pk);
   if (intro_keys == NULL) {
     log_info(LD_REND, "Invalid INTRODUCE2 encrypted data. Unable to "
                       "compute key material on circuit %u for service %s",
@@ -490,7 +490,6 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
   log_info(LD_REND, "Valid INTRODUCE2 cell. Launching rendezvous circuit.");
 
  done:
-  memwipe(&client_pk, 0, sizeof(client_pk));
   if (intro_keys) {
     memwipe(intro_keys, 0, sizeof(hs_ntor_intro_cell_keys_t));
     tor_free(intro_keys);
diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c
index a11699227..7184e1e18 100644
--- a/src/or/hs_circuit.c
+++ b/src/or/hs_circuit.c
@@ -11,6 +11,7 @@
 #include "circuitlist.h"
 #include "circuituse.h"
 #include "config.h"
+#include "policies.h"
 #include "relay.h"
 #include "rephist.h"
 #include "router.h"
@@ -22,6 +23,7 @@
 #include "hs_service.h"
 
 /* Trunnel. */
+#include "ed25519_cert.h"
 #include "hs/cell_common.h"
 #include "hs/cell_establish_intro.h"
 
@@ -240,6 +242,46 @@ count_opened_desc_intro_point_circuits(const hs_service_t *service,
   return count;
 }
 
+/* From a given service, rendezvous cookie and handshake infor, create a
+ * rendezvous point circuit identifier. This can't fail. */
+static hs_ident_circuit_t *
+create_rp_circuit_identifier(const hs_service_t *service,
+                             const uint8_t *rendezvous_cookie,
+                             const curve25519_public_key_t *server_pk,
+                             const hs_ntor_rend_cell_keys_t *keys)
+{
+  hs_ident_circuit_t *ident;
+  uint8_t handshake_info[CURVE25519_PUBKEY_LEN + DIGEST256_LEN];
+
+  tor_assert(service);
+  tor_assert(rendezvous_cookie);
+  tor_assert(server_pk);
+  tor_assert(keys);
+
+  ident = hs_ident_circuit_new(&service->keys.identity_pk,
+                               HS_IDENT_CIRCUIT_RENDEZVOUS);
+  /* Copy the RENDEZVOUS_COOKIE which is the unique identifier. */
+  memcpy(ident->rendezvous_cookie, rendezvous_cookie,
+         sizeof(ident->rendezvous_cookie));
+  /* Build the HANDSHAKE_INFO which looks like this:
+   *    SERVER_PK        [32 bytes]
+   *    AUTH_INPUT_MAC   [32 bytes]
+   */
+  memcpy(handshake_info, server_pk->public_key, CURVE25519_PUBKEY_LEN);
+  memcpy(handshake_info + CURVE25519_PUBKEY_LEN, keys->rend_cell_auth_mac,
+         DIGEST256_LEN);
+  tor_assert(sizeof(ident->rendezvous_handshake_info) ==
+             sizeof(handshake_info));
+  memcpy(ident->rendezvous_handshake_info, handshake_info,
+         sizeof(ident->rendezvous_handshake_info));
+  /* Finally copy the NTOR_KEY_SEED for e2e encryption on the circuit. */
+  tor_assert(sizeof(ident->rendezvous_ntor_key_seed) ==
+             sizeof(keys->ntor_key_seed));
+  memcpy(ident->rendezvous_ntor_key_seed, keys->ntor_key_seed,
+         sizeof(ident->rendezvous_ntor_key_seed));
+  return ident;
+}
+
 /* From a given service and service intro point, create an introduction point
  * circuit identifier. This can't fail. */
 static hs_ident_circuit_t *
@@ -308,22 +350,225 @@ send_establish_intro(const hs_service_t *service,
   memwipe(payload, 0, sizeof(payload));
 }
 
-/* ========== */
-/* Public API */
-/* ========== */
+/* From a list of link specifier, an onion key and if we are requesting a
+ * direct connection (ex: single onion service), return a newly allocated
+ * extend_info_t object. This function checks the firewall policies and if we
+ * are allowed to extend to the chosen address.
+ *
+ *  if either IPv4 or legacy ID is missing, error.
+ *  if not direct_conn, IPv4 is prefered.
+ *  if direct_conn, IPv6 is prefered if we have one available.
+ *  if firewall does not allow the chosen address, error.
+ *
+ * Return NULL if we can fulfill the conditions. */
+static extend_info_t *
+get_rp_extend_info(const smartlist_t *link_specifiers,
+                   const curve25519_public_key_t *onion_key, int direct_conn)
+{
+  int have_v4 = 0, have_v6 = 0, have_legacy_id = 0, have_ed25519_id = 0;
+  char legacy_id[DIGEST_LEN] = {0};
+  uint16_t port_v4 = 0, port_v6 = 0, port = 0;
+  tor_addr_t addr_v4, addr_v6, *addr = NULL;
+  ed25519_public_key_t ed25519_pk;
+  extend_info_t *info = NULL;
+
+  tor_assert(link_specifiers);
+  tor_assert(onion_key);
 
-int
-hs_circ_launch_rendezvous_point(const hs_service_t *service,
-                                const curve25519_public_key_t *onion_key,
-                                const uint8_t *rendezvous_cookie)
+  SMARTLIST_FOREACH_BEGIN(link_specifiers, const link_specifier_t *, ls) {
+    switch (link_specifier_get_ls_type(ls)) {
+    case LS_IPV4:
+      /* Skip if we already seen a v4. */
+      if (have_v4) continue;
+      tor_addr_from_ipv4h(&addr_v4,
+                          link_specifier_get_un_ipv4_addr(ls));
+      port_v4 = link_specifier_get_un_ipv4_port(ls);
+      have_v4 = 1;
+      break;
+    case LS_IPV6:
+      /* Skip if we already seen a v6. */
+      if (have_v6) continue;
+      tor_addr_from_ipv6_bytes(&addr_v6,
+          (const char *) link_specifier_getconstarray_un_ipv6_addr(ls));
+      port_v6 = link_specifier_get_un_ipv6_port(ls);
+      have_v6 = 1;
+      break;
+    case LS_LEGACY_ID:
+      /* Make sure we do have enough bytes for the legacy ID. */
+      if (link_specifier_getlen_un_legacy_id(ls) < sizeof(legacy_id)) {
+        break;
+      }
+      memcpy(legacy_id, link_specifier_getconstarray_un_legacy_id(ls),
+             sizeof(legacy_id));
+      have_legacy_id = 1;
+      break;
+    case LS_ED25519_ID:
+      memcpy(ed25519_pk.pubkey,
+             link_specifier_getconstarray_un_ed25519_id(ls),
+             ED25519_PUBKEY_LEN);
+      have_ed25519_id = 1;
+      break;
+    default:
+      /* Ignore unknown. */
+      break;
+    }
+  } SMARTLIST_FOREACH_END(ls);
+
+  /* IPv4, legacy ID and ed25519 are mandatory. */
+  if (!have_v4 || !have_legacy_id || !have_ed25519_id) {
+    goto done;
+  }
+  /* By default, we pick IPv4 but this might change to v6 if certain
+   * conditions are met. */
+  addr = &addr_v4; port = port_v4;
+
+  /* If we are NOT in a direct connection, we'll use our Guard and a 3-hop
+   * circuit so we can't extend in IPv6. And at this point, we do have an IPv4
+   * address available so go to validation. */
+  if (!direct_conn) {
+    goto validate;
+  }
+
+  /* From this point on, we have a request for a direct connection to the
+   * rendezvous point so make sure we can actually connect through our
+   * firewall. We'll prefer IPv6. */
+
+  /* IPv6 test. */
+  if (have_v6 &&
+      fascist_firewall_allows_address_addr(&addr_v6, port_v6,
+                                           FIREWALL_OR_CONNECTION, 1, 1)) {
+    /* Direct connection and we can reach it in IPv6 so go for it. */
+    addr = &addr_v6; port = port_v6;
+    goto validate;
+  }
+  /* IPv4 test and we are sure we have a v4 because of the check above. */
+  if (fascist_firewall_allows_address_addr(&addr_v4, port_v4,
+                                           FIREWALL_OR_CONNECTION, 0, 0)) {
+    /* Direct connection and we can reach it in IPv4 so go for it. */
+    addr = &addr_v4; port = port_v4;
+    goto validate;
+  }
+
+ validate:
+  /* We'll validate now that the address we've picked isn't a private one. If
+   * it is, are we allowing to extend to private address? */
+  if (!extend_info_addr_is_allowed(addr)) {
+    log_warn(LD_REND, "Rendezvous point address is private and it is not "
+                      "allowed to extend to it: %s:%u",
+             fmt_addr(&addr_v4), port_v4);
+    goto done;
+  }
+
+  /* We do have everything for which we think we can connect successfully. */
+  info = extend_info_new(NULL, legacy_id, &ed25519_pk, NULL, onion_key,
+                         addr, port);
+ done:
+  return info;
+}
+
+/* For a given service, the ntor onion key and a rendezvous cookie, launch a
+ * circuit to the rendezvous point specified by the link specifiers. On
+ * success, a circuit identifier is attached to the circuit with the needed
+ * data. This function will try to open a circuit for a maximum value of
+ * MAX_REND_FAILURES then it will give up. */
+static void
+launch_rendezvous_point_circuit(const hs_service_t *service,
+                                const hs_service_intro_point_t *ip,
+                                const hs_cell_introduce2_data_t *data)
 {
+  int circ_needs_uptime;
+  time_t now = time(NULL);
+  extend_info_t *info = NULL;
+  origin_circuit_t *circ;
+
   tor_assert(service);
-  tor_assert(onion_key);
-  tor_assert(rendezvous_cookie);
-  /* XXX: Implement rendezvous launch support. */
-  return 0;
+  tor_assert(ip);
+  tor_assert(data);
+
+  circ_needs_uptime = hs_service_requires_uptime_circ(service->config.ports);
+  /* Help predict this next time */
+  rep_hist_note_used_internal(now, circ_needs_uptime, 1);
+
+  /* Get the extend info data structure for the chosen rendezvous point
+   * specified by the given link specifiers. */
+  info = get_rp_extend_info(data->link_specifiers, &data->onion_pk,
+                            service->config.is_single_onion);
+  if (info == NULL) {
+    /* We are done here, we can't extend to the rendezvous point. */
+    goto end;
+  }
+
+  for (int i = 0; i < MAX_REND_FAILURES; i++) {
+    int circ_flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL;
+    if (circ_needs_uptime) {
+      circ_flags |= CIRCLAUNCH_NEED_UPTIME;
+    }
+    /* Firewall and policies are checked when getting the extend info. */
+    if (service->config.is_single_onion) {
+      circ_flags |= CIRCLAUNCH_ONEHOP_TUNNEL;
+    }
+
+    circ = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND, info,
+                                         circ_flags);
+    if (circ != NULL) {
+      /* Stop retrying, we have a circuit! */
+      break;
+    }
+  }
+  if (circ == NULL) {
+    log_warn(LD_REND, "Giving up on launching rendezvous circuit to %s "
+                      "for service %s",
+             safe_str_client(extend_info_describe(info)),
+             safe_str_client(service->onion_address));
+    goto end;
+  }
+  log_info(LD_REND, "Rendezvous circuit launched to %s with cookie %s "
+                    "for service %s",
+           safe_str_client(extend_info_describe(info)),
+           safe_str_client(hex_str((const char *) data->rendezvous_cookie,
+                                   REND_COOKIE_LEN)),
+           safe_str_client(service->onion_address));
+  tor_assert(circ->build_state);
+  /* Rendezvous circuit have a specific timeout for the time spent on trying
+   * to connect to the rendezvous point. */
+  circ->build_state->expiry_time = now + MAX_REND_TIMEOUT;
+
+  /* Create circuit identifier and key material. */
+  {
+    hs_ntor_rend_cell_keys_t keys;
+    curve25519_keypair_t ephemeral_kp;
+    /* No need for extra strong, this is only for this circuit life time. This
+     * key will be used for the RENDEZVOUS1 cell that will be sent on the
+     * circuit once opened. */
+    curve25519_keypair_generate(&ephemeral_kp, 0);
+    if (hs_ntor_service_get_rendezvous1_keys(&ip->auth_key_kp.pubkey,
+                                             &ip->enc_key_kp,
+                                             &ephemeral_kp, &data->client_pk,
+                                             &keys) < 0) {
+      /* This should not really happened but just in case, don't make tor
+       * freak out, close the circuit and move on. */
+      log_info(LD_REND, "Unable to get RENDEZVOUS1 key material for "
+                        "service %s",
+               safe_str_client(service->onion_address));
+      circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
+      goto end;
+    }
+    circ->hs_ident = create_rp_circuit_identifier(service,
+                                                  data->rendezvous_cookie,
+                                                  &ephemeral_kp.pubkey, &keys);
+    memwipe(&ephemeral_kp, 0, sizeof(ephemeral_kp));
+    memwipe(&keys, 0, sizeof(keys));
+    tor_assert(circ->hs_ident);
+  }
+
+ end:
+  extend_info_free(info);
 }
 
+/* ========== */
+/* Public API */
+/* ========== */
+
 /* For a given service and a service intro point, launch a circuit to the
  * extend info ei. If the service is a single onion, a one-hop circuit will be
  * requested. Return 0 if the circuit was successfully launched and tagged
@@ -517,12 +762,7 @@ hs_circ_handle_introduce2(const hs_service_t *service,
   ip->introduce2_count++;
 
   /* Launch rendezvous circuit with the onion key and rend cookie. */
-  ret = hs_circ_launch_rendezvous_point(service, &data.onion_pk,
-                                        data.rendezvous_cookie);
-  if (ret < 0) {
-    goto done;
-  }
-
+  launch_rendezvous_point_circuit(service, ip, &data);
   /* Success. */
   ret = 0;
 
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index 102e4689f..571f4c517 100644
--- a/src/or/hs_common.c
+++ b/src/or/hs_common.c
@@ -19,6 +19,7 @@
 #include "hs_common.h"
 #include "hs_service.h"
 #include "rendcommon.h"
+#include "rendservice.h"
 
 /* Ed25519 Basepoint value. Taken from section 5 of
  * https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03 */
@@ -724,7 +725,22 @@ hs_overlap_mode_is_active(const networkstatus_t *consensus, time_t now)
   if (valid_after_tm.tm_hour > 0 && valid_after_tm.tm_hour < 12) {
     return 1;
   }
+  return 0;
+}
+
+/* Return 1 if any virtual port in ports needs a circuit with good uptime.
+ * Else return 0. */
+int
+hs_service_requires_uptime_circ(const smartlist_t *ports)
+{
+  tor_assert(ports);
 
+  SMARTLIST_FOREACH_BEGIN(ports, rend_service_port_config_t *, p) {
+    if (smartlist_contains_int_as_string(get_options()->LongLivedPorts,
+                                         p->virtual_port)) {
+      return 1;
+    }
+  } SMARTLIST_FOREACH_END(p);
   return 0;
 }
 
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index 6abcd9831..f9e3f297a 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -107,6 +107,21 @@ typedef enum {
   HS_AUTH_KEY_TYPE_ED25519 = 2,
 } hs_auth_key_type_t;
 
+/* Represents the mapping from a virtual port of a rendezvous service to a
+ * real port on some IP. */
+typedef struct rend_service_port_config_t {
+  /* The incoming HS virtual port we're mapping */
+  uint16_t virtual_port;
+  /* Is this an AF_UNIX port? */
+  unsigned int is_unix_addr:1;
+  /* The outgoing TCP port to use, if !is_unix_addr */
+  uint16_t real_port;
+  /* The outgoing IPv4 or IPv6 address to use, if !is_unix_addr */
+  tor_addr_t real_addr;
+  /* The socket path to connect to, if is_unix_addr */
+  char unix_addr[FLEXIBLE_ARRAY_MEMBER];
+} rend_service_port_config_t;
+
 void hs_init(void);
 void hs_free_all(void);
 
@@ -128,6 +143,7 @@ void hs_build_blinded_keypair(const ed25519_keypair_t *kp,
                               const uint8_t *secret, size_t secret_len,
                               uint64_t time_period_num,
                               ed25519_keypair_t *kp_out);
+int hs_service_requires_uptime_circ(const smartlist_t *ports);
 
 void rend_data_free(rend_data_t *data);
 rend_data_t *rend_data_dup(const rend_data_t *data);
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 8239803fb..7353a4f99 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -83,22 +83,6 @@ static smartlist_t* rend_get_service_list_mutable(
                                   smartlist_t* substitute_service_list);
 static int rend_max_intro_circs_per_period(unsigned int n_intro_points_wanted);
 
-/** Represents the mapping from a virtual port of a rendezvous service to
- * a real port on some IP.
- */
-struct rend_service_port_config_s {
-  /* The incoming HS virtual port we're mapping */
-  uint16_t virtual_port;
-  /* Is this an AF_UNIX port? */
-  unsigned int is_unix_addr:1;
-  /* The outgoing TCP port to use, if !is_unix_addr */
-  uint16_t real_port;
-  /* The outgoing IPv4 or IPv6 address to use, if !is_unix_addr */
-  tor_addr_t real_addr;
-  /* The socket path to connect to, if is_unix_addr */
-  char unix_addr[FLEXIBLE_ARRAY_MEMBER];
-};
-
 /* Hidden service directory file names:
  * new file names should be added to rend_service_add_filenames_to_list()
  * for sandboxing purposes. */
@@ -1694,24 +1678,6 @@ rend_service_get_by_service_id(const char *id)
   return NULL;
 }
 
-/** Return 1 if any virtual port in <b>service</b> wants a circuit
- * to have good uptime. Else return 0.
- */
-static int
-rend_service_requires_uptime(rend_service_t *service)
-{
-  int i;
-  rend_service_port_config_t *p;
-
-  for (i=0; i < smartlist_len(service->ports); ++i) {
-    p = smartlist_get(service->ports, i);
-    if (smartlist_contains_int_as_string(get_options()->LongLivedPorts,
-                                  p->virtual_port))
-      return 1;
-  }
-  return 0;
-}
-
 /** Check client authorization of a given <b>descriptor_cookie</b> of
  * length <b>cookie_len</b> for <b>service</b>. Return 1 for success
  * and 0 for failure. */
@@ -2029,7 +1995,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
     goto err;
   }
 
-  circ_needs_uptime = rend_service_requires_uptime(service);
+  circ_needs_uptime = hs_service_requires_uptime_circ(service->ports);
 
   /* help predict this next time */
   rep_hist_note_used_internal(now, circ_needs_uptime, 1);
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 78f4b92c2..a6d6ec6a4 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -16,7 +16,6 @@
 #include "hs_service.h"
 
 typedef struct rend_intro_cell_s rend_intro_cell_t;
-typedef struct rend_service_port_config_s rend_service_port_config_t;
 
 #ifdef RENDSERVICE_PRIVATE
 





More information about the tor-commits mailing list