[tor-commits] [tor/master] Write unittest that covers cases of INTRODUCE1 handling.

nickm at torproject.org nickm at torproject.org
Mon Feb 24 12:48:35 UTC 2020


commit ba99287d13782048f58a88dc5d18780fad9f2034
Author: George Kadianakis <desnacked at riseup.net>
Date:   Mon Jan 27 17:26:47 2020 +0200

    Write unittest that covers cases of INTRODUCE1 handling.
    
    Also fix some memleaks of other OB unittests.
---
 src/core/or/circuitbuild.c      |   4 +-
 src/core/or/circuitbuild.h      |   3 +-
 src/feature/hs/hs_cell.c        |   2 +-
 src/feature/hs/hs_circuit.c     |   8 +-
 src/feature/hs/hs_circuit.h     |   6 +
 src/feature/nodelist/nodelist.c |   4 +-
 src/feature/nodelist/nodelist.h |   4 +-
 src/test/hs_test_helpers.c      |  45 ++++-
 src/test/hs_test_helpers.h      |   8 +-
 src/test/test_hs_ob.c           |   8 +-
 src/test/test_hs_service.c      | 356 +++++++++++++++++++++++++++++++++++++++-
 11 files changed, 421 insertions(+), 27 deletions(-)

diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c
index 03ed2c7d2..003b91af8 100644
--- a/src/core/or/circuitbuild.c
+++ b/src/core/or/circuitbuild.c
@@ -2819,8 +2819,8 @@ extend_info_dup(extend_info_t *info)
  * If there is no chosen exit, or if we don't know the node_t for
  * the chosen exit, return NULL.
  */
