Browse Source

Merge branch 'maint-0.3.2'

Nick Mathewson 6 years ago
parent
commit
a46dcc0709
7 changed files with 153 additions and 93 deletions
  1. 5 0
      changes/bug23820
  2. 51 15
      src/or/hs_circuit.c
  3. 23 4
      src/or/hs_client.c
  4. 29 49
      src/or/hs_common.c
  5. 12 4
      src/or/hs_config.c
  6. 30 20
      src/or/hs_service.c
  7. 3 1
      src/test/include.am

+ 5 - 0
changes/bug23820

@@ -0,0 +1,5 @@
+  o Minor bugfixes (IPv6, v3 single onion services):
+    - Remove buggy code for IPv6-only v3 single onion services, and reject
+      attempts to configure them. This release supports IPv4, dual-stack, and
+      IPv6-only v3 hidden services; and IPv4 and dual-stack v3 single onion
+      services. Fixes bug 23820; bugfix on 0.3.2.1-alpha.

+ 51 - 15
src/or/hs_circuit.c

@@ -343,6 +343,17 @@ send_establish_intro(const hs_service_t *service,
   memwipe(payload, 0, sizeof(payload));
 }
 
+/* Return a string constant describing the anonymity of service. */
+static const char *
+get_service_anonymity_string(const hs_service_t *service)
+{
+  if (service->config.is_single_onion) {
+    return "single onion";
+  } else {
+    return "hidden";
+  }
+}
+
 /* 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
@@ -370,7 +381,15 @@ launch_rendezvous_point_circuit(const hs_service_t *service,
                                         &data->onion_pk,
                                         service->config.is_single_onion);
   if (info == NULL) {
-    /* We are done here, we can't extend to the rendezvous point. */
+    /* We are done here, we can't extend to the rendezvous point.
+     * If you're running an IPv6-only v3 single onion service on 0.3.2 or with
+     * 0.3.2 clients, and somehow disable the option check, it will fail here.
+     */
+    log_fn(LOG_PROTOCOL_WARN, LD_REND,
+           "Not enough info to open a circuit to a rendezvous point for "
+           "%s service %s.",
+           get_service_anonymity_string(service),
+           safe_str_client(service->onion_address));
     goto end;
   }
 
@@ -392,17 +411,19 @@ launch_rendezvous_point_circuit(const hs_service_t *service,
     }
   }
   if (circ == NULL) {
-    log_warn(LD_REND, "Giving up on launching rendezvous circuit to %s "
-                      "for service %s",
+    log_warn(LD_REND, "Giving up on launching a rendezvous circuit to %s "
+                      "for %s service %s",
              safe_str_client(extend_info_describe(info)),
+             get_service_anonymity_string(service),
              safe_str_client(service->onion_address));
     goto end;
   }
   log_info(LD_REND, "Rendezvous circuit launched to %s with cookie %s "
-                    "for service %s",
+                    "for %s service %s",
            safe_str_client(extend_info_describe(info)),
            safe_str_client(hex_str((const char *) data->rendezvous_cookie,
                                    REND_COOKIE_LEN)),
+           get_service_anonymity_string(service),
            safe_str_client(service->onion_address));
   tor_assert(circ->build_state);
   /* Rendezvous circuit have a specific timeout for the time spent on trying
@@ -533,7 +554,10 @@ retry_service_rendezvous_point(const origin_circuit_t *circ)
 }
 
 /* Using an extend info object ei, set all possible link specifiers in lspecs.
- * IPv4, legacy ID and ed25519 ID are mandatory thus MUST be present in ei. */
+ * legacy ID is mandatory thus MUST be present in ei. If IPv4 is not present,
+ * logs a BUG() warning, and returns an empty smartlist. Clients never make
+ * direct connections to rendezvous points, so they should always have an
+ * IPv4 address in ei. */
 static void
 get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs)
 {
@@ -542,7 +566,11 @@ get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs)
   tor_assert(ei);
   tor_assert(lspecs);
 
-  /* IPv4 is mandatory. */
+  /* We require IPv4, we will add IPv6 support in a later tor version */
+  if (BUG(!tor_addr_is_v4(&ei->addr))) {
+    return;
+  }
+
   ls = link_specifier_new();
   link_specifier_set_ls_type(ls, LS_IPV4);
   link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ei->addr));
