ExitPolicyRejectPrivate rejects local IPv6 address and interface addresses

ExitPolicyRejectPrivate now rejects more local addresses by default:
 * the relay's published IPv6 address (if any), and
 * any publicly routable IPv4 or IPv6 addresses on any local interfaces.

This resolves a security issue for IPv6 Exits and multihomed Exits that
trust connections originating from localhost.

Resolves ticket 17027. Patch by "teor".
Patch on 42b8fb5a15 (11 Nov 2007), released in 0.2.0.11-alpha.
This commit is contained in:
teor (Tim Wilson-Brown) 2015-09-15 18:34:18 +10:00
parent 31eb486c46
commit 098b82c7b2
10 changed files with 175 additions and 68 deletions

View File

@ -1,5 +1,6 @@
o Minor bug fixes (security, exit policies):
- Add get_interface_address[6]_list by refactoring
get_interface_address6. Add unit tests for new and existing functions.
Preparation for ticket 17027. Patch by "teor".
- ExitPolicyRejectPrivate rejects more private addresses by default:
* the relay's published IPv6 address (if any), and
* any publicly routable IPv4 or IPv6 addresses on any local interfaces.
Resolves ticket 17027. Patch by "teor".
Patch on 42b8fb5a1523 (11 Nov 2007), released in 0.2.0.11-alpha.

View File

@ -1578,8 +1578,11 @@ is non-zero):
accept *:*
[[ExitPolicyRejectPrivate]] **ExitPolicyRejectPrivate** **0**|**1**::
Reject all private (local) networks, along with your own public IP address,
at the beginning of your exit policy. See above entry on ExitPolicy.
Reject all private (local) networks, along with your own configured public
IPv4 and IPv6 addresses, at the beginning of your exit policy. Also reject
any public IPv4 and IPv6 addresses on any interface on the relay. (If
IPv6Exit is not set, all IPv6 addresses will be rejected anyway.)
See above entry on ExitPolicy.
(Default: 1)
[[IPv6Exit]] **IPv6Exit** **0**|**1**::

View File