-const node_t *
-build_state_get_exit_node(cpath_build_state_t *state)
+MOCK_IMPL(const node_t *,
+build_state_get_exit_node,(cpath_build_state_t *state))
 {
   if (!state || !state->chosen_exit)
     return NULL;
diff --git a/src/core/or/circuitbuild.h b/src/core/or/circuitbuild.h
index f5a343906..48592dd34 100644
--- a/src/core/or/circuitbuild.h
+++ b/src/core/or/circuitbuild.h
@@ -66,7 +66,8 @@ int circuit_can_use_tap(const origin_circuit_t *circ);
 int circuit_has_usable_onion_key(const origin_circuit_t *circ);
 int extend_info_has_preferred_onion_key(const extend_info_t* ei);
 const uint8_t *build_state_get_exit_rsa_id(cpath_build_state_t *state);
-const node_t *build_state_get_exit_node(cpath_build_state_t *state);
+MOCK_DECL(const node_t *,
+          build_state_get_exit_node,(cpath_build_state_t *state));
 const char *build_state_get_exit_nickname(cpath_build_state_t *state);
 
 struct circuit_guard_state_t;
diff --git a/src/feature/hs/hs_cell.c b/src/feature/hs/hs_cell.c
index fce6fe022..97a1691f1 100644
--- a/src/feature/hs/hs_cell.c
+++ b/src/feature/hs/hs_cell.c
@@ -869,7 +869,7 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
   /* Check our replay cache for this introduction point. */
   if (replaycache_add_test_and_elapsed(data->replay_cache, encrypted_section,
                                        encrypted_section_len, &elapsed)) {
-    log_warn(LD_REND, "Possible replay detected! An INTRODUCE2 cell with the"
+    log_warn(LD_REND, "Possible replay detected! An INTRODUCE2 cell with the "
                       "same ENCRYPTED section was seen %ld seconds ago. "
                       "Dropping cell.", (long int) elapsed);
     goto done;
diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c
index febeec473..97507df9f 100644
--- a/src/feature/hs/hs_circuit.c
+++ b/src/feature/hs/hs_circuit.c
@@ -368,10 +368,10 @@ get_service_anonymity_string(const hs_service_t *service)
  * 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)
+MOCK_IMPL(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);
diff --git a/src/feature/hs/hs_circuit.h b/src/feature/hs/hs_circuit.h
index 5c20e3818..22e936e68 100644
--- a/src/feature/hs/hs_circuit.h
+++ b/src/feature/hs/hs_circuit.h
@@ -79,6 +79,12 @@ create_rp_circuit_identifier(const hs_service_t *service,
                              const curve25519_public_key_t *server_pk,
                              const struct hs_ntor_rend_cell_keys_t *keys);
 
+struct hs_cell_introduce2_data_t;
+MOCK_DECL(STATIC void,
+launch_rendezvous_point_circuit,(const hs_service_t *service,
+                                 const hs_service_intro_point_t *ip,
+                                const struct hs_cell_introduce2_data_t *data));
+
 #endif /* defined(HS_CIRCUIT_PRIVATE) */
 
 #endif /* !defined(TOR_HS_CIRCUIT_H) */
diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c
index 94ff08826..1d0b1a0d4 100644
--- a/src/feature/nodelist/nodelist.c
+++ b/src/feature/nodelist/nodelist.c
@@ -1213,8 +1213,8 @@ node_get_rsa_id_digest(const node_t *node)
  * If node is NULL, returns an empty smartlist.
  *
  * The smartlist must be freed using link_specifier_smartlist_free(). */
-smartlist_t *
-node_get_link_specifier_smartlist(const node_t *node, bool direct_conn)
+MOCK_IMPL(smartlist_t *,
+node_get_link_specifier_smartlist,(const node_t *node, bool direct_conn))
 {
   link_specifier_t *ls;
   tor_addr_port_t ap;
diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h
index 9742e3dff..40aed0067 100644
--- a/src/feature/nodelist/nodelist.h
+++ b/src/feature/nodelist/nodelist.h
@@ -78,8 +78,8 @@ int node_supports_ed25519_hs_intro(const node_t *node);
 int node_supports_v3_rendezvous_point(const node_t *node);
 int node_supports_establish_intro_dos_extension(const node_t *node);
 const uint8_t *node_get_rsa_id_digest(const node_t *node);
-smartlist_t *node_get_link_specifier_smartlist(const node_t *node,
-                                               bool direct_conn);
+MOCK_DECL(smartlist_t *,node_get_link_specifier_smartlist,(const node_t *node,
+                                                           bool direct_conn));
 void link_specifier_smartlist_free_(smartlist_t *ls_list);
 #define link_specifier_smartlist_free(ls_list) \
   FREE_AND_NULL(smartlist_t, link_specifier_smartlist_free_, (ls_list))
diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c
index eb4af1c9f..5116fc716 100644
--- a/src/test/hs_test_helpers.c
+++ b/src/test/hs_test_helpers.c
@@ -13,9 +13,22 @@
 #include "feature/hs/hs_service.h"
 #include "test/hs_test_helpers.h"
 
+/**
+ * Create an introduction point taken straight out of an HSv3 descriptor.
+ *
+ * Use 'signing_kp' to sign the introduction point certificates.
+ *
+ * If 'intro_auth_kp' is provided use that as the introduction point
+ * authentication keypair, otherwise generate one on the fly.
+ *
+ * If 'intro_enc_kp' is provided use that as the introduction point encryption
+ * keypair, otherwise generate one on the fly.
+ */
 hs_desc_intro_point_t *
 hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
-                            const char *addr, int legacy)
+                            const char *addr, int legacy,
+                            const ed25519_keypair_t *intro_auth_kp,
+                            const curve25519_keypair_t *intro_enc_kp)
 {
   int ret;
   ed25519_keypair_t auth_kp;
@@ -56,8 +69,12 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
     smartlist_add(ip->link_specifiers, ls_ip);
   }
 
-  ret = ed25519_keypair_generate(&auth_kp, 0);
-  tt_int_op(ret, OP_EQ, 0);
+  if (intro_auth_kp) {
+    memcpy(&auth_kp, intro_auth_kp, sizeof(ed25519_keypair_t));
+  } else {
+    ret = ed25519_keypair_generate(&auth_kp, 0);
+    tt_int_op(ret, OP_EQ, 0);
+  }
   ip->auth_key_cert = tor_cert_create(signing_kp, CERT_TYPE_AUTH_HS_IP_KEY,
                                       &auth_kp.pubkey, now,
                                       HS_DESC_CERT_LIFETIME,
@@ -85,8 +102,12 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
     ed25519_keypair_t ed25519_kp;
     tor_cert_t *cross_cert;
 
-    ret = curve25519_keypair_generate(&curve25519_kp, 0);
-    tt_int_op(ret, OP_EQ, 0);
+    if (intro_enc_kp) {
+      memcpy(&curve25519_kp, intro_enc_kp, sizeof(curve25519_keypair_t));
+    } else {
+      ret = curve25519_keypair_generate(&curve25519_kp, 0);
+      tt_int_op(ret, OP_EQ, 0);
+    }
     ed25519_keypair_from_curve25519_keypair(&ed25519_kp, &signbit,
                                             &curve25519_kp);
     cross_cert = tor_cert_create(signing_kp, CERT_TYPE_CROSS_HS_IP_KEYS,
@@ -95,6 +116,8 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
                                  CERT_FLAG_INCLUDE_SIGNING_KEY);
     tt_assert(cross_cert);
     ip->enc_key_cert = cross_cert;
+    memcpy(ip->enc_key.public_key, curve25519_kp.pubkey.public_key,
+           CURVE25519_PUBKEY_LEN);
   }
 
   intro_point = ip;
@@ -165,13 +188,17 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
   if (!no_ip) {
     /* Add four intro points. */
     smartlist_add(desc->encrypted_data.intro_points,
-              hs_helper_build_intro_point(signing_kp, now, "1.2.3.4", 0));
+                  hs_helper_build_intro_point(signing_kp, now, "1.2.3.4", 0,
+                                              NULL, NULL));
     smartlist_add(desc->encrypted_data.intro_points,
-              hs_helper_build_intro_point(signing_kp, now, "[2600::1]", 0));
+                  hs_helper_build_intro_point(signing_kp, now, "[2600::1]", 0,
+                                              NULL, NULL));
     smartlist_add(desc->encrypted_data.intro_points,
-              hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1));
+                  hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1,
+                                              NULL, NULL));
     smartlist_add(desc->encrypted_data.intro_points,
-              hs_helper_build_intro_point(signing_kp, now, "5.6.7.8", 1));
+                  hs_helper_build_intro_point(signing_kp, now, "5.6.7.8", 1,
+                                              NULL, NULL));
   }
 
   descp = desc;
