aboutsummaryrefslogtreecommitdiff
path: root/src/or/policies.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/policies.c')
-rw-r--r--src/or/policies.c218
1 files changed, 193 insertions, 25 deletions
diff --git a/src/or/policies.c b/src/or/policies.c
index 560b8cb4c3..b247e6a64d 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
@@ -152,7 +154,7 @@ policy_expand_unspec(smartlist_t **policy)
}
/**
- * Given a linked list of config lines containing "allow" and "deny"
+ * Given a linked list of config lines containing "accept[6]" and "reject[6]"
* tokens, parse them and append the result to <b>dest</b>. Return -1
* if any tokens are malformed (and don't append any), else return 0.
*
@@ -167,6 +169,7 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
smartlist_t *result;
smartlist_t *entries;
addr_policy_t *item;
+ int malformed_list;
int r = 0;
if (!cfg)
@@ -179,12 +182,22 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(entries, const char *, ent) {
log_debug(LD_CONFIG,"Adding new entry '%s'",ent);
- item = router_parse_addr_policy_item_from_string(ent, assume_action);
+ malformed_list = 0;
+ item = router_parse_addr_policy_item_from_string(ent, assume_action,
+ &malformed_list);
if (item) {
smartlist_add(result, item);
- } else {
- log_warn(LD_CONFIG,"Malformed policy '%s'.", ent);
+ } else if (malformed_list) {
+ /* the error is so severe the entire list should be discarded */
+ log_warn(LD_CONFIG, "Malformed policy '%s'. Discarding entire policy "
+ "list.", ent);
r = -1;
+ } else {
+ /* the error is minor: don't add the item, but keep processing the
+ * rest of the policies in the list */
+ log_debug(LD_CONFIG, "Ignored policy '%s' due to non-fatal error. "
+ "The remainder of the policy list will be used.",
+ ent);
}
} SMARTLIST_FOREACH_END(ent);
SMARTLIST_FOREACH(entries, char *, ent, tor_free(ent));
@@ -430,7 +443,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.");
}
@@ -568,6 +581,8 @@ cmp_single_addr_policy(addr_policy_t *a, addr_policy_t *b)
return r;
if ((r=((int)a->is_private - (int)b->is_private)))
return r;
+ /* refcnt and is_canonical are irrelevant to equality,
+ * they are hash table implementation details */
if ((r=tor_addr_compare(&a->addr, &b->addr, CMP_EXACT)))
return r;
if ((r=((int)a->maskbits - (int)b->maskbits)))
@@ -969,12 +984,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,21 +1012,142 @@ 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);
+ 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) {
+ 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);
+ log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our "
+ "published IPv6 address", 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);
+ 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);
+ }
}
}
if (parse_addr_policy(cfg, dest, -1))
return -1;
+
+ /* Before we add the default policy and final rejects, check to see if
+ * there are any lines after accept *:* or reject *:*. These lines have no
+ * effect, and are most likely an error. */
+ int found_final_effective_entry = 0;
+ int first_redundant_entry = 0;
+ for (int i = 0; i < smartlist_len(*dest); ++i) {
+ sa_family_t family;
+ addr_policy_t *p;
+ int found_ipv4_wildcard = 0, found_ipv6_wildcard = 0;
+
+ p = smartlist_get(*dest, i);
+
+ /* Look for accept/reject *[4|6|]:* entires */
+ if (p->prt_min <= 1 && p->prt_max == 65535 && p->maskbits == 0) {
+ family = tor_addr_family(&p->addr);
+ /* accept/reject *:* may have already been expanded into
+ * accept/reject *4:*,accept/reject *6:*
+ * But handle both forms.
+ */
+ if (family == AF_INET || family == AF_UNSPEC) {
+ found_ipv4_wildcard = 1;
+ }
+ if (family == AF_INET6 || family == AF_UNSPEC) {
+ found_ipv6_wildcard = 1;
+ }
+ }
+
+ /* We also find accept *4:*,reject *6:* ; and
+ * accept *4:*,<other policies>,accept *6:* ; and similar.
+ * That's ok, because they make any subsequent entries redundant. */
+ if (found_ipv4_wildcard && found_ipv6_wildcard) {
+ found_final_effective_entry = 1;
+ /* if we're not on the final entry in the list */
+ if (i < smartlist_len(*dest) - 1) {
+ first_redundant_entry = i + 1;
+ }
+ break;
+ }
+ }
+ /* Work out if there are redundant trailing entries in the policy list */
+ if (found_final_effective_entry && first_redundant_entry > 0) {
+ addr_policy_t *p;
+ /* Longest possible policy is
+ * "accept6 ffff:ffff:..255/128:10000-65535",
+ * which contains a max-length IPv6 address, plus 24 characters. */
+ char line[TOR_ADDR_BUF_LEN + 32];
+
+ tor_assert(first_redundant_entry < smartlist_len(*dest));
+ p = smartlist_get(*dest, first_redundant_entry);
+ /* since we've already parsed the policy into an addr_policy_t struct,
+ * we might not log exactly what the user typed in */
+ policy_write_item(line, TOR_ADDR_BUF_LEN + 32, p, 0);
+ log_warn(LD_DIR, "Exit policy '%s' and all following policies are "
+ "redundant, as it follows accept/reject *:* rules for both "
+ "IPv4 and IPv6. They will be removed from the exit policy. (Use "
+ "accept/reject *:* as the last entry in any exit policy.)",
+ line);
+ }
+
if (add_default_policy) {
append_exit_policy_string(dest, DEFAULT_EXIT_POLICY);
} else {
@@ -1013,20 +1161,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 +1191,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 +1220,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 +1245,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
@@ -1223,9 +1391,9 @@ policy_write_item(char *buf, size_t buflen, addr_policy_t *policy,
if (result < 0)
return -1;
written += strlen(buf);
- /* If the maskbits is 32 we don't need to give it. If the mask is 0,
- * we already wrote "*". */
- if (policy->maskbits < 32 && policy->maskbits > 0) {
+ /* If the maskbits is 32 (IPv4) or 128 (IPv6) we don't need to give it. If
+ the mask is 0, we already wrote "*". */
+ if (policy->maskbits < (is_ip6?128:32) && policy->maskbits > 0) {
if (tor_snprintf(buf+written, buflen-written, "/%d", policy->maskbits)<0)
return -1;
written += strlen(buf+written);