@ -1605,33 +1605,33 @@ MOCK_IMPL(int,
get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
{
smartlist_t *addrs;
int rv = -1;
tor_assert(addr);
/* Get a list of public or internal IPs in arbitrary order */
if ((addrs = get_interface_address6_list(severity, family, 1))) {
int rv = -1;
/* Find the first non-internal address, or the last internal address
* Ideally, we want the default route, see #12377 for details */
SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
tor_addr_copy(addr, a);
rv = 0;
addrs = get_interface_address6_list(severity, family, 1);
/* If we found a non-internal address, declare success. Otherwise,
* keep looking. */
if (!tor_addr_is_internal(a, 0))
break;
} SMARTLIST_FOREACH_END(a);
/* Find the first non-internal address, or the last internal address
* Ideally, we want the default route, see #12377 for details */
SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
tor_addr_copy(addr, a);
rv = 0;
free_interface_address6_list(addrs);
return rv;
}
/* If we found a non-internal address, declare success. Otherwise,
* keep looking. */
if (!tor_addr_is_internal(a, 0))
break;
} SMARTLIST_FOREACH_END(a);
return -1;
free_interface_address6_list(addrs);
return rv;
}
/** Free a smartlist of IP addresses returned by get_interface_address6_list.
*/
void free_interface_address6_list(smartlist_t *addrs) {
void
free_interface_address6_list(smartlist_t *addrs)
{
SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a));
smartlist_free(addrs);
}
@ -1654,8 +1654,9 @@ MOCK_IMPL(smartlist_t *,get_interface_address6_list,(int severity,
/* Try to do this the smart way if possible. */
if ((addrs = get_interface_addresses_raw(severity))) {
SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
if (family != AF_UNSPEC && family != tor_addr_family(a)){
SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a)
{
if (family != AF_UNSPEC && family != tor_addr_family(a)) {
SMARTLIST_DEL_CURRENT(addrs, a);
tor_free(a);
continue;
@ -1668,7 +1669,7 @@ MOCK_IMPL(smartlist_t *,get_interface_address6_list,(int severity,
continue;
}
if (!include_internal && tor_addr_is_internal(a, 0)){
if (!include_internal && tor_addr_is_internal(a, 0)) {
SMARTLIST_DEL_CURRENT(addrs, a);
tor_free(a);
continue;

View File

@ -1,5 +1,5 @@
## Configuration file for a typical Tor user
## Last updated 2 September 2014 for Tor 0.2.6.1-alpha.
## Last updated 15 September 2015 for Tor 0.2.7.3-alpha.
## (may or may not work for much older or much newer versions of Tor.)
##
## Lines that begin with "## " try to explain what's going on. Lines
@ -171,8 +171,10 @@
## users will be told that those destinations are down.
##
## For security, by default Tor rejects connections to private (local)
## networks, including to your public IP address. See the man page entry
## for ExitPolicyRejectPrivate if you want to allow "exit enclaving".
## networks, including to the configured public IPv4 and IPv6 addresses,
## and any public IPv4 and IPv6 addresses on any interface on the relay.
## See the man page entry for ExitPolicyRejectPrivate if you want to allow
## "exit enclaving".
##
#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports but no more
#ExitPolicy accept *:119 # accept nntp as well as default exit policy

View File

@ -1,5 +1,5 @@
## Configuration file for a typical Tor user
## Last updated 2 September 2014 for Tor 0.2.6.1-alpha.
## Last updated 15 September 2015 for Tor 0.2.7.3-alpha.
## (may or may not work for much older or much newer versions of Tor.)
##
## Lines that begin with "## " try to explain what's going on. Lines
@ -171,8 +171,10 @@
## users will be told that those destinations are down.
##
## For security, by default Tor rejects connections to private (local)
## networks, including to your public IP address. See the man page entry
## for ExitPolicyRejectPrivate if you want to allow "exit enclaving".
## networks, including to the configured public IPv4 and IPv6 addresses,
## and any public IPv4 and IPv6 addresses on any interface on the relay.
## See the man page entry for ExitPolicyRejectPrivate if you want to allow
## "exit enclaving".
##
#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports but no more
#ExitPolicy accept *:119 # accept nntp as well as default exit policy

View File

@ -67,6 +67,8 @@ static int policies_parse_exit_policy_internal(config_line_t *cfg,
int ipv6_exit,
int rejectprivate,
uint32_t local_address,
tor_addr_t *ipv6_local_address,
int reject_interface_addresses,
int add_default_policy);
/** Replace all "private" entries in *<b>policy</b> with their expanded
@ -430,7 +432,7 @@ validate_addr_policies(const or_options_t *options, char **msg)
smartlist_t *addr_policy=NULL;
*msg = NULL;
if (policies_parse_exit_policy_from_options(options,0,&addr_policy)) {
if (policies_parse_exit_policy_from_options(options,0,NULL,0,&addr_policy)) {
REJECT("Error in ExitPolicy entry.");
}
@ -969,12 +971,24 @@ exit_policy_remove_redundancies(smartlist_t *dest)
"reject *:563,reject *:1214,reject *:4661-4666," \
"reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*"
/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>. If
* cfg doesn't end in an absolute accept or reject and if
/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>.
*
* If <b>ipv6_exit</b> is true, prepend "reject *6:*" to the policy.
*
* If <b>rejectprivate</b> is true:
* - prepend "reject private:*" to the policy.
* - if local_address is non-zero, treat it as a host-order IPv4 address,
* and prepend an entry that rejects it as a destination.
* - if ipv6_local_address is non-NULL, prepend an entry that rejects it as
* a destination.
* - if reject_interface_addresses is true, prepend entries that reject each
* public IPv4 and IPv6 address of each interface on this machine.
*
* If cfg doesn't end in an absolute accept or reject and if
* <b>add_default_policy</b> is true, add the default exit
* policy afterwards. If <b>rejectprivate</b> is true, prepend
* "reject private:*" to the policy. Return -1 if we can't parse cfg,
* else return 0.
* policy afterwards.
*
* Return -1 if we can't parse cfg, else return 0.
*
* This function is used to parse the exit policy from our torrc. For
* the functions used to parse the exit policy from a router descriptor,
@ -985,18 +999,73 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
int ipv6_exit,
int rejectprivate,
uint32_t local_address,
tor_addr_t *ipv6_local_address,
int reject_interface_addresses,
int add_default_policy)
{
if (!ipv6_exit) {
append_exit_policy_string(dest, "reject *6:*");
}
if (rejectprivate) {
/* Reject IPv4 and IPv6 reserved private netblocks */
append_exit_policy_string(dest, "reject private:*");
/* Reject our local IPv4 address */
if (local_address) {
char buf[POLICY_BUF_LEN];
tor_snprintf(buf, sizeof(buf), "reject %s:*", fmt_addr32(local_address));
append_exit_policy_string(dest, buf);
}
/* Reject our local IPv6 address */
if (ipv6_exit && ipv6_local_address != NULL) {
if (tor_addr_is_v4(ipv6_local_address)) {
log_warn(LD_CONFIG, "IPv4 address '%s' provided as our IPv6 local "
"address", fmt_addr(ipv6_local_address));
} else {
char buf6[POLICY_BUF_LEN];
tor_snprintf(buf6, sizeof(buf6), "reject %s:*",
fmt_addr(ipv6_local_address));
append_exit_policy_string(dest, buf6);
}
}
/* Reject local addresses from public netblocks on any interface,
* but don't reject our published addresses twice */
if (reject_interface_addresses) {
smartlist_t *public_addresses = NULL;
char bufif[POLICY_BUF_LEN];
/* Reject public IPv4 addresses on any interface,
* but don't reject our published IPv4 address twice */
public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0);
SMARTLIST_FOREACH_BEGIN(public_addresses, tor_addr_t *, a) {
if (!tor_addr_eq_ipv4h(a, local_address)) {
tor_snprintf(bufif, sizeof(bufif), "reject %s:*",
fmt_addr(a));
append_exit_policy_string(dest, bufif);
log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for a local "
"interface's public IPv4 address", bufif);
}
} SMARTLIST_FOREACH_END(a);
free_interface_address6_list(public_addresses);
if (ipv6_exit) {
/* Reject public IPv6 addresses on any interface,
* but don't reject our published IPv6 address (if any) twice */
public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0);
SMARTLIST_FOREACH_BEGIN(public_addresses, tor_addr_t *, a) {
/* if we don't have an IPv6 local address, we won't have rejected
* it above. This could happen if a future release does IPv6
* autodiscovery, and we are waiting to discover our external IPv6
* address */
if (ipv6_local_address == NULL
|| !tor_addr_eq(ipv6_local_address, a)) {
tor_snprintf(bufif, sizeof(bufif), "reject6 %s:*",
fmt_addr(a));
append_exit_policy_string(dest, bufif);
}
} SMARTLIST_FOREACH_END(a);
free_interface_address6_list(public_addresses);
}
}
}
if (parse_addr_policy(cfg, dest, -1))
return -1;
@ -1013,20 +1082,28 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
/** Parse exit policy in <b>cfg</b> into <b>dest</b> smartlist.
*
* Add entry that rejects all IPv6 destinations unless
* Prepend an entry that rejects all IPv6 destinations unless
* <b>EXIT_POLICY_IPV6_ENABLED</b> bit is set in <b>options</b> bitmask.
*
* If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>,
* do add entry that rejects all destinations in private subnetwork
* Tor is running in.
* If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>:
* - prepend an entry that rejects all destinations in all netblocks
* reserved for private use.
* - if local_address is non-zero, treat it as a host-order IPv4 address,
* and prepend an entry that rejects it as a destination.
* - if ipv6_local_address is non-NULL, prepend an entry that rejects it as
* a destination.
* - if reject_interface_addresses is true, prepend entries that reject each
* public IPv4 and IPv6 address of each interface on this machine.
*
* Respectively, if <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set, add
* If <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set in <b>options</b>, append
* default exit policy entries to <b>result</b> smartlist.
*/
int
policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
exit_policy_parser_cfg_t options,
uint32_t local_address)
uint32_t local_address,
tor_addr_t *ipv6_local_address,
int reject_interface_addresses)
{
int ipv6_enabled = (options & EXIT_POLICY_IPV6_ENABLED) ? 1 : 0;
int reject_private = (options & EXIT_POLICY_REJECT_PRIVATE) ? 1 : 0;
@ -1035,19 +1112,27 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled,
reject_private,
local_address,
ipv6_local_address,
reject_interface_addresses,
add_default);
}
/** Parse <b>ExitPolicy</b> member of <b>or_options</b> into <b>result</b>
* smartlist.
* If <b>or_options->IPv6Exit</b> is false, add an entry that
* If <b>or_options->IPv6Exit</b> is false, prepend an entry that
* rejects all IPv6 destinations.
*
* If <b>or_options->ExitPolicyRejectPrivate</b> is true, add entry that
* rejects all destinations in the private subnetwork of machine Tor
* instance is running in.
* If <b>or_options->ExitPolicyRejectPrivate</b> is true:
* - prepend an entry that rejects all destinations in all netblocks reserved
* for private use.
* - if local_address is non-zero, treat it as a host-order IPv4 address, and
* prepend an entry that rejects it as a destination.
* - if ipv6_local_address is non-NULL, prepend an entry that rejects it as a
* destination.
* - if reject_interface_addresses is true, prepend entries that reject each
* public IPv4 and IPv6 address of each interface on this machine.
*
* If <b>or_options->BridgeRelay</b> is false, add entries of default
* If <b>or_options->BridgeRelay</b> is false, append entries of default
* Tor exit policy into <b>result</b> smartlist.
*
* If or_options->ExitRelay is false, then make our exit policy into
@ -1056,6 +1141,8 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
int
policies_parse_exit_policy_from_options(const or_options_t *or_options,
uint32_t local_address,
tor_addr_t *ipv6_local_address,
int reject_interface_addresses,
smartlist_t **result)
{
exit_policy_parser_cfg_t parser_cfg = 0;
@ -1079,7 +1166,9 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options,
}
return policies_parse_exit_policy(or_options->ExitPolicy,result,
parser_cfg,local_address);
parser_cfg,local_address,
ipv6_local_address,
reject_interface_addresses);
}
/** Add "reject *:*" to the end of the policy in *<b>dest</b>, allocating

View File

@ -48,18 +48,16 @@ MOCK_DECL(addr_policy_result_t, compare_tor_addr_to_addr_policy,
addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr,
uint16_t port, const node_t *node);
/*
int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
int ipv6exit,
int rejectprivate, uint32_t local_address,
int add_default_policy);
*/
int policies_parse_exit_policy_from_options(const or_options_t *or_options,
uint32_t local_address,
tor_addr_t *ipv6_local_address,
int reject_interface_addresses,
smartlist_t **result);
int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
exit_policy_parser_cfg_t options,
uint32_t local_address);
uint32_t local_address,
tor_addr_t *ipv6_local_address,
int reject_interface_addresses);
void policies_exit_policy_append_reject_star(smartlist_t **dest);
void addr_policy_append_reject_addr(smartlist_t **dest,
const tor_addr_t *addr);

