[tor-commits] [tor/master] prop224: Circuit has opened and ESTABLISH_INTRO cell

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


commit d765cf30b51dfcd58756b6b3d24a14ac2c47f3e8
Author: David Goulet <dgoulet at torproject.org>
Date:   Tue Feb 21 14:20:39 2017 -0500

    prop224: Circuit has opened and ESTABLISH_INTRO cell
    
    Add the entry point from the circuit subsystem of "circuit has opened" which
    is for all type of hidden service circuits. For the introduction point, this
    commit actually adds the support for handling those circuits when opened and
    sending ESTABLISH_INTRO on a circuit.
    
    Rendevzou point circuit aren't supported yet at this commit.
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 src/or/circuituse.c           |   4 +-
 src/or/hs_cell.c              | 163 +++++++++++++++++++++++++++++++++++++++++
 src/or/hs_cell.h              |  19 +++++
 src/or/hs_circuit.c           | 164 +++++++++++++++++++++++++++++++++++++++++-
 src/or/hs_circuit.h           |   9 +++
 src/or/hs_service.c           | 164 +++++++++++++++++++++++++++++++++++++++---
 src/or/hs_service.h           |   4 ++
 src/or/include.am             |   4 +-
 src/or/rendservice.c          |  11 +--
 src/or/rendservice.h          |   8 +--
 src/test/test_hs_intropoint.c |   8 +--
 11 files changed, 533 insertions(+), 25 deletions(-)

diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index af061527d..4d450f114 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1636,11 +1636,11 @@ circuit_has_opened(origin_circuit_t *circ)
       break;
     case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
       /* at the service, waiting for introductions */
-      rend_service_intro_has_opened(circ);
+      hs_service_circuit_has_opened(circ);
       break;
     case CIRCUIT_PURPOSE_S_CONNECT_REND:
       /* at the service, connecting to rend point */
-      rend_service_rendezvous_has_opened(circ);
+      hs_service_circuit_has_opened(circ);
       break;
     case CIRCUIT_PURPOSE_TESTING:
       circuit_testing_opened(circ);