@@ -560,15 +588,15 @@ get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs)
   link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls));
   smartlist_add(lspecs, ls);
 
-  /* ed25519 ID is mandatory. */
-  ls = link_specifier_new();
-  link_specifier_set_ls_type(ls, LS_ED25519_ID);
-  memcpy(link_specifier_getarray_un_ed25519_id(ls), &ei->ed_identity,
-         link_specifier_getlen_un_ed25519_id(ls));
-  link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls));
-  smartlist_add(lspecs, ls);
-
-  /* XXX: IPv6 is not clearly a thing in extend_info_t? */
+  /* ed25519 ID is only included if the extend_info has it. */
+  if (!ed25519_public_key_is_zero(&ei->ed_identity)) {
+    ls = link_specifier_new();
+    link_specifier_set_ls_type(ls, LS_ED25519_ID);
+    memcpy(link_specifier_getarray_un_ed25519_id(ls), &ei->ed_identity,
+           link_specifier_getlen_un_ed25519_id(ls));
+    link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls));
+    smartlist_add(lspecs, ls);
+  }
 }
 
 /* Using the given descriptor intro point ip, the extend information of the
@@ -1053,6 +1081,14 @@ hs_circ_send_introduce1(origin_circuit_t *intro_circ,
    * object which is used to build the content of the cell. */
   setup_introduce1_data(ip, rend_circ->build_state->chosen_exit,
                         subcredential, &intro1_data);
+  /* If we didn't get any link specifiers, it's because our extend info was
+   * bad. */
+  if (BUG(!intro1_data.link_specifiers) ||
+      !smartlist_len(intro1_data.link_specifiers)) {
+    log_warn(LD_REND, "Unable to get link specifiers for INTRODUCE1 cell on "
+             "circuit %u.", TO_CIRCUIT(intro_circ)->n_circ_id);
+    goto done;
+  }
 
   /* Final step before we encode a cell, we setup the circuit identifier which
    * will generate both the rendezvous cookie and client keypair for this

+ 23 - 4
src/or/hs_client.c

@@ -776,15 +776,24 @@ client_get_random_intro(const ed25519_public_key_t *service_pk)
   const hs_descriptor_t *desc;
   const hs_desc_encrypted_data_t *enc_data;
   const or_options_t *options = get_options();
+  /* Calculate the onion address for logging purposes */
+  char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
 
   tor_assert(service_pk);
 
   desc = hs_cache_lookup_as_client(service_pk);
+  /* Assume the service is v3 if the descriptor is missing. This is ok,
+   * because we only use the address in log messages */
+  hs_build_address(service_pk,
+                   desc ? desc->plaintext_data.version : HS_VERSION_THREE,
+                   onion_address);
   if (desc == NULL || !hs_client_any_intro_points_usable(service_pk,
                                                          desc)) {
     log_info(LD_REND, "Unable to randomly select an introduction point "
-                      "because descriptor %s.",
-             (desc) ? "doesn't have usable intro point" : "is missing");
+             "for service %s because descriptor %s. We can't connect.",
+             safe_str_client(onion_address),
+             (desc) ? "doesn't have any usable intro points"
+                    : "is missing (assuming v3 onion address)");
     goto end;
   }
 
@@ -812,6 +821,10 @@ client_get_random_intro(const ed25519_public_key_t *service_pk)
     if (ei == NULL) {
       /* We can get here for instance if the intro point is a private address
        * and we aren't allowed to extend to those. */
+      log_info(LD_REND, "Unable to select introduction point with auth key %s "
+               "for service %s, because we could not extend to it.",
+               safe_str_client(ed25519_fmt(&ip->auth_key_cert->signed_key)),
+               safe_str_client(onion_address));
       continue;
     }
 
@@ -840,14 +853,20 @@ client_get_random_intro(const ed25519_public_key_t *service_pk)
    * set, we are forced to not use anything. */
   ei = ei_excluded;
   if (options->StrictNodes) {
-    log_warn(LD_REND, "Every introduction points are in the ExcludeNodes set "
-             "and StrictNodes is set. We can't connect.");
+    log_warn(LD_REND, "Every introduction point for service %s is in the "
+             "ExcludeNodes set and StrictNodes is set. We can't connect.",
+             safe_str_client(onion_address));
     extend_info_free(ei);
     ei = NULL;
+  } else {
+    log_fn(LOG_PROTOCOL_WARN, LD_REND, "Every introduction point for service "
+           "%s is unusable or we can't extend to it. We can't connect.",
+           safe_str_client(onion_address));
   }
 
  end:
   smartlist_free(usable_ips);
