diff options
author | teor (Tim Wilson-Brown) <teor2345@gmail.com> | 2015-09-15 18:34:18 +1000 |
---|---|---|
committer | teor (Tim Wilson-Brown) <teor2345@gmail.com> | 2015-09-16 02:56:50 +1000 |
commit | 098b82c7b2a6bb711e3616eb5b7e7e5e7401f01d (patch) | |
tree | bc40d90c97de2a09a6c1e277ea3c5f2c455f8787 /src | |
parent | 31eb486c4624d1437d982ffdfc1f9d7d83c5ffd6 (diff) | |
download | tor-098b82c7b2a6bb711e3616eb5b7e7e5e7401f01d.tar.gz tor-098b82c7b2a6bb711e3616eb5b7e7e5e7401f01d.zip |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/common/address.c | 43 | ||||
-rw-r--r-- | src/config/torrc.minimal.in-staging | 8 | ||||
-rw-r--r-- | src/config/torrc.sample.in | 8 | ||||
-rw-r--r-- | src/or/policies.c | 125 | ||||
-rw-r--r-- | src/or/policies.h | 12 | ||||
-rw-r--r-- | src/or/router.c | 2 | ||||
-rw-r--r-- | src/test/test_address.c | 13 | ||||
-rw-r--r-- | src/test/test_policy.c | 20 |
8 files changed, 167 insertions, 64 deletions
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; - - /* If we found a non-internal address, declare success. Otherwise, - * keep looking. */ - if (!tor_addr_is_internal(a, 0)) - break; - } SMARTLIST_FOREACH_END(a); + addrs = get_interface_address6_list(severity, family, 1); - free_interface_address6_list(addrs); - return rv; - } + /* 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; - return -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); + + 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 *<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 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)); |