diff --git a/src/or/hs_cell.c b/src/or/hs_cell.c
new file mode 100644
index 000000000..e15f4e3e5
--- /dev/null
+++ b/src/or/hs_cell.c
@@ -0,0 +1,163 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_cell.c
+ * \brief Hidden service API for cell creation and handling.
+ **/
+
+#include "or.h"
+#include "rendservice.h"
+
+#include "hs_cell.h"
+
+/* Trunnel. */
+#include "hs/cell_common.h"
+#include "hs/cell_establish_intro.h"
+
+/* Build a legacy ESTABLISH_INTRO cell with the given circuit nonce and RSA
+ * encryption key. The encoded cell is put in cell_out that MUST at least be
+ * of the size of RELAY_PAYLOAD_SIZE. Return the encoded cell length on
+ * success else a negative value and cell_out is untouched. */
+static ssize_t
+build_legacy_establish_intro(const char *circ_nonce, crypto_pk_t *enc_key,
+                             uint8_t *cell_out)
+{
+  ssize_t cell_len;
+  char buf[RELAY_PAYLOAD_SIZE] = {0};
+
+  tor_assert(circ_nonce);
+  tor_assert(enc_key);
+  tor_assert(cell_out);
+
+  cell_len = rend_service_encode_establish_intro_cell(buf, sizeof(buf),
+                                                      enc_key, circ_nonce);
+  tor_assert(cell_len <= RELAY_PAYLOAD_SIZE);
+  if (cell_len >= 0) {
+    memcpy(cell_out, buf, cell_len);
+  }
+  return cell_len;
+}
+
+/* ========== */
+/* Public API */
+/* ========== */
+
+/* Build an ESTABLISH_INTRO cell with the given circuit nonce and intro point
+ * object. The encoded cell is put in cell_out that MUST at least be of the
+ * size of RELAY_PAYLOAD_SIZE. Return the encoded cell length on success else
+ * a negative value and cell_out is untouched. This function also supports
+ * legacy cell creation. */
+ssize_t
+hs_cell_build_establish_intro(const char *circ_nonce,
+                              const hs_service_intro_point_t *ip,
+                              uint8_t *cell_out)
+{
+  ssize_t cell_len = -1;
+  uint16_t sig_len = ED25519_SIG_LEN;
+  trn_cell_extension_t *ext;
+  trn_cell_establish_intro_t *cell = NULL;
+
+  tor_assert(circ_nonce);
+  tor_assert(ip);
+
+  /* Quickly handle the legacy IP. */
+  if (ip->base.is_only_legacy) {
+    tor_assert(ip->legacy_key);
+    cell_len = build_legacy_establish_intro(circ_nonce, ip->legacy_key,
+                                            cell_out);
+    tor_assert(cell_len <= RELAY_PAYLOAD_SIZE);
+    /* Success or not we are done here. */
+    goto done;
+  }
+
+  /* Set extension data. None used here. */
+  ext = trn_cell_extension_new();
+  trn_cell_extension_set_num(ext, 0);
+  cell = trn_cell_establish_intro_new();
+  trn_cell_establish_intro_set_extensions(cell, ext);
+  /* Set signature size. Array is then allocated in the cell. We need to do
+   * this early so we can use trunnel API to get the signature length. */
+  trn_cell_establish_intro_set_sig_len(cell, sig_len);
+  trn_cell_establish_intro_setlen_sig(cell, sig_len);
+
+  /* Set AUTH_KEY_TYPE: 2 means ed25519 */
+  trn_cell_establish_intro_set_auth_key_type(cell,
+                                             HS_INTRO_AUTH_KEY_TYPE_ED25519);
+
+  /* Set AUTH_KEY and AUTH_KEY_LEN field. Must also set byte-length of
+   * AUTH_KEY to match */
+  {
+    uint16_t auth_key_len = ED25519_PUBKEY_LEN;
+    trn_cell_establish_intro_set_auth_key_len(cell, auth_key_len);
+    trn_cell_establish_intro_setlen_auth_key(cell, auth_key_len);
+    /* We do this call _after_ setting the length because it's reallocated at
+     * that point only. */
+    uint8_t *auth_key_ptr = trn_cell_establish_intro_getarray_auth_key(cell);
+    memcpy(auth_key_ptr, ip->auth_key_kp.pubkey.pubkey, auth_key_len);
+  }
+
+  /* Calculate HANDSHAKE_AUTH field (MAC). */
+  {
+    ssize_t tmp_cell_enc_len = 0;
+    ssize_t tmp_cell_mac_offset =
+      sig_len + sizeof(cell->sig_len) +
+      trn_cell_establish_intro_getlen_handshake_mac(cell);
+    uint8_t tmp_cell_enc[RELAY_PAYLOAD_SIZE] = {0};
+    uint8_t mac[TRUNNEL_SHA3_256_LEN], *handshake_ptr;
+
+    /* We first encode the current fields we have in the cell so we can
+     * compute the MAC using the raw bytes. */
+    tmp_cell_enc_len = trn_cell_establish_intro_encode(tmp_cell_enc,
+                                                       sizeof(tmp_cell_enc),
+                                                       cell);
+    if (BUG(tmp_cell_enc_len < 0)) {
+      goto done;
+    }
+    /* Sanity check. */
+    tor_assert(tmp_cell_enc_len > tmp_cell_mac_offset);
+
+    /* Circuit nonce is always DIGEST_LEN according to tor-spec.txt. */
+    crypto_mac_sha3_256(mac, sizeof(mac),
+                        (uint8_t *) circ_nonce, DIGEST_LEN,
+                        tmp_cell_enc, tmp_cell_enc_len - tmp_cell_mac_offset);
+    handshake_ptr = trn_cell_establish_intro_getarray_handshake_mac(cell);
+    memcpy(handshake_ptr, mac, sizeof(mac));
+  }
+
+  /* Calculate the cell signature SIG. */
+  {
+    ssize_t tmp_cell_enc_len = 0;
+    ssize_t tmp_cell_sig_offset = (sig_len + sizeof(cell->sig_len));
+    uint8_t tmp_cell_enc[RELAY_PAYLOAD_SIZE] = {0}, *sig_ptr;
+    ed25519_signature_t sig;
+
+    /* We first encode the current fields we have in the cell so we can
+     * compute the signature from the raw bytes of the cell. */
+    tmp_cell_enc_len = trn_cell_establish_intro_encode(tmp_cell_enc,
+                                                       sizeof(tmp_cell_enc),
+                                                       cell);
+    if (BUG(tmp_cell_enc_len < 0)) {
+      goto done;
+    }
+
+    if (ed25519_sign_prefixed(&sig, tmp_cell_enc,
+                              tmp_cell_enc_len - tmp_cell_sig_offset,
+                              ESTABLISH_INTRO_SIG_PREFIX, &ip->auth_key_kp)) {
+      log_warn(LD_BUG, "Unable to make signature for ESTABLISH_INTRO cell.");
+      goto done;
+    }
+    /* Copy the signature into the cell. */
+    sig_ptr = trn_cell_establish_intro_getarray_sig(cell);
+    memcpy(sig_ptr, sig.sig, sig_len);
+  }
+
+  /* Encode the cell. Can't be bigger than a standard cell. */
+  cell_len = trn_cell_establish_intro_encode(cell_out, RELAY_PAYLOAD_SIZE,
+                                             cell);
+
+ done:
+  trn_cell_establish_intro_free(cell);
+  return cell_len;
+}
+
diff --git a/src/or/hs_cell.h b/src/or/hs_cell.h
new file mode 100644
index 000000000..9cc6109eb
--- /dev/null
+++ b/src/or/hs_cell.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_cell.h
+ * \brief Header file containing cell data for the whole HS subsytem.
+ **/
+
+#ifndef TOR_HS_CELL_H
+#define TOR_HS_CELL_H
+
+#include "hs_service.h"
+
+ssize_t hs_cell_build_establish_intro(const char *circ_nonce,
+                                      const hs_service_intro_point_t *ip,
+                                      uint8_t *cell_out);
+
+#endif /* TOR_HS_CELL_H */
+
diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c
index 482ba30f3..01fd86483 100644
--- a/src/or/hs_circuit.c
+++ b/src/or/hs_circuit.c
@@ -6,13 +6,16 @@
  **/
 
 #include "or.h"