View File

@ -1922,7 +1922,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
/* DNS is screwed up; don't claim to be an exit. */
policies_exit_policy_append_reject_star(&ri->exit_policy);
} else {
policies_parse_exit_policy_from_options(options,ri->addr,
policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr,1,
&ri->exit_policy);
}
ri->policy_is_reject_star =

View File

@ -149,7 +149,6 @@ smartlist_contains_ipv6_tor_addr(smartlist_t *smartlist)
return 0;
}
#ifdef HAVE_IFADDRS_TO_SMARTLIST
static void
test_address_ifaddrs_to_smartlist(void *arg)
@ -700,7 +699,7 @@ test_address_get_if_addrs_list_internal(void *arg)
tt_assert(smartlist_contains_ipv4_tor_addr(results));
tt_assert(!smartlist_contains_ipv6_tor_addr(results));
done:
done:
free_interface_address_list(results);
return;
}
@ -725,7 +724,7 @@ test_address_get_if_addrs_list_no_internal(void *arg)
/* The list may or may not contain IPv4 addresses */
tt_assert(!smartlist_contains_ipv6_tor_addr(results));
done:
done:
free_interface_address_list(results);
return;
}
@ -750,7 +749,7 @@ test_address_get_if_addrs6_list_internal(void *arg)
tt_assert(!smartlist_contains_ipv4_tor_addr(results));
/* The list may or may not contain IPv6 addresses */
done:
done:
free_interface_address6_list(results);
return;
}
@ -775,7 +774,7 @@ test_address_get_if_addrs6_list_no_internal(void *arg)
tt_assert(!smartlist_contains_ipv4_tor_addr(results));
/* The list may or may not contain IPv6 addresses */
done:
done:
free_interface_address6_list(results);
return;
}
@ -802,7 +801,7 @@ test_address_get_if_addrs(void *arg)
tt_assert(tor_addr_is_v4(&tor_addr));
done:
done:
return;
}
@ -825,7 +824,7 @@ test_address_get_if_addrs6(void *arg)
tt_assert(!tor_addr_is_v4(&tor_addr));
}
done:
done:
return;
}

