Merge branch 'maint-0.3.2' into release-0.3.2

This commit is contained in:
Nick Mathewson 2017-11-02 10:22:35 -04:00
commit aed642a4b2
7 changed files with 153 additions and 93 deletions

5
changes/bug23820 Normal file
View File

@ -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.

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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