From d3358a0a05f661f78286d43fcc71d79daa090460 Mon Sep 17 00:00:00 2001 From: "teor (Tim Wilson-Brown)" Date: Mon, 14 Sep 2015 11:46:58 +1000 Subject: [PATCH] ExitPolicy accept6/reject6 produces IPv6 wildcard addresses only In previous versions of Tor, ExitPolicy accept6/reject6 * produced policy entries for IPv4 and IPv6 wildcard addresses. To reduce operator confusion, change accept6/reject6 * to only produce an IPv6 wildcard address. Resolves bug #16069. Patch on 2eb7eafc9d78 and a96c0affcb4c (25 Oct 2012), released in 0.2.4.7-alpha. --- changes/bug16069-exit-policy-rule6 | 4 ++- doc/tor.1.txt | 32 ++++++++++++++------- src/common/address.c | 36 +++++++++++++++++------ src/common/address.h | 11 +++++++- src/config/torrc.minimal.in-staging | 9 ++++-- src/config/torrc.sample.in | 9 ++++-- src/or/routerparse.c | 11 +++++++- src/test/test_policy.c | 44 +++++++++++++++++++++++++++-- 8 files changed, 126 insertions(+), 30 deletions(-) diff --git a/changes/bug16069-exit-policy-rule6 b/changes/bug16069-exit-policy-rule6 index 5e7b894e7a..a8d7e70596 100644 --- a/changes/bug16069-exit-policy-rule6 +++ b/changes/bug16069-exit-policy-rule6 @@ -1,4 +1,6 @@ o Minor bug fixes (torrc exit policies): + - accept6/reject6 * lines only produce IPv6 wildcard addresses, + previously they would produce both IPv4 and IPv6 wildcard addresses. - When parsing torrc ExitPolicies, we now warn if: * an IPv4 address is used on an accept6 or reject6 line. The line is ignored, but the rest of the policy items in the list are used. @@ -13,6 +15,6 @@ * when expanding an accept/reject * line to include both IPv4 and IPv6 wildcard addresses. - In each instance, usage advice is provided to avoid the message. - Partial fix for ticket 16069. Patch by "teor". + Resolves ticket 16069. Patch by "teor". Patch on 2eb7eafc9d78 and a96c0affcb4c (25 Oct 2012), released in 0.2.4.7-alpha. diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 89673a865d..4f922c8f14 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -1537,12 +1537,26 @@ is non-zero): "\*". + + For example, "accept 18.7.22.69:\*,reject 18.0.0.0/8:\*,accept \*:\*" would - reject any traffic destined for MIT except for web.mit.edu, and accept - anything else. + + reject any IPv4 traffic destined for MIT except for web.mit.edu, and accept + any other IPv4 or IPv6 traffic. + + - To specify all internal and link-local networks (including 0.0.0.0/8, - 169.254.0.0/16, 127.0.0.0/8, 192.168.0.0/16, 10.0.0.0/8, and - 172.16.0.0/12), you can use the "private" alias instead of an address. + Tor also allows IPv6 exit policy entries. For instance, "reject6 [FC00::]/7:*" + rejects all destinations that share 7 most significant bit prefix with + address FC00::. Respectively, "accept6 [C000::]/3:*" accepts all destinations + that share 3 most significant bit prefix with address C000::. + + + + accept6 and reject6 only produce IPv6 exit policy entries. Using an IPv4 + address with accept6 or reject6 is ignored and generates a warning. + accept/reject allows either IPv4 or IPv6 addresses. Use *4 as an IPv4 + wildcard address, and *6 as an IPv6 wildcard address. accept/reject * + expands to matching IPv4 and IPv6 wildcard address rules. + + + + To specify all IPv4 and IPv6 internal and link-local networks (including + 0.0.0.0/8, 169.254.0.0/16, 127.0.0.0/8, 192.168.0.0/16, 10.0.0.0/8, + 172.16.0.0/12, [::]/8, [FC00::]/7, [FE80::]/10, [FEC0::]/10, [FF00::]/8, + and [::]/127), you can use the "private" alias instead of an address. + ("private" always produces rules for IPv4 and IPv6 addresses, even when + used with accept6/reject6.) These addresses are rejected by default (at the beginning of your exit policy), along with your public IP address, unless you set the ExitPolicyRejectPrivate config option to 0. For example, once you've done @@ -1551,11 +1565,6 @@ is non-zero): may also allow connections to your own computer that are addressed to its public (external) IP address. See RFC 1918 and RFC 3330 for more details about internal and reserved IP address space. + - + - Tor also allow IPv6 exit policy entries. For instance, "reject6 [FC00::]/7:*" - rejects all destinations that share 7 most significant bit prefix with - address FC00::. Respectively, "accept6 [C000::]/3:*" accepts all destinations - that share 3 most significant bit prefix with address C000::. + + This directive can be specified multiple times so you don't have to put it all on one line. + @@ -1577,6 +1586,9 @@ is non-zero): reject *:6881-6999 accept *:* + Since the default exit policy uses accept/reject *, it applies to both + IPv4 and IPv6 addresses. + [[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. diff --git a/src/common/address.c b/src/common/address.c index 597f6990d3..066355373a 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -620,13 +620,20 @@ tor_addr_to_PTR_name(char *out, size_t outlen, * yield an IPv4 wildcard. * * If 'flags & TAPMP_EXTENDED_STAR' is true, then the wildcard address '*' - * yields an AF_UNSPEC wildcard address, and the following change is made + * yields an AF_UNSPEC wildcard address, which expands to corresponding + * wildcard IPv4 and IPv6 rules, and the following change is made * in the grammar above: * Address ::= IPv4Address / "[" IPv6Address "]" / "*" / "*4" / "*6" * with the new "*4" and "*6" productions creating a wildcard to match * IPv4 or IPv6 addresses. * - */ + * If 'flags & TAPMP_EXTENDED_STAR' and 'flags & TAPMP_STAR_IPV4_ONLY' are + * both true, then the wildcard address '*' yields an IPv4 wildcard. + * + * If 'flags & TAPMP_EXTENDED_STAR' and 'flags & TAPMP_STAR_IPV6_ONLY' are + * both true, then the wildcard address '*' yields an IPv6 wildcard. + * + * TAPMP_STAR_IPV4_ONLY and TAPMP_STAR_IPV6_ONLY are mutually exclusive. */ int tor_addr_parse_mask_ports(const char *s, unsigned flags, @@ -643,6 +650,10 @@ tor_addr_parse_mask_ports(const char *s, tor_assert(s); tor_assert(addr_out); + /* We can either only want an IPv4 address or only want an IPv6 address, + * but we can't only want IPv4 & IPv6 at the same time. */ + tor_assert(!((flags & TAPMP_STAR_IPV4_ONLY) + && (flags & TAPMP_STAR_IPV6_ONLY))); /** Longest possible length for an address, mask, and port-range combination. * Includes IP, [], /mask, :, ports */ @@ -688,12 +699,21 @@ tor_addr_parse_mask_ports(const char *s, if (!strcmp(address, "*")) { if (flags & TAPMP_EXTENDED_STAR) { - family = AF_UNSPEC; - tor_addr_make_unspec(addr_out); - log_info(LD_GENERAL, - "'%s' expands into rules which apply to all IPv4 and IPv6 " - "addresses. (Use accept/reject *4:* for IPv4 or " - "accept[6]/reject[6] *6:* for IPv6.)", s); + if (flags & TAPMP_STAR_IPV4_ONLY) { + family = AF_INET; + tor_addr_from_ipv4h(addr_out, 0); + } else if (flags & TAPMP_STAR_IPV6_ONLY) { + static char nil_bytes[16] = { [0]=0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; + family = AF_INET6; + tor_addr_from_ipv6_bytes(addr_out, nil_bytes); + } else { + family = AF_UNSPEC; + tor_addr_make_unspec(addr_out); + log_info(LD_GENERAL, + "'%s' expands into rules which apply to all IPv4 and IPv6 " + "addresses. (Use accept/reject *4:* for IPv4 or " + "accept[6]/reject[6] *6:* for IPv6.)", s); + } } else { family = AF_INET; tor_addr_from_ipv4h(addr_out, 0); diff --git a/src/common/address.h b/src/common/address.h index 48a34cee31..90207c3338 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -227,10 +227,19 @@ int tor_addr_parse_PTR_name(tor_addr_t *result, const char *address, int tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out); -/* Does the address * yield an AF_UNSPEC wildcard address (1) and do we + +/* Does the address * yield an AF_UNSPEC wildcard address (1), + * which expands to corresponding wildcard IPv4 and IPv6 rules, and do we * allow *4 and *6 for IPv4 and IPv6 wildcards, respectively; * or does the address * yield IPv4 wildcard address (0). */ #define TAPMP_EXTENDED_STAR 1 +/* Does the address * yield an IPv4 wildcard address rule (1); + * or does it yield wildcard IPv4 and IPv6 rules (0) */ +#define TAPMP_STAR_IPV4_ONLY (1 << 1) +/* Does the address * yield an IPv6 wildcard address rule (1); + * or does it yield wildcard IPv4 and IPv6 rules (0) */ +#define TAPMP_STAR_IPV6_ONLY (1 << 2) +/* TAPMP_STAR_IPV4_ONLY and TAPMP_STAR_IPV6_ONLY are mutually exclusive. */ int tor_addr_parse_mask_ports(const char *s, unsigned flags, tor_addr_t *addr_out, maskbits_t *mask_out, uint16_t *port_min_out, uint16_t *port_max_out); diff --git a/src/config/torrc.minimal.in-staging b/src/config/torrc.minimal.in-staging index d54a5599cd..c17f7eb87e 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 11 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 @@ -24,6 +24,7 @@ ## can access your SocksPort may be able to learn about the connections ## you make. #SocksPolicy accept 192.168.0.0/16 +#SocksPolicy accept6 FC00::/7 #SocksPolicy reject * ## Logs go to stdout at level "notice" unless redirected by something @@ -174,8 +175,10 @@ ## networks, including to your public IP address. 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 +#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports on IPv4 and IPv6 but no more +#ExitPolicy accept *:119 # accept nntp ports on IPv4 and IPv6 as well as default exit policy +#ExitPolicy accept *4:119 # accept nntp ports on IPv4 only as well as default exit policy +#ExitPolicy accept6 *6:119 # accept nntp ports on IPv6 only as well as default exit policy #ExitPolicy reject *:* # no exits allowed ## Bridge relays (or "bridges") are Tor relays that aren't listed in the diff --git a/src/config/torrc.sample.in b/src/config/torrc.sample.in index d54a5599cd..c17f7eb87e 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 11 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 @@ -24,6 +24,7 @@ ## can access your SocksPort may be able to learn about the connections ## you make. #SocksPolicy accept 192.168.0.0/16 +#SocksPolicy accept6 FC00::/7 #SocksPolicy reject * ## Logs go to stdout at level "notice" unless redirected by something @@ -174,8 +175,10 @@ ## networks, including to your public IP address. 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 +#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports on IPv4 and IPv6 but no more +#ExitPolicy accept *:119 # accept nntp ports on IPv4 and IPv6 as well as default exit policy +#ExitPolicy accept *4:119 # accept nntp ports on IPv4 only as well as default exit policy +#ExitPolicy accept6 *6:119 # accept nntp ports on IPv6 only as well as default exit policy #ExitPolicy reject *:* # no exits allowed ## Bridge relays (or "bridges") are Tor relays that aren't listed in the diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 2f7e50e60a..7bb18ecc30 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -3820,6 +3820,12 @@ router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags) else newe.policy_type = ADDR_POLICY_ACCEPT; + /* accept6/reject6 * produces an IPv6 wildcard address only. + * (accept/reject * produces rules for IPv4 and IPv6 wildcard addresses.) */ + if (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) { + fmt_flags |= TAPMP_STAR_IPV6_ONLY; + } + if (tor_addr_parse_mask_ports(arg, fmt_flags, &newe.addr, &newe.maskbits, &newe.prt_min, &newe.prt_max) < 0) { log_warn(LD_DIR,"Couldn't parse line %s. Dropping", escaped(arg)); @@ -3829,9 +3835,12 @@ router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags) return addr_policy_get_canonical_entry(&newe); } -/** Parse an exit policy line of the format "accept/reject private:...". +/** Parse an exit policy line of the format "accept[6]/reject[6] private:...". * This didn't exist until Tor 0.1.1.15, so nobody should generate it in * router descriptors until earlier versions are obsolete. + * + * accept/reject and accept6/reject6 private all produce rules for both + * IPv4 and IPv6 addresses. */ static addr_policy_t * router_parse_addr_policy_private(directory_token_t *tok) diff --git a/src/test/test_policy.c b/src/test/test_policy.c index 5e91468e8c..4150b97468 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -77,7 +77,8 @@ 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, *policy8 = NULL, *policy9 = NULL, + *policy10 = NULL, *policy11 = NULL; addr_policy_t *p; tor_addr_t tar; config_line_t line; @@ -192,6 +193,30 @@ test_policies_general(void *arg) tt_assert(p != NULL); smartlist_add(policy7, p); + tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy8, + EXIT_POLICY_IPV6_ENABLED | + EXIT_POLICY_REJECT_PRIVATE | + EXIT_POLICY_ADD_DEFAULT, 0)); + + tt_assert(policy8); + + tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy9, + EXIT_POLICY_REJECT_PRIVATE | + EXIT_POLICY_ADD_DEFAULT, 0)); + + tt_assert(policy9); + + /* accept6 * and reject6 * produce IPv6 wildcards only */ + policy10 = smartlist_new(); + p = router_parse_addr_policy_item_from_string("accept6 *:*",-1); + tt_assert(p != NULL); + smartlist_add(policy10, p); + + policy11 = smartlist_new(); + p = router_parse_addr_policy_item_from_string("reject6 *:*",-1); + tt_assert(p != NULL); + smartlist_add(policy11, p); + tt_assert(!exit_policy_is_general_exit(policy)); tt_assert(exit_policy_is_general_exit(policy2)); tt_assert(!exit_policy_is_general_exit(NULL)); @@ -200,6 +225,10 @@ test_policies_general(void *arg) tt_assert(!exit_policy_is_general_exit(policy5)); tt_assert(!exit_policy_is_general_exit(policy6)); tt_assert(!exit_policy_is_general_exit(policy7)); + tt_assert(exit_policy_is_general_exit(policy8)); + tt_assert(exit_policy_is_general_exit(policy9)); + tt_assert(!exit_policy_is_general_exit(policy10)); + tt_assert(!exit_policy_is_general_exit(policy11)); tt_assert(cmp_addr_policies(policy, policy2)); tt_assert(cmp_addr_policies(policy, NULL)); @@ -208,7 +237,12 @@ test_policies_general(void *arg) tt_assert(!policy_is_reject_star(policy2, AF_INET)); tt_assert(policy_is_reject_star(policy, AF_INET)); + tt_assert(policy_is_reject_star(policy10, AF_INET)); + tt_assert(!policy_is_reject_star(policy10, AF_INET6)); + tt_assert(policy_is_reject_star(policy11, AF_INET)); + tt_assert(policy_is_reject_star(policy11, AF_INET6)); tt_assert(policy_is_reject_star(NULL, AF_INET)); + tt_assert(policy_is_reject_star(NULL, AF_INET6)); addr_policy_list_free(policy); policy = NULL; @@ -219,13 +253,13 @@ test_policies_general(void *arg) line.value = (char*)"accept *:80,reject private:*,reject *:*"; line.next = NULL; tt_int_op(0, OP_EQ, policies_parse_exit_policy(&line,&policy, - EXIT_POLICY_IPV6_ENABLED | + ~EXIT_POLICY_IPV6_ENABLED | EXIT_POLICY_ADD_DEFAULT,0)); tt_assert(policy); //test_streq(policy->string, "accept *:80"); //test_streq(policy->next->string, "reject *:*"); - tt_int_op(smartlist_len(policy),OP_EQ, 4); + tt_int_op(smartlist_len(policy),OP_EQ, 9); /* test policy summaries */ /* check if we properly ignore private IP addresses */ @@ -427,6 +461,10 @@ test_policies_general(void *arg) addr_policy_list_free(policy5); addr_policy_list_free(policy6); addr_policy_list_free(policy7); + addr_policy_list_free(policy8); + addr_policy_list_free(policy9); + addr_policy_list_free(policy10); + addr_policy_list_free(policy11); tor_free(policy_str); if (sm) { SMARTLIST_FOREACH(sm, char *, s, tor_free(s));