+  memwipe(onion_address, 0, sizeof(onion_address));
   return ei;
 }
 

+ 29 - 49
src/or/hs_common.c

@@ -1638,24 +1638,22 @@ hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str)
 
 /* 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.
+ * extend_info_t object. This function always returns an extend info with
+ * an IPv4 address, or NULL.
  *
- *  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. */
+ * It performs the following checks:
+ *  if either IPv4 or legacy ID is missing, return NULL.
+ *  if direct_conn, and we can't reach the IPv4 address, return NULL.
+ */
 extend_info_t *
 hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
                                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;
+  int have_v4 = 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;
+  uint16_t port_v4 = 0;
+  tor_addr_t addr_v4;
   ed25519_public_key_t ed25519_pk;
   extend_info_t *info = NULL;
 
@@ -1671,14 +1669,6 @@ hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
       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)) {
@@ -1700,55 +1690,45 @@ hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
     }
   } SMARTLIST_FOREACH_END(ls);
 
-  /* IPv4 and legacy ID are mandatory. */
+  /* Legacy ID is mandatory, and we require IPv4. */
   if (!have_v4 || !have_legacy_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. */
+  /* We know we have IPv4, because we just checked. */
   if (!direct_conn) {
+    /* All clients can extend to any IPv4 via a 3-hop path. */
     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)) {
+  } else if (direct_conn &&
+             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;
+
+    /* We will add support for falling back to a 3-hop path in a later
+     * release. */
+  } else {
+    /* If we can't reach IPv4, return NULL. */
+    goto done;
   }
 
+  /* We will add support for IPv6 in a later release. */
+
  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, "Requested address is private and it is not "
-                      "allowed to extend to it: %s:%u",
-             fmt_addr(&addr_v4), port_v4);
+  if (!extend_info_addr_is_allowed(&addr_v4)) {
+    log_fn(LOG_PROTOCOL_WARN, LD_REND,
+           "Requested address is private and we are 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,
                          (have_ed25519_id) ? &ed25519_pk : NULL, NULL,
-                         onion_key, addr, port);
+                         onion_key, &addr_v4, port_v4);
  done:
   return info;
 }

+ 12 - 4
src/or/hs_config.c

@@ -424,11 +424,19 @@ config_generic_service(const config_line_t *line_,
     }
   }
 
-  /* Check if we are configured in non anonymous mode and single hop mode
-   * meaning every service become single onion. */
-  if (rend_service_allow_non_anonymous_connection(options) &&
-      rend_service_non_anonymous_mode_enabled(options)) {
+  /* Check if we are configured in non anonymous mode meaning every service
+   * becomes a single onion service. */
+  if (rend_service_non_anonymous_mode_enabled(options)) {
     config->is_single_onion = 1;
+    /* We will add support for IPv6-only v3 single onion services in a future
+     * Tor version. This won't catch "ReachableAddresses reject *4", but that
+     * option doesn't work anyway. */
+    if (options->ClientUseIPv4 == 0 && config->version == HS_VERSION_THREE) {
+      log_warn(LD_CONFIG, "IPv6-only v3 single onion services are not "
+               "supported. Set HiddenServiceSingleHopMode 0 and "
+               "HiddenServiceNonAnonymousMode 0, or set ClientUseIPv4 1.");
+      goto err;
+    }
   }
 
   /* Success */

+ 30 - 20
src/or/hs_service.c

@@ -375,7 +375,14 @@ service_intro_point_free_(void *obj)
 
 /* Return a newly allocated service intro point and fully initialized from the
  * given extend_info_t ei if non NULL. If is_legacy is true, we also generate
- * the legacy key. On error, NULL is returned. */
+ * the legacy key. On error, NULL is returned.
+ *
+ * If ei is NULL, returns a hs_service_intro_point_t with an empty link
+ * specifier list and no onion key. (This is used for testing.)
+ *
+ * ei must be an extend_info_t containing an IPv4 address. (We will add supoort
+ * for IPv6 in a later release.) When calling extend_info_from_node(), pass
+ * 0 in for_direct_connection to make sure ei always has an IPv4 address. */
 STATIC hs_service_intro_point_t *
 service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy)
 {
@@ -427,8 +434,8 @@ service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy)
     goto done;
   }
 