+#include "circpathbias.h"
 #include "circuitbuild.h"
 #include "circuitlist.h"
 #include "circuituse.h"
 #include "config.h"
+#include "relay.h"
 #include "rephist.h"
 #include "router.h"
 
+#include "hs_cell.h"
 #include "hs_circuit.h"
 #include "hs_ident.h"
 #include "hs_ntor.h"
@@ -195,6 +198,48 @@ register_intro_circ(const hs_service_intro_point_t *ip,
   }
 }
 
+/* Return the number of opened introduction circuit for the given circuit that
+ * is matching its identity key. */
+static unsigned int
+count_opened_desc_intro_point_circuits(const hs_service_t *service,
+                                       const hs_service_descriptor_t *desc)
+{
+  unsigned int count = 0;
+
+  tor_assert(service);
+  tor_assert(desc);
+
+  DIGEST256MAP_FOREACH(desc->intro_points.map, key,
+                       const hs_service_intro_point_t *, ip) {
+    circuit_t *circ;
+    origin_circuit_t *ocirc;
+    if (ip->base.is_only_legacy) {
+      uint8_t digest[DIGEST_LEN];
+      if (BUG(crypto_pk_get_digest(ip->legacy_key, (char *) digest) < 0)) {
+        continue;
+      }
+      ocirc = hs_circuitmap_get_intro_circ_v2_service_side(digest);
+    } else {
+      ocirc =
+        hs_circuitmap_get_intro_circ_v3_service_side(&ip->auth_key_kp.pubkey);
+    }
+    if (ocirc == NULL) {
+      continue;
+    }
+    circ = TO_CIRCUIT(ocirc);
+    tor_assert(circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
+               circ->purpose == CIRCUIT_PURPOSE_S_INTRO);
+    /* Having a circuit not for the requested service is really bad. */
+    tor_assert(ed25519_pubkey_eq(&service->keys.identity_pk,
+                                 &ocirc->hs_ident->identity_pk));
+    /* Only count opened circuit and skip circuit that will be closed. */
+    if (!circ->marked_for_close && circ->state == CIRCUIT_STATE_OPEN) {
+      count++;
+    }
+  } DIGEST256MAP_FOREACH_END;
+  return count;
+}
+
 /* From a given service and service intro point, create an introduction point
  * circuit identifier. This can't fail. */
 static hs_ident_circuit_t *