diff --git a/src/test/hs_test_helpers.h b/src/test/hs_test_helpers.h
index cad453282..23d11f2a4 100644
--- a/src/test/hs_test_helpers.h
+++ b/src/test/hs_test_helpers.h
@@ -8,9 +8,11 @@
 #include "feature/hs/hs_descriptor.h"
 
 /* Set of functions to help build and test descriptors. */
-hs_desc_intro_point_t *hs_helper_build_intro_point(
-                          const ed25519_keypair_t *signing_kp, time_t now,
-                          const char *addr, int legacy);
+hs_desc_intro_point_t *
+hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
+                            const char *addr, int legacy,
+                            const ed25519_keypair_t *intro_auth_kp,
+                            const curve25519_keypair_t *intro_enc_kp);
 hs_descriptor_t *hs_helper_build_hs_desc_no_ip(
                                  const ed25519_keypair_t *signing_kp);
 hs_descriptor_t *hs_helper_build_hs_desc_with_ip(
diff --git a/src/test/test_hs_ob.c b/src/test/test_hs_ob.c
index c4d9d239d..b84cef9de 100644
--- a/src/test/test_hs_ob.c
+++ b/src/test/test_hs_ob.c
@@ -169,6 +169,7 @@ static void
 test_get_subcredentials(void *arg)
 {
   int ret;
+  hs_service_t *service = NULL;
   hs_service_config_t config;
 
   (void) arg;
@@ -192,7 +193,7 @@ test_get_subcredentials(void *arg)
   smartlist_add(config.ob_master_pubkeys, &onion_addr_kp_1.pubkey);
 
   /* Set up an instance */
-  hs_service_t *service = tor_malloc_zero(sizeof(hs_service_t));
+  service = tor_malloc_zero(sizeof(hs_service_t));
   service->config = config;
   service->desc_current = service_descriptor_new();
   service->desc_next = service_descriptor_new();
@@ -234,7 +235,12 @@ test_get_subcredentials(void *arg)
 
  done:
   tor_free(subcreds);
+
   smartlist_free(config.ob_master_pubkeys);
+  if (service) {
+    memset(&service->config, 0, sizeof(hs_service_config_t));
+    hs_service_free(service);
+  }
 
   UNMOCK(networkstatus_get_live_consensus);
 }
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index e33d593d9..1767648bb 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -51,6 +51,8 @@
 #include "feature/hs/hs_common.h"
 #include "feature/hs/hs_config.h"
 #include "feature/hs/hs_ident.h"
+#include "feature/hs/hs_ob.h"
+#include "feature/hs/hs_cell.h"
 #include "feature/hs/hs_intropoint.h"
 #include "feature/hs/hs_service.h"
 #include "feature/nodelist/networkstatus.h"
@@ -109,6 +111,9 @@ mock_circuit_mark_for_close(circuit_t *circ, int reason, int line,
   return;
 }
 
+static size_t relay_payload_len;
+static char relay_payload[RELAY_PAYLOAD_SIZE];
+
 static int
 mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
                                   uint8_t relay_command, const char *payload,
@@ -124,6 +129,10 @@ mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
   (void) cpath_layer;
   (void) filename;
   (void) lineno;
+
+  memcpy(relay_payload, payload, payload_len);
+  relay_payload_len = payload_len;
+
   return 0;
 }
 