-  /* We'll try to add all link specifier. Legacy, IPv4 and ed25519 are
-   * mandatory. */
+  /* We'll try to add all link specifiers. Legacy is mandatory.
+   * IPv4 or IPv6 is required, and we always send IPv4. */
   ls = hs_desc_link_specifier_new(ei, LS_IPV4);
   /* It is impossible to have an extend info object without a v4. */
   if (BUG(!ls)) {
@@ -450,11 +457,7 @@ service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy)
     smartlist_add(ip->base.link_specifiers, ls);
   }
 
-  /* IPv6 is optional. */
-  ls = hs_desc_link_specifier_new(ei, LS_IPV6);
-  if (ls) {
-    smartlist_add(ip->base.link_specifiers, ls);
-  }
+  /* IPv6 is not supported in this release. */
 
   /* Finally, copy onion key from the extend_info_t object. */
   memcpy(&ip->onion_key, &ei->curve25519_onion_key, sizeof(ip->onion_key));
@@ -1520,10 +1523,17 @@ build_all_descriptors(time_t now)
 /* Randomly pick a node to become an introduction point but not present in the
  * given exclude_nodes list. The chosen node is put in the exclude list
  * regardless of success or not because in case of failure, the node is simply
- * unsusable from that point on. If direct_conn is set, try to pick a node
- * that our local firewall/policy allows to directly connect to and if not,
- * fallback to a normal 3-hop node. Return a newly allocated service intro
- * point ready to be used for encoding. NULL on error. */
+ * unsusable from that point on.
+ *
+ * If direct_conn is set, try to pick a node that our local firewall/policy
+ * allows us to connect to directly. If we can't find any, return NULL.
+ * This function supports selecting dual-stack nodes for direct single onion
+ * service IPv6 connections. But it does not send IPv6 addresses in link
+ * specifiers. (Current clients don't use IPv6 addresses to extend, and
+ * direct client connections to intro points are not supported.)
+ *
+ * Return a newly allocated service intro point ready to be used for encoding.
+ * Return NULL on error. */
 static hs_service_intro_point_t *
 pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes)
 {
@@ -1537,12 +1547,9 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes)
 
   node = router_choose_random_node(exclude_nodes, get_options()->ExcludeNodes,
                                    direct_conn ? direct_flags : flags);
-  if (node == NULL && direct_conn) {
-    /* Unable to find a node for direct connection, let's fall back to a
-     * normal 3-hop node. */
-    node = router_choose_random_node(exclude_nodes,
-                                     get_options()->ExcludeNodes, flags);
-  }
+  /* Unable to find a node. When looking for a node for a direct connection,
+   * we could try a 3-hop path instead. We'll add support for this in a later
+   * release. */
   if (!node) {
     goto err;
   }
@@ -1553,8 +1560,11 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes)
   smartlist_add(exclude_nodes, (void *) node);
 
   /* We do this to ease our life but also this call makes appropriate checks
-   * of the node object such as validating ntor support for instance. */
-  info = extend_info_from_node(node, direct_conn);
+   * of the node object such as validating ntor support for instance.
+   *
+   * We must provide an extend_info for clients to connect over a 3-hop path,
+   * so we don't pass direct_conn here. */
+  info = extend_info_from_node(node, 0);
   if (BUG(info == NULL)) {
     goto err;
   }

+ 3 - 1
src/test/include.am

@@ -42,8 +42,10 @@ TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
 TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-v2-min hs-v3-min \
 	single-onion-v23
 # only run if we can ping6 ::1 (localhost)
+# IPv6-only v3 single onion services don't work yet, so we don't test the
+# single-onion-v23-ipv6 flavor
 TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-v23-ipv6 \
-	single-onion-v23-ipv6
+	single-onion-ipv6
 # only run if we can find a stable (or simply another) version of tor
 TEST_CHUTNEY_FLAVORS_MIXED = mixed+hs-v23