@@ -213,6 +258,60 @@ create_intro_circuit_identifier(const hs_service_t *service,
   return ident;
 }
 
+/* For a given introduction point and an introduction circuit, send the
+ * ESTABLISH_INTRO cell. The service object is used for logging. This can fail
+ * and if so, the circuit is closed and the intro point object is flagged
+ * that the circuit is not established anymore which is important for the
+ * retry mechanism. */
+static void
+send_establish_intro(const hs_service_t *service,
+                     hs_service_intro_point_t *ip, origin_circuit_t *circ)
+{
+  ssize_t cell_len;
+  uint8_t payload[RELAY_PAYLOAD_SIZE];
+
+  tor_assert(service);
+  tor_assert(ip);
+  tor_assert(circ);
+
+  /* Encode establish intro cell. */
+  cell_len = hs_cell_build_establish_intro(circ->cpath->prev->rend_circ_nonce,
+                                           ip, payload);
+  if (cell_len < 0) {
+    log_warn(LD_REND, "Unable to encode ESTABLISH_INTRO cell for service %s "
+                      "on circuit %u. Closing circuit.",
+             safe_str_client(service->onion_address),
+             TO_CIRCUIT(circ)->n_circ_id);
+    goto err;
+  }
+
+  /* Send the cell on the circuit. */
+  if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ),
+                                   RELAY_COMMAND_ESTABLISH_INTRO,
+                                   (char *) payload, cell_len,
+                                   circ->cpath->prev) < 0) {
+    log_info(LD_REND, "Unable to send ESTABLISH_INTRO cell for service %s "
+                      "on circuit %u.",
+             safe_str_client(service->onion_address),
+             TO_CIRCUIT(circ)->n_circ_id);
+    /* On error, the circuit has been closed. */
+    goto done;
+  }
+
+  /* Record the attempt to use this circuit. */
+  pathbias_count_use_attempt(circ);
+  goto done;
+
+ err:
+  circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
+ done:
+  memwipe(payload, 0, sizeof(payload));
+}
+
+/* ========== */
+/* 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
@@ -266,6 +365,69 @@ hs_circ_launch_intro_point(hs_service_t *service,
   return ret;
 }
 
+/* Called when a service introduction point circuit is done building. Given
+ * the service and intro point object, this function will send the
+ * ESTABLISH_INTRO cell on the circuit. Return 0 on success. Return 1 if the
+ * circuit has been repurposed to General because we already have too many
+ * opened. */
+int
+hs_circ_service_intro_has_opened(hs_service_t *service,
+                                 hs_service_intro_point_t *ip,
+                                 const hs_service_descriptor_t *desc,
+                                 origin_circuit_t *circ)
+{
+  int ret = 0;
+  unsigned int num_intro_circ, num_needed_circ;
+
+  tor_assert(service);
+  tor_assert(ip);
+  tor_assert(desc);
+  tor_assert(circ);
+
+  num_intro_circ = count_opened_desc_intro_point_circuits(service, desc);
+  num_needed_circ = service->config.num_intro_points;
+  if (num_intro_circ > num_needed_circ) {
+    /* There are too many opened valid intro circuit for what the service
+     * needs so repurpose this one. */
+
+    /* XXX: Legacy code checks options->ExcludeNodes and if not NULL it just
+     * closes the circuit. I have NO idea why it does that so it hasn't been
+     * added here. I can only assume in case our ExcludeNodes list changes but
+     * in that case, all circuit are flagged unusable (config.c). --dgoulet */
+
+    log_info(LD_CIRC | LD_REND, "Introduction circuit just opened but we "
+                                "have enough for service %s. Repurposing "
+                                "it to general and leaving internal.",
+             safe_str_client(service->onion_address));
+    tor_assert(circ->build_state->is_internal);
+    /* Remove it from the circuitmap. */
+    hs_circuitmap_remove_circuit(TO_CIRCUIT(circ));
+    /* Cleaning up the hidden service identifier and repurpose. */
+    hs_ident_circuit_free(circ->hs_ident);
+    circ->hs_ident = NULL;
+    circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_GENERAL);
+    /* Inform that this circuit just opened for this new purpose. */
+    circuit_has_opened(circ);
+    /* This return value indicate to the caller that the IP object should be
+     * removed from the service because it's corresponding circuit has just
+     * been repurposed. */
+    ret = 1;
+    goto done;
+  }
+
+  log_info(LD_REND, "Introduction circuit %u established for service %s.",
+           TO_CIRCUIT(circ)->n_circ_id,
+           safe_str_client(service->onion_address));
+  circuit_log_path(LOG_INFO, LD_REND, circ);
+
+  /* Time to send an ESTABLISH_INTRO cell on this circuit. On error, this call
+   * makes sure the circuit gets closed. */
+  send_establish_intro(service, ip, circ);
+
+ done:
+  return ret;
+}
+
 /* Circuit <b>circ</b> just finished the rend ntor key exchange. Use the key
  * exchange output material at <b>ntor_key_seed</b> and setup <b>circ</b> to
  * serve as a rendezvous end-to-end circuit between the client and the
@@ -273,7 +435,7 @@ hs_circ_launch_intro_point(hs_service_t *service,
  * and the other side is the client.
  *
  * Return 0 if the operation went well; in case of error return -1. */