@@ -1160,7 +1169,7 @@ test_closing_intro_circs(void *arg)
 
 /** Test sending and receiving introduce2 cells */
 static void
-test_introduce2(void *arg)
+test_bad_introduce2(void *arg)
 {
   int ret;
   int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
@@ -2169,6 +2178,348 @@ test_export_client_circuit_id(void *arg)
   tor_free(cp2);
 }
 
+static smartlist_t *
+mock_node_get_link_specifier_smartlist(const node_t *node, bool direct_conn)
+{
+  (void) node;
+  (void) direct_conn;
+
+  smartlist_t *lspecs = smartlist_new();
+  link_specifier_t *ls_legacy = link_specifier_new();
+  smartlist_add(lspecs, ls_legacy);
+
+  return lspecs;
+}
+
+static node_t *fake_node = NULL;
+
+static const node_t *
+mock_build_state_get_exit_node(cpath_build_state_t *state)
+{
+  (void) state;
+
+  if (!fake_node) {
+    curve25519_secret_key_t seckey;
+    curve25519_secret_key_generate(&seckey, 0);
+
+    fake_node = tor_malloc_zero(sizeof(node_t));
+    fake_node->ri = tor_malloc_zero(sizeof(routerinfo_t));
+    fake_node->ri->onion_curve25519_pkey =
+      tor_malloc_zero(sizeof(curve25519_public_key_t));
+    curve25519_public_key_generate(fake_node->ri->onion_curve25519_pkey,
+                                   &seckey);
+  }
+
+  return fake_node;
+}
+
+static void
+mock_launch_rendezvous_point_circuit(const hs_service_t *service,
+                                     const hs_service_intro_point_t *ip,
+                                     const hs_cell_introduce2_data_t *data)
+{
+  (void) service;
+  (void) ip;
+  (void) data;
+  return;
+}
+
+/**
+ *  Test that INTRO2 cells are handled well by onion services in the normal
+ *  case and also when onionbalance is enabled.
+ */
+static void
+test_intro2_handling(void *arg)
+{
+  (void)arg;
+
+  MOCK(build_state_get_exit_node, mock_build_state_get_exit_node);
+  MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge);
+  MOCK(node_get_link_specifier_smartlist,
+       mock_node_get_link_specifier_smartlist);
+  MOCK(launch_rendezvous_point_circuit, mock_launch_rendezvous_point_circuit);
+
+  memset(relay_payload, 0, sizeof(relay_payload));
+
+  int retval;
+  time_t now = 0101010101;
+  update_approx_time(now);
+
+  /** OK this is the play:
+   *
+   *  In Act I, we have a standalone onion service X (without onionbalance
+   *  enabled). We test that X can properly handle INTRO2 cells sent by a
+   *  client Alice.
+   *
+   *  In Act II, we create an onionbalance setup with frontend being Z which
+   *  includes instances X and Y. We then setup onionbalance on X and test that
+   *  Alice who addresses Z can communicate with X through INTRO2 cells.
+   *
+   *  In Act III, we test that Alice can also communicate with X
+   *  directly even tho onionbalance is enabled.
+   *
+   *  And finally in Act IV, we check various cases where the INTRO2 cell
+   *  should not go through because the subcredentials don't line up
+   *  (e.g. Alice sends INTRO2 to X using Y's subcredential).
+   */
+
+  /** Let's start with some setup! Create the instances and the frontend
+      service, create Alice, etc:  */
+
+  /* Create instance X */
+  hs_service_t x_service;
+  memset(&x_service, 0, sizeof(hs_service_t));
+  /* Disable onionbalance */
+  x_service.config.ob_master_pubkeys = NULL;
+  x_service.state.replay_cache_rend_cookie = replaycache_new(0,0);
+
+  /* Create subcredential for x: */
+  ed25519_keypair_t x_identity_keypair;
+  hs_subcredential_t x_subcred;
+  ed25519_keypair_generate(&x_identity_keypair, 0);
+  hs_helper_get_subcred_from_identity_keypair(&x_identity_keypair,
+                                              &x_subcred);
+
+  /* Create the x instance's intro point */
+  hs_service_intro_point_t *x_ip = NULL;
+  {
+    curve25519_secret_key_t seckey;
+    curve25519_public_key_t pkey;
+    curve25519_secret_key_generate(&seckey, 0);
+    curve25519_public_key_generate(&pkey, &seckey);
+
+    node_t intro_node;
+    memset(&intro_node, 0, sizeof(intro_node));
+    routerinfo_t ri;
+    memset(&ri, 0, sizeof(routerinfo_t));
+    ri.onion_curve25519_pkey = &pkey;
+    intro_node.ri = &ri;
+
+    x_ip = service_intro_point_new(&intro_node);
+  }
+
+  /* Create z frontend's subcredential */
+  ed25519_keypair_t z_identity_keypair;
+  hs_subcredential_t z_subcred;
+  ed25519_keypair_generate(&z_identity_keypair, 0);
+  hs_helper_get_subcred_from_identity_keypair(&z_identity_keypair,
+                                              &z_subcred);
+
+  /* Create y instance's subcredential */
+  ed25519_keypair_t y_identity_keypair;
+  hs_subcredential_t y_subcred;
+  ed25519_keypair_generate(&y_identity_keypair, 0);
+  hs_helper_get_subcred_from_identity_keypair(&y_identity_keypair,
+                                              &y_subcred);
+
+  /* Create Alice's intro point */
+  hs_desc_intro_point_t *alice_ip;
+  ed25519_keypair_t signing_kp;
+  ed25519_keypair_generate(&signing_kp, 0);
+  alice_ip = hs_helper_build_intro_point(&signing_kp, now, "1.2.3.4", 0,
+                                         &x_ip->auth_key_kp,
+                                         &x_ip->enc_key_kp);
+
+  /* Create Alice's intro and rend circuits */
+  origin_circuit_t *intro_circ = origin_circuit_new();
+  intro_circ->cpath = tor_malloc_zero(sizeof(crypt_path_t));
+  intro_circ->cpath->prev = intro_circ->cpath;
+  intro_circ->hs_ident = tor_malloc_zero(sizeof(*intro_circ->hs_ident));
+  origin_circuit_t rend_circ;
+  rend_circ.hs_ident = tor_malloc_zero(sizeof(*rend_circ.hs_ident));
+  curve25519_keypair_generate(&rend_circ.hs_ident->rendezvous_client_kp, 0);
+  memset(rend_circ.hs_ident->rendezvous_cookie, 'r', HS_REND_COOKIE_LEN);
+
+  /* ************************************************************ */
+
+  /* Act I:
+   *
+   * Where Alice connects to X without onionbalance in the picture */
+
+  /* Create INTRODUCE1 */
+  tt_assert(fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
+  retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
+                                   alice_ip, &x_subcred);
+
+  /* Check that the payload was written successfully */
+  tt_int_op(retval, OP_EQ, 0);
+  tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
+  tt_int_op(relay_payload_len, OP_NE, 0);
+
+  /* Handle the cell */
+  retval = hs_circ_handle_introduce2(&x_service,
+                                    intro_circ, x_ip,
+                                    &x_subcred,
+                                    (uint8_t*)relay_payload,relay_payload_len);
+  tt_int_op(retval, OP_EQ, 0);
+
+  /* ************************************************************ */
+
+  /* Act II:
+   *
+   * We now create an onionbalance setup with Z being the frontend and X and Y
+   * being the backend instances. Make sure that Alice can talk with the
+   * backend instance X even tho she thinks she is talking to the frontend Z.
+   */
+
+  /* Now configure the X instance to do onionbalance with Z as the frontend */
+  x_service.config.ob_master_pubkeys = smartlist_new();
+  smartlist_add(x_service.config.ob_master_pubkeys,
+                &z_identity_keypair.pubkey);
+
+  /* Create descriptors for x and load next descriptor with the x's
+   * subcredential so that it can accept connections for itself. */
+  x_service.desc_current = service_descriptor_new();
+  memset(x_service.desc_current->desc->subcredential.subcred, 'C',SUBCRED_LEN);
+  x_service.desc_next = service_descriptor_new();
+  memcpy(&x_service.desc_next->desc->subcredential, &x_subcred, SUBCRED_LEN);
+
+  /* Refresh OB keys */
+  hs_ob_refresh_keys(&x_service);
+
+  /* Create INTRODUCE1 from Alice to X through Z */
+  memset(relay_payload, 0, sizeof(relay_payload));
+  retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
+                                   alice_ip, &z_subcred);
+
+  /* Check that the payload was written successfully */
+  tt_int_op(retval, OP_EQ, 0);
+  tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
+  tt_int_op(relay_payload_len, OP_NE, 0);
+
+  /* Deliver INTRODUCE1 to X even tho it carries Z's subcredential */
+  replaycache_free(x_service.state.replay_cache_rend_cookie);
+  x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0);
+
+  retval = hs_circ_handle_introduce2(&x_service,
+                                   intro_circ, x_ip,
+                                   &z_subcred,
+                                   (uint8_t*)relay_payload, relay_payload_len);
+  tt_int_op(retval, OP_EQ, 0);
+
+  replaycache_free(x_ip->replay_cache);
+  x_ip->replay_cache = replaycache_new(0, 0);
+
+  replaycache_free(x_service.state.replay_cache_rend_cookie);
+  x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0);
+
+  /* ************************************************************ */
+
+  /* Act III:
+   *
+   * Now send a direct INTRODUCE cell from Alice to X using X's subcredential
+   * and check that it succeeds even with onionbalance enabled.
+   */
+
+  /* Refresh OB keys (just to check for memleaks) */
+  hs_ob_refresh_keys(&x_service);
+
+  /* Create INTRODUCE1 from Alice to X using X's subcred. */
+  memset(relay_payload, 0, sizeof(relay_payload));
+  retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
+                                   alice_ip, &x_subcred);
+
+  /* Check that the payload was written successfully */
+  tt_int_op(retval, OP_EQ, 0);
+  tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
+  tt_int_op(relay_payload_len, OP_NE, 0);
+
+  /* Send INTRODUCE1 to X with X's subcredential (should succeed) */
+  replaycache_free(x_service.state.replay_cache_rend_cookie);
+  x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0);
+
+  retval = hs_circ_handle_introduce2(&x_service,
+                                   intro_circ, x_ip,
+                                   &x_subcred,
+                                   (uint8_t*)relay_payload, relay_payload_len);
+  tt_int_op(retval, OP_EQ, 0);
+
+  /* ************************************************************ */
+
+  /* Act IV:
+   *
+   * Test cases where the INTRO2 cell should not be able to decode.
+   */
+
+  /* Try sending the exact same INTRODUCE2 cell again and see that the intro
+   * point replay cache triggers: */
+  setup_full_capture_of_logs(LOG_WARN);
+  retval = hs_circ_handle_introduce2(&x_service,
+                                   intro_circ, x_ip,
+                                   &x_subcred,
+                                   (uint8_t*)relay_payload, relay_payload_len);
+  tt_int_op(retval, OP_EQ, -1);
+  expect_log_msg_containing("with the same ENCRYPTED section");
+  teardown_capture_of_logs();
+
+  /* Now cleanup the intro point replay cache but not the service replay cache
+     and see that this one triggers this time. */
+  replaycache_free(x_ip->replay_cache);
+  x_ip->replay_cache = replaycache_new(0, 0);
+  setup_full_capture_of_logs(LOG_INFO);
+  retval = hs_circ_handle_introduce2(&x_service,
+                                   intro_circ, x_ip,
+                                   &x_subcred,
+                                   (uint8_t*)relay_payload, relay_payload_len);
+  tt_int_op(retval, OP_EQ, -1);
+  expect_log_msg_containing("with same REND_COOKIE");
+  teardown_capture_of_logs();
+
+  /* Now just to make sure cleanup both replay caches and make sure that the
+     cell gets through */
+  replaycache_free(x_ip->replay_cache);
+  x_ip->replay_cache = replaycache_new(0, 0);
+  replaycache_free(x_service.state.replay_cache_rend_cookie);
+  x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0);
+  retval = hs_circ_handle_introduce2(&x_service,
+                                   intro_circ, x_ip,
+                                   &x_subcred,
+                                   (uint8_t*)relay_payload, relay_payload_len);
+  tt_int_op(retval, OP_EQ, 0);
+
+  /* As a final thing, create an INTRODUCE1 cell from Alice to X using Y's
+   * subcred (should fail since Y is just another instance and not the frontend
+   * service!) */
+  memset(relay_payload, 0, sizeof(relay_payload));
+  retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
+                                   alice_ip, &y_subcred);
+  tt_int_op(retval, OP_EQ, 0);
+
+  /* Check that the payload was written successfully */
+  tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
+  tt_int_op(relay_payload_len, OP_NE, 0);
+
+  retval = hs_circ_handle_introduce2(&x_service,
+                                   intro_circ, x_ip,
+                                   &y_subcred,
+                                   (uint8_t*)relay_payload, relay_payload_len);
+  tt_int_op(retval, OP_EQ, -1);
+
+ done:
+  /* Start cleaning up X */
+  replaycache_free(x_service.state.replay_cache_rend_cookie);
+  smartlist_free(x_service.config.ob_master_pubkeys);
+  tor_free(x_service.ob_subcreds);
+  service_descriptor_free(x_service.desc_current);
+  service_descriptor_free(x_service.desc_next);
+  service_intro_point_free(x_ip);
+
+  /* Clean up Alice */
+  hs_desc_intro_point_free(alice_ip);
+  tor_free(rend_circ.hs_ident);
+
+  if (fake_node) {
+    tor_free(fake_node->ri->onion_curve25519_pkey);
+    tor_free(fake_node->ri);
+    tor_free(fake_node);
+  }
+
+  UNMOCK(build_state_get_exit_node);
+  UNMOCK(relay_send_command_from_edge_);
+  UNMOCK(node_get_link_specifier_smartlist);
+  UNMOCK(launch_rendezvous_point_circuit);
+}
+
 struct testcase_t hs_service_tests[] = {
   { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK,
     NULL, NULL },
@@ -2194,7 +2545,7 @@ struct testcase_t hs_service_tests[] = {
     NULL, NULL },
   { "rdv_circuit_opened", test_rdv_circuit_opened, TT_FORK,
     NULL, NULL },
-  { "introduce2", test_introduce2, TT_FORK,
+  { "bad_introduce2", test_bad_introduce2, TT_FORK,
     NULL, NULL },
   { "service_event", test_service_event, TT_FORK,
     NULL, NULL },
@@ -2212,6 +2563,7 @@ struct testcase_t hs_service_tests[] = {
     TT_FORK, NULL, NULL },
   { "export_client_circuit_id", test_export_client_circuit_id, TT_FORK,
     NULL, NULL },
+  { "intro2_handling", test_intro2_handling, TT_FORK, NULL, NULL },
 
   END_OF_TESTCASES
 };





More information about the tor-commits mailing list