View File

@ -49,7 +49,7 @@ test_policy_summary_helper(const char *policy_str,
r = policies_parse_exit_policy(&line, &policy,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_ADD_DEFAULT ,0);
EXIT_POLICY_ADD_DEFAULT, 0, NULL, 0);
tt_int_op(r,OP_EQ, 0);
summary = policy_summarize(policy, AF_INET);
@ -77,7 +77,7 @@ test_policies_general(void *arg)
int i;
smartlist_t *policy = NULL, *policy2 = NULL, *policy3 = NULL,
*policy4 = NULL, *policy5 = NULL, *policy6 = NULL,
*policy7 = NULL;
*policy7 = NULL, *policy12 = NULL;
addr_policy_t *p;
tor_addr_t tar;
config_line_t line;
@ -112,10 +112,20 @@ test_policies_general(void *arg)
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy2,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_REJECT_PRIVATE |
EXIT_POLICY_ADD_DEFAULT, 0));
EXIT_POLICY_ADD_DEFAULT, 0,
NULL, 0));
tt_assert(policy2);
tor_addr_parse(&tar, "[2000::1234]");
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy12,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_REJECT_PRIVATE |
EXIT_POLICY_ADD_DEFAULT,
0x0306090cu, &tar, 1));
tt_assert(policy12);
policy3 = smartlist_new();
p = router_parse_addr_policy_item_from_string("reject *:*",-1);
tt_assert(p != NULL);
@ -202,7 +212,8 @@ test_policies_general(void *arg)
line.next = NULL;
tt_int_op(0, OP_EQ, policies_parse_exit_policy(&line,&policy,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_ADD_DEFAULT,0));
EXIT_POLICY_ADD_DEFAULT, 0,
NULL, 0));
tt_assert(policy);
//test_streq(policy->string, "accept *:80");
@ -347,6 +358,7 @@ test_policies_general(void *arg)
addr_policy_list_free(policy5);
addr_policy_list_free(policy6);
addr_policy_list_free(policy7);
addr_policy_list_free(policy12);
tor_free(policy_str);
if (sm) {
SMARTLIST_FOREACH(sm, char *, s, tor_free(s));