-int
+  int
 hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ,
                                const uint8_t *ntor_key_seed, size_t seed_len,
                                int is_service_side)
diff --git a/src/or/hs_circuit.h b/src/or/hs_circuit.h
index 8738438eb..35eab7569 100644
--- a/src/or/hs_circuit.h
+++ b/src/or/hs_circuit.h
@@ -16,10 +16,19 @@
 #include "hs_service.h"
 
 /* Circuit API. */
+int hs_circ_service_intro_has_opened(hs_service_t *service,
+                                     hs_service_intro_point_t *ip,
+                                     const hs_service_descriptor_t *desc,
+                                     origin_circuit_t *circ);
 int hs_circ_launch_intro_point(hs_service_t *service,
                                const hs_service_intro_point_t *ip,
                                extend_info_t *ei, time_t now);
 
+/* Cell API. */
+void hs_circ_send_establish_intro(const hs_service_t *service,
+                                  hs_service_intro_point_t *ip,
+                                  origin_circuit_t *circ);
+
 /* e2e circuit API. */
 
 int hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ,
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index 06ab7b983..4cd808133 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -12,6 +12,7 @@
 #include "circpathbias.h"
 #include "circuitbuild.h"
 #include "circuitlist.h"
+#include "circuituse.h"
 #include "config.h"
 #include "main.h"
 #include "networkstatus.h"
