From 31eb486c4624d1437d982ffdfc1f9d7d83c5ffd6 Mon Sep 17 00:00:00 2001 From: "teor (Tim Wilson-Brown)" Date: Tue, 15 Sep 2015 17:04:18 +1000 Subject: [PATCH 1/3] Add get_interface_address[6]_list for a list of interface IP addresses 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". Patch on 42b8fb5a1523 (11 Nov 2007), released in 0.2.0.11-alpha. --- .../bug17027-reject-private-all-interfaces | 5 + src/common/address.c | 107 ++++++-- src/common/address.h | 28 +- src/test/test_address.c | 243 ++++++++++++++++-- 4 files changed, 341 insertions(+), 42 deletions(-) create mode 100644 changes/bug17027-reject-private-all-interfaces diff --git a/changes/bug17027-reject-private-all-interfaces b/changes/bug17027-reject-private-all-interfaces new file mode 100644 index 0000000000..2801642f8a --- /dev/null +++ b/changes/bug17027-reject-private-all-interfaces @@ -0,0 +1,5 @@ + 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". + Patch on 42b8fb5a1523 (11 Nov 2007), released in 0.2.0.11-alpha. diff --git a/src/common/address.c b/src/common/address.c index dd336257ef..0614256521 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -1503,7 +1503,7 @@ get_interface_addresses_raw(int severity) } /** Return true iff a is a multicast address. */ -static int +STATIC int tor_addr_is_multicast(const tor_addr_t *a) { sa_family_t family = tor_addr_family(a); @@ -1593,27 +1593,26 @@ get_interface_address6_via_udp_socket_hack(int severity, return r; } -/** Set *addr to the IP address (if any) of whatever interface - * connects to the Internet. This address should only be used in checking - * whether our address has changed. Return 0 on success, -1 on failure. +/** Set *addr to an arbitrary IP address (if any) of an interface that + * connects to the Internet. Prefer public IP addresses to internal IP + * addresses. This address should only be used in checking whether our + * address has changed, as it may be an internal IP address. Return 0 on + * success, -1 on failure. + * Prefer get_interface_address6_list for a list of all addresses on all + * interfaces which connect to the Internet. */ MOCK_IMPL(int, get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)) { - /* XXX really, this function should yield a smartlist of addresses. */ smartlist_t *addrs; tor_assert(addr); - /* Try to do this the smart way if possible. */ - if ((addrs = get_interface_addresses_raw(severity))) { + /* 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) { - if (family != AF_UNSPEC && family != tor_addr_family(a)) - continue; - if (tor_addr_is_loopback(a) || - tor_addr_is_multicast(a)) - continue; - tor_addr_copy(addr, a); rv = 0; @@ -1623,13 +1622,78 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)) break; } SMARTLIST_FOREACH_END(a); - SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a)); - smartlist_free(addrs); + free_interface_address6_list(addrs); return rv; } + return -1; +} + +/** Free a smartlist of IP addresses returned by get_interface_address6_list. + */ +void free_interface_address6_list(smartlist_t *addrs) { + SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a)); + smartlist_free(addrs); +} + +/** Return a smartlist of the IP addresses of type family from all interfaces + * on the server. Excludes loopback and multicast addresses. Only includes + * internal addresses if include_internal is true. (Note that a relay behind + * NAT may use an internal address to connect to the Internet.) + * An empty smartlist means that there are no addresses of the selected type + * matching these criteria. + * Returns NULL on failure. + * Use free_interface_address6_list to free the returned list. + */ +MOCK_IMPL(smartlist_t *,get_interface_address6_list,(int severity, + sa_family_t family, + int include_internal)) +{ + smartlist_t *addrs; + tor_addr_t addr; + + /* 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_DEL_CURRENT(addrs, a); + tor_free(a); + continue; + } + + if (tor_addr_is_loopback(a) || + tor_addr_is_multicast(a)) { + SMARTLIST_DEL_CURRENT(addrs, a); + tor_free(a); + continue; + } + + if (!include_internal && tor_addr_is_internal(a, 0)){ + SMARTLIST_DEL_CURRENT(addrs, a); + tor_free(a); + continue; + } + } SMARTLIST_FOREACH_END(a); + } + + if (addrs && smartlist_len(addrs) > 0) { + return addrs; + } + + /* if we removed all entries as unsuitable */ + if (addrs) { + smartlist_free(addrs); + } + /* Okay, the smart way is out. */ - return get_interface_address6_via_udp_socket_hack(severity,family,addr); + get_interface_address6_via_udp_socket_hack(severity,family,&addr); + if (!include_internal && tor_addr_is_internal(&addr, 0)) { + return smartlist_new(); + } else { + addrs = smartlist_new(); + smartlist_add(addrs, tor_dup_addr(&addr)); + return addrs; + } } /* ====== @@ -1871,10 +1935,13 @@ tor_dup_ip(uint32_t addr) } /** - * Set *addr to the host-order IPv4 address (if any) of whatever - * interface connects to the Internet. This address should only be used in - * checking whether our address has changed. Return 0 on success, -1 on - * failure. + * Set *addr to a host-order IPv4 address (if any) of an + * interface that connects to the Internet. Prefer public IP addresses to + * internal IP addresses. This address should only be used in checking + * whether our address has changed, as it may be an internal IPv4 address. + * Return 0 on success, -1 on failure. + * Prefer get_interface_address_list6 for a list of all IPv4 and IPv6 + * addresses on all interfaces which connect to the Internet. */ MOCK_IMPL(int, get_interface_address,(int severity, uint32_t *addr)) diff --git a/src/common/address.h b/src/common/address.h index cd80615f93..c15dea8807 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -15,6 +15,7 @@ #include "orconfig.h" #include "torint.h" #include "compat.h" +#include "container.h" #ifdef ADDRESS_PRIVATE @@ -43,7 +44,6 @@ #endif // TODO win32 specific includes -#include "container.h" #endif // ADDRESS_PRIVATE /** The number of bits from an address to consider while doing a masked @@ -190,8 +190,13 @@ char *tor_dup_addr(const tor_addr_t *addr) ATTR_MALLOC; const char *fmt_addr_impl(const tor_addr_t *addr, int decorate); const char *fmt_addrport(const tor_addr_t *addr, uint16_t port); const char * fmt_addr32(uint32_t addr); + MOCK_DECL(int,get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)); +void free_interface_address6_list(smartlist_t * addrs); +MOCK_DECL(smartlist_t *,get_interface_address6_list,(int severity, + sa_family_t family, + int include_internal)); /** Flag to specify how to do a comparison between addresses. In an "exact" * comparison, addresses are equivalent only if they are in the same family @@ -269,11 +274,32 @@ int addr_mask_get_bits(uint32_t mask); int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len); char *tor_dup_ip(uint32_t addr) ATTR_MALLOC; MOCK_DECL(int,get_interface_address,(int severity, uint32_t *addr)); +/** Free a smartlist of IP addresses returned by get_interface_address_list. + */ +static INLINE void +free_interface_address_list(smartlist_t *addrs) +{ + free_interface_address6_list(addrs); +} +/** Return a smartlist of the IPv4 addresses of all interfaces on the server. + * Excludes loopback and multicast addresses. Only includes internal addresses + * if include_internal is true. (Note that a relay behind NAT may use an + * internal address to connect to the Internet.) + * An empty smartlist means that there are no IPv4 addresses. + * Returns NULL on failure. + * Use free_interface_address_list to free the returned list. + */ +static INLINE smartlist_t * +get_interface_address_list(int severity, int include_internal) +{ + return get_interface_address6_list(severity, AF_INET, include_internal); +} tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port); #ifdef ADDRESS_PRIVATE STATIC smartlist_t *get_interface_addresses_raw(int severity); +STATIC int tor_addr_is_multicast(const tor_addr_t *a); STATIC int get_interface_address6_via_udp_socket_hack(int severity, sa_family_t family, tor_addr_t *addr); diff --git a/src/test/test_address.c b/src/test/test_address.c index 9d6456315c..5a4126799e 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -74,37 +74,82 @@ sockaddr_in_from_string(const char *ip_str, struct sockaddr_in *out) } /** Return 1 iff smartlist contains a tor_addr_t structure - * that points to 127.0.0.1. Otherwise, return 0. + * that is an IPv4 or IPv6 localhost address. Otherwise, return 0. */ static int smartlist_contains_localhost_tor_addr(smartlist_t *smartlist) { - int found_localhost = 0; - - struct sockaddr_in *sockaddr_localhost; - struct sockaddr_storage *sockaddr_to_check; - - sockaddr_localhost = sockaddr_in_from_string("127.0.0.1",NULL); - - sockaddr_to_check = tor_malloc(sizeof(struct sockaddr_in)); - SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) { - tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check, - sizeof(struct sockaddr_in)); - - if (sockaddr_in_are_equal((struct sockaddr_in *)sockaddr_to_check, - sockaddr_localhost)) { - found_localhost = 1; - break; + if (tor_addr_is_loopback(tor_addr)) { + return 1; } } SMARTLIST_FOREACH_END(tor_addr); - tor_free(sockaddr_localhost); - tor_free(sockaddr_to_check); - - return found_localhost; + return 0; } +/** Return 1 iff smartlist contains a tor_addr_t structure + * that is an IPv4 or IPv6 multicast address. Otherwise, return 0. + */ +static int +smartlist_contains_multicast_tor_addr(smartlist_t *smartlist) +{ + SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) { + if (tor_addr_is_multicast(tor_addr)) { + return 1; + } + } SMARTLIST_FOREACH_END(tor_addr); + + return 0; +} + +/** Return 1 iff smartlist contains a tor_addr_t structure + * that is an IPv4 or IPv6 internal address. Otherwise, return 0. + */ +static int +smartlist_contains_internal_tor_addr(smartlist_t *smartlist) +{ + SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) { + if (tor_addr_is_internal(tor_addr, 0)) { + return 1; + } + } SMARTLIST_FOREACH_END(tor_addr); + + return 0; +} + +/** Return 1 iff smartlist contains a tor_addr_t structure + * that is an IPv4 address. Otherwise, return 0. + */ +static int +smartlist_contains_ipv4_tor_addr(smartlist_t *smartlist) +{ + SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) { + if (tor_addr_is_v4(tor_addr)) { + return 1; + } + } SMARTLIST_FOREACH_END(tor_addr); + + return 0; +} + +/** Return 1 iff smartlist contains a tor_addr_t structure + * that is an IPv6 address. Otherwise, return 0. + */ +static int +smartlist_contains_ipv6_tor_addr(smartlist_t *smartlist) +{ + SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) { + /* Since there's no tor_addr_is_v6, assume all non-v4s are v6 */ + if (!tor_addr_is_v4(tor_addr)) { + return 1; + } + } SMARTLIST_FOREACH_END(tor_addr); + + return 0; +} + + #ifdef HAVE_IFADDRS_TO_SMARTLIST static void test_address_ifaddrs_to_smartlist(void *arg) @@ -634,12 +679,168 @@ test_address_udp_socket_trick_blackbox(void *arg) return; } +static void +test_address_get_if_addrs_list_internal(void *arg) +{ + smartlist_t *results = NULL; + + (void)arg; + + results = get_interface_address_list(LOG_ERR, 1); + + tt_assert(results != NULL); + /* Assume every system has at least 1 non-local non-multicast IPv4 + * interface, even if it is an internal one */ + tt_int_op(smartlist_len(results),>=,1); + + tt_assert(!smartlist_contains_localhost_tor_addr(results)); + tt_assert(!smartlist_contains_multicast_tor_addr(results)); + /* The list may or may not contain internal addresses */ + + tt_assert(smartlist_contains_ipv4_tor_addr(results)); + tt_assert(!smartlist_contains_ipv6_tor_addr(results)); + +done: + free_interface_address_list(results); + return; +} + +static void +test_address_get_if_addrs_list_no_internal(void *arg) +{ + smartlist_t *results = NULL; + + (void)arg; + + results = get_interface_address_list(LOG_ERR, 0); + + tt_assert(results != NULL); + /* Work even on systems with only internal IPv4 addresses */ + tt_int_op(smartlist_len(results),>=,0); + + tt_assert(!smartlist_contains_localhost_tor_addr(results)); + tt_assert(!smartlist_contains_multicast_tor_addr(results)); + tt_assert(!smartlist_contains_internal_tor_addr(results)); + + /* The list may or may not contain IPv4 addresses */ + tt_assert(!smartlist_contains_ipv6_tor_addr(results)); + +done: + free_interface_address_list(results); + return; +} + +static void +test_address_get_if_addrs6_list_internal(void *arg) +{ + smartlist_t *results = NULL; + + (void)arg; + + results = get_interface_address6_list(LOG_ERR, AF_INET6, 1); + + tt_assert(results != NULL); + /* Work even on systems without IPv6 interfaces */ + tt_int_op(smartlist_len(results),>=,0); + + tt_assert(!smartlist_contains_localhost_tor_addr(results)); + tt_assert(!smartlist_contains_multicast_tor_addr(results)); + /* The list may or may not contain internal addresses */ + + tt_assert(!smartlist_contains_ipv4_tor_addr(results)); + /* The list may or may not contain IPv6 addresses */ + +done: + free_interface_address6_list(results); + return; +} + +static void +test_address_get_if_addrs6_list_no_internal(void *arg) +{ + smartlist_t *results = NULL; + + (void)arg; + + results = get_interface_address6_list(LOG_ERR, AF_INET6, 0); + + tt_assert(results != NULL); + /* Work even on systems without IPv6 interfaces */ + tt_int_op(smartlist_len(results),>=,0); + + tt_assert(!smartlist_contains_localhost_tor_addr(results)); + tt_assert(!smartlist_contains_multicast_tor_addr(results)); + tt_assert(!smartlist_contains_internal_tor_addr(results)); + + tt_assert(!smartlist_contains_ipv4_tor_addr(results)); + /* The list may or may not contain IPv6 addresses */ + +done: + free_interface_address6_list(results); + return; +} + +static void +test_address_get_if_addrs(void *arg) +{ + int rv; + uint32_t addr_h = 0; + tor_addr_t tor_addr; + + (void)arg; + + rv = get_interface_address(LOG_ERR, &addr_h); + + /* Assume every system has at least 1 non-local non-multicast IPv4 + * interface, even if it is an internal one */ + tt_assert(rv == 0); + tor_addr_from_ipv4h(&tor_addr, addr_h); + + tt_assert(!tor_addr_is_loopback(&tor_addr)); + tt_assert(!tor_addr_is_multicast(&tor_addr)); + /* The address may or may not be an internal address */ + + tt_assert(tor_addr_is_v4(&tor_addr)); + +done: + return; +} + +static void +test_address_get_if_addrs6(void *arg) +{ + int rv; + tor_addr_t tor_addr; + + (void)arg; + + rv = get_interface_address6(LOG_ERR, AF_INET6, &tor_addr); + + /* Work even on systems without IPv6 interfaces */ + if (rv == 0) { + tt_assert(!tor_addr_is_loopback(&tor_addr)); + tt_assert(!tor_addr_is_multicast(&tor_addr)); + /* The address may or may not be an internal address */ + + tt_assert(!tor_addr_is_v4(&tor_addr)); + } + +done: + return; +} + #define ADDRESS_TEST(name, flags) \ { #name, test_address_ ## name, flags, NULL, NULL } struct testcase_t address_tests[] = { ADDRESS_TEST(udp_socket_trick_whitebox, TT_FORK), ADDRESS_TEST(udp_socket_trick_blackbox, TT_FORK), + ADDRESS_TEST(get_if_addrs_list_internal, 0), + ADDRESS_TEST(get_if_addrs_list_no_internal, 0), + ADDRESS_TEST(get_if_addrs6_list_internal, 0), + ADDRESS_TEST(get_if_addrs6_list_no_internal, 0), + ADDRESS_TEST(get_if_addrs, 0), + ADDRESS_TEST(get_if_addrs6, 0), #ifdef HAVE_IFADDRS_TO_SMARTLIST ADDRESS_TEST(get_if_addrs_ifaddrs, TT_FORK), ADDRESS_TEST(ifaddrs_to_smartlist, 0), From 098b82c7b2a6bb711e3616eb5b7e7e5e7401f01d Mon Sep 17 00:00:00 2001 From: "teor (Tim Wilson-Brown)" Date: Tue, 15 Sep 2015 18:34:18 +1000 Subject: [PATCH 2/3] 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 42b8fb5a1523 (11 Nov 2007), released in 0.2.0.11-alpha. --- .../bug17027-reject-private-all-interfaces | 7 +- doc/tor.1.txt | 7 +- src/common/address.c | 41 +++--- src/config/torrc.minimal.in-staging | 8 +- src/config/torrc.sample.in | 8 +- src/or/policies.c | 125 +++++++++++++++--- src/or/policies.h | 12 +- src/or/router.c | 2 +- src/test/test_address.c | 13 +- src/test/test_policy.c | 20 ++- 10 files changed, 175 insertions(+), 68 deletions(-) diff --git a/changes/bug17027-reject-private-all-interfaces b/changes/bug17027-reject-private-all-interfaces index 2801642f8a..755cd5c9f2 100644 --- a/changes/bug17027-reject-private-all-interfaces +++ b/changes/bug17027-reject-private-all-interfaces @@ -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. diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 89673a865d..5ac6164f0f 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -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**:: diff --git a/src/common/address.c b/src/common/address.c index 0614256521..545865b5df 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -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; diff --git a/src/config/torrc.minimal.in-staging b/src/config/torrc.minimal.in-staging index d54a5599cd..8ce16bb598 100644 --- a/src/config/torrc.minimal.in-staging +++ b/src/config/torrc.minimal.in-staging @@ -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 diff --git a/src/config/torrc.sample.in b/src/config/torrc.sample.in index d54a5599cd..8ce16bb598 100644 --- a/src/config/torrc.sample.in +++ b/src/config/torrc.sample.in @@ -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 diff --git a/src/or/policies.c b/src/or/policies.c index 560b8cb4c3..1031fc0cca 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -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 *policy 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 cfg into the linked list *dest. If - * cfg doesn't end in an absolute accept or reject and if +/** Parse the exit policy cfg into the linked list *dest. + * + * If ipv6_exit is true, prepend "reject *6:*" to the policy. + * + * If rejectprivate 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 * add_default_policy is true, add the default exit - * policy afterwards. If rejectprivate 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 cfg into dest smartlist. * - * Add entry that rejects all IPv6 destinations unless + * Prepend an entry that rejects all IPv6 destinations unless * EXIT_POLICY_IPV6_ENABLED bit is set in options bitmask. * - * If EXIT_POLICY_REJECT_PRIVATE bit is set in options, - * do add entry that rejects all destinations in private subnetwork - * Tor is running in. + * If EXIT_POLICY_REJECT_PRIVATE bit is set in options: + * - 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 EXIT_POLICY_ADD_DEFAULT bit is set, add + * If EXIT_POLICY_ADD_DEFAULT bit is set in options, append * default exit policy entries to result 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 ExitPolicy member of or_options into result * smartlist. - * If or_options->IPv6Exit is false, add an entry that + * If or_options->IPv6Exit is false, prepend an entry that * rejects all IPv6 destinations. * - * If or_options->ExitPolicyRejectPrivate is true, add entry that - * rejects all destinations in the private subnetwork of machine Tor - * instance is running in. + * If or_options->ExitPolicyRejectPrivate 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 or_options->BridgeRelay is false, add entries of default + * If or_options->BridgeRelay is false, append entries of default * Tor exit policy into result 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 *dest, allocating diff --git a/src/or/policies.h b/src/or/policies.h index 0225b57a2c..f200d7babe 100644 --- a/src/or/policies.h +++ b/src/or/policies.h @@ -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); diff --git a/src/or/router.c b/src/or/router.c index 03973ae90a..8fdad9a5fa 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -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 = diff --git a/src/test/test_address.c b/src/test/test_address.c index 5a4126799e..72742df2cd 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -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; } diff --git a/src/test/test_policy.c b/src/test/test_policy.c index 33f90c7da5..d7d3cf0c0d 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -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)); From eb1759e63cccc9fb870dfb2f87b21ce1e6d4df2d Mon Sep 17 00:00:00 2001 From: "teor (Tim Wilson-Brown)" Date: Tue, 15 Sep 2015 18:57:00 +1000 Subject: [PATCH 3/3] Log an info-level message for each IP blocked by ExitPolicyRejectPrivate Log an info-level message containing the reject line added to the exit policy for each local IP address blocked by ExitPolicyRejectPrivate: - Published IPv4 and IPv6 addresses - Publicly routable IPv4 and IPv6 interface addresses --- src/or/policies.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/or/policies.c b/src/or/policies.c index 1031fc0cca..641b109696 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -1014,6 +1014,8 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest, char buf[POLICY_BUF_LEN]; tor_snprintf(buf, sizeof(buf), "reject %s:*", fmt_addr32(local_address)); append_exit_policy_string(dest, buf); + log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our published " + "IPv4 address", buf); } /* Reject our local IPv6 address */ if (ipv6_exit && ipv6_local_address != NULL) { @@ -1025,6 +1027,8 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest, tor_snprintf(buf6, sizeof(buf6), "reject %s:*", fmt_addr(ipv6_local_address)); append_exit_policy_string(dest, buf6); + log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our " + "published IPv6 address", buf6); } } /* Reject local addresses from public netblocks on any interface, @@ -1061,6 +1065,8 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest, tor_snprintf(bufif, sizeof(bufif), "reject6 %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 IPv6 address", bufif); } } SMARTLIST_FOREACH_END(a); free_interface_address6_list(public_addresses);