@@ -338,13 +339,72 @@ service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy)
 static void
 service_intro_point_add(digest256map_t *map, hs_service_intro_point_t *ip)
 {
-  uint8_t key[DIGEST256_LEN] = {0};
-
   tor_assert(map);
   tor_assert(ip);
 
-  memcpy(key, ip->auth_key_kp.pubkey.pubkey, sizeof(key));
-  digest256map_set(map, key, ip);
+  digest256map_set(map, ip->auth_key_kp.pubkey.pubkey, ip);
+}
+
+/* For a given service, remove the intro point from that service which will
+ * look in both descriptors. */
+static void
+service_intro_point_remove(const hs_service_t *service,
+                           const hs_service_intro_point_t *ip)
+{
+  tor_assert(service);
+  tor_assert(ip);
+
+  /* Trying all descriptors. */
+  FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+    /* We'll try to remove the descriptor on both descriptors which is not
+     * very expensive to do instead of doing loopup + remove. */
+    digest256map_remove(desc->intro_points.map,
+                        ip->auth_key_kp.pubkey.pubkey);
+  } FOR_EACH_DESCRIPTOR_END;
+}
+
+/* For a given service and authentication key, return the intro point or NULL
+ * if not found. This will check both descriptors in the service. */
+static hs_service_intro_point_t *
+service_intro_point_find(const hs_service_t *service,
+                         const ed25519_public_key_t *auth_key)
+{
+  hs_service_intro_point_t *ip = NULL;
+
+  tor_assert(service);
+  tor_assert(auth_key);
+
+  /* Trying all descriptors. */
+  FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+    if ((ip = digest256map_get(desc->intro_points.map,
+                               auth_key->pubkey)) != NULL) {
+      break;
+    }
+  } FOR_EACH_DESCRIPTOR_END;
+
+  return ip;
+}
+
+/* For a given service and intro point, return the descriptor for which the
+ * intro point is assigned to. NULL is returned if not found. */
+static hs_service_descriptor_t *
+service_desc_find_by_intro(const hs_service_t *service,
+                           const hs_service_intro_point_t *ip)
+{
+  hs_service_descriptor_t *descp = NULL;
+
+  tor_assert(service);
+  tor_assert(ip);
+
+  FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+    if (digest256map_get(desc->intro_points.map,
+                         ip->auth_key_kp.pubkey.pubkey)) {
+      descp = desc;
+      break;
+    }
+  } FOR_EACH_DESCRIPTOR_END;
+
+  return descp;
 }
 
 /* From a given intro point, return the first link specifier of type
@@ -790,7 +850,6 @@ service_descriptor_new(void)
   return sdesc;
 }
 
-#if 0
 /* Copy the descriptor link specifier object from src to dst. */
 static void
 link_specifier_copy(hs_desc_link_specifier_t *dst,
@@ -916,8 +975,6 @@ build_desc_intro_points(const hs_service_t *service,
   } DIGEST256MAP_FOREACH_END;
 }
 
-#endif /* build_desc_intro_points is disabled because not used */
-
 /* Populate the descriptor encrypted section fomr the given service object.
  * This will generate a valid list of introduction points that can be used
  * after for circuit creation. Return 0 on success else -1 on error. */
@@ -1635,14 +1692,105 @@ run_upload_descriptor_event(time_t now)
   /* Run v3+ check. */
   FOR_EACH_SERVICE_BEGIN(service) {
     /* XXX: Upload if needed the descriptor(s). Update next upload time. */
-    (void) service;
+    /* XXX: Build the descriptor intro points list with
+     * build_desc_intro_points() once we have enough circuit opened. */
+    build_desc_intro_points(service, NULL, now);
   } FOR_EACH_SERVICE_END;
 }
 
+/* Called when the introduction point circuit is done building and ready to be
+ * used. */
+static void
+service_intro_circ_has_opened(origin_circuit_t *circ)
+{
+  int close_reason;
+  hs_service_t *service;
+  hs_service_intro_point_t *ip;
+  hs_service_descriptor_t *desc = NULL;
+
+  tor_assert(circ);
+  tor_assert(circ->cpath);
+  /* Getting here means this is a v3 intro circuit. */
+  tor_assert(circ->hs_ident);
+  tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
+
+  /* Get service object from the circuit identifier. */
+  service = find_service(hs_service_map, &circ->hs_ident->identity_pk);
+  if (service == NULL) {
+    log_warn(LD_REND, "Unknown service identity key %s on the introduction "
+                      "circuit %u. Can't find onion service.",
+             safe_str_client(ed25519_fmt(&circ->hs_ident->identity_pk)),
+             TO_CIRCUIT(circ)->n_circ_id);
+    close_reason = END_CIRC_REASON_NOSUCHSERVICE;
+    goto err;
+  }
+
+  /* From the service object, get the intro point object of that circuit. The
+   * following will query both descriptors intro points list. */
+  ip = service_intro_point_find(service, &circ->hs_ident->intro_auth_pk);
+  if (ip == NULL) {
+    log_warn(LD_REND, "Unknown authentication key on the introduction "
+                      "circuit %u for service %s",
+             TO_CIRCUIT(circ)->n_circ_id,
+             safe_str_client(service->onion_address));
+    /* Closing this circuit because we don't recognize the key. */
+    close_reason = END_CIRC_REASON_NOSUCHSERVICE;
+    goto err;
+  }
+  /* We can't have an IP object without a descriptor. */
+  desc = service_desc_find_by_intro(service, ip);
+  tor_assert(desc);
+
+  if (hs_circ_service_intro_has_opened(service, ip, desc, circ)) {
+    /* Getting here means that the circuit has been re-purposed because we
+     * have enough intro circuit opened. Remove the IP from the service. */
+    service_intro_point_remove(service, ip);
+    service_intro_point_free(ip);
+  }
+
+  goto done;
+
+ err:
+  /* Close circuit, we can't use it. */
+  circuit_mark_for_close(TO_CIRCUIT(circ), close_reason);
+ done:
+  return;
+}
+
+static void
+service_rendezvous_circ_has_opened(origin_circuit_t *circ)
+{
+  tor_assert(circ);
+  /* XXX: Implement rendezvous support. */
+}
+
 /* ========== */
 /* Public API */
 /* ========== */
 
+/* Called when any kind of hidden service circuit is done building thus
+ * opened. This is the entry point from the circuit subsystem. */
+void
+hs_service_circuit_has_opened(origin_circuit_t *circ)
+{
+  tor_assert(circ);
+
+  /* Handle both version. v2 uses rend_data and v3 uses the hs circuit
+   * identifier hs_ident. Can't be both. */
+  switch (TO_CIRCUIT(circ)->purpose) {
+  case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
+    (circ->hs_ident) ? service_intro_circ_has_opened(circ) :
+                       rend_service_intro_has_opened(circ);
+    break;
+  case CIRCUIT_PURPOSE_S_CONNECT_REND:
+    (circ->hs_ident) ? service_rendezvous_circ_has_opened(circ) :
+                       rend_service_rendezvous_has_opened(circ);
+    break;
+  default:
+    tor_assert(0);
+  }
+}
+
 /* Load and/or generate keys for all onion services including the client
  * authorization if any. Return 0 on success, -1 on failure. */
 int
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index 476fee72f..96df09493 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -103,6 +103,9 @@ typedef struct hs_service_descriptor_t {
    * hs_service_intropoints_t object indexed by authentication key (the RSA
    * key if the node is legacy). */
   hs_service_intropoints_t intro_points;
+
+  /* The time period number this descriptor has been created for. */
+  uint64_t time_period_num;
 } hs_service_descriptor_t;
 
 /* Service key material. */
@@ -228,6 +231,7 @@ void hs_service_stage_services(const smartlist_t *service_list);
 int hs_service_load_all_keys(void);
 
 void hs_service_run_scheduled_events(time_t now);
+void hs_service_circuit_has_opened(origin_circuit_t *circ);
 
 /* These functions are only used by unit tests and we need to expose them else
  * hs_service.o ends up with no symbols in libor.a which makes clang throw a
diff --git a/src/or/include.am b/src/or/include.am
index 15b86ef50..8db5be095 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -54,6 +54,7 @@ LIBTOR_A_SOURCES = \
 	src/or/ext_orport.c				\
 	src/or/hibernate.c				\
 	src/or/hs_cache.c				\
+	src/or/hs_cell.c				\
 	src/or/hs_circuit.c				\
 	src/or/hs_circuitmap.c				\
 	src/or/hs_client.c				\
@@ -184,11 +185,12 @@ ORHEADERS = \
 	src/or/entrynodes.h				\
 	src/or/hibernate.h				\
 	src/or/hs_cache.h				\
+	src/or/hs_cell.h				\
+	src/or/hs_config.h				\
 	src/or/hs_circuit.h				\
 	src/or/hs_circuitmap.h				\
 	src/or/hs_client.h				\
 	src/or/hs_common.h				\
-	src/or/hs_config.h				\
 	src/or/hs_descriptor.h				\
 	src/or/hs_ident.h				\
 	src/or/hs_intropoint.h				\
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 4641e110d..8239803fb 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -3116,10 +3116,11 @@ count_intro_point_circuits(const rend_service_t *service)
    crypto material. On success, fill <b>cell_body_out</b> and return the number
    of bytes written. On fail, return -1.
  */
-STATIC ssize_t
-encode_establish_intro_cell_legacy(char *cell_body_out,
-                                 size_t cell_body_out_len,
-                                 crypto_pk_t *intro_key, char *rend_circ_nonce)
+ssize_t
+rend_service_encode_establish_intro_cell(char *cell_body_out,
+                                         size_t cell_body_out_len,
+                                         crypto_pk_t *intro_key,
+                                         const char *rend_circ_nonce)
 {
   int retval = -1;
   int r;
@@ -3256,7 +3257,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
   /* Send the ESTABLISH_INTRO cell */
   {
     ssize_t len;
-    len = encode_establish_intro_cell_legacy(buf, sizeof(buf),
+    len = rend_service_encode_establish_intro_cell(buf, sizeof(buf),
                                       circuit->intro_key,
                                       circuit->cpath->prev->rend_circ_nonce);
     if (len < 0) {
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 4a06657ea..78f4b92c2 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -126,10 +126,6 @@ STATIC int rend_service_verify_single_onion_poison(
 STATIC int rend_service_poison_new_single_onion_dir(
                                                   const rend_service_t *s,
                                                   const or_options_t* options);
-STATIC ssize_t encode_establish_intro_cell_legacy(char *cell_body_out,
-                                                  size_t cell_body_out_len,
-                                                  crypto_pk_t *intro_key,
-                                                  char *rend_circ_nonce);
 #ifdef TOR_UNIT_TESTS
 
 STATIC void set_rend_service_list(smartlist_t *new_list);
@@ -172,6 +168,10 @@ rend_intro_cell_t * rend_service_begin_parse_intro(const uint8_t *request,
                                                    char **err_msg_out);
 int rend_service_parse_intro_plaintext(rend_intro_cell_t *intro,
                                        char **err_msg_out);
+ssize_t rend_service_encode_establish_intro_cell(char *cell_body_out,
+                                                 size_t cell_body_out_len,
+                                                 crypto_pk_t *intro_key,
+                                                 const char *rend_circ_nonce);
 int rend_service_validate_intro_late(const rend_intro_cell_t *intro,
                                      char **err_msg_out);
 void rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc);
diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c
index c6197875b..076d125ff 100644
--- a/src/test/test_hs_intropoint.c
+++ b/src/test/test_hs_intropoint.c
@@ -488,10 +488,10 @@ helper_establish_intro_v2(or_circuit_t *intro_circ)
   key1 = pk_generate(0);
 
   /* Use old circuit_key_material why not */
-  cell_len = encode_establish_intro_cell_legacy((char*)cell_body,
-                                                sizeof(cell_body),
-                                                key1,
-                                                (char *) circuit_key_material);
+  cell_len = rend_service_encode_establish_intro_cell(
+                                           (char*)cell_body,
+                                           sizeof(cell_body), key1,
+                                           (char *) circuit_key_material);
   tt_int_op(cell_len, >, 0);
 
   /* Receive legacy establish_intro */





More information about the tor-commits mailing list