Better policy support for IPv6

Now, "accept *:80" means "accept all addresses on port 80", and not
just IPv4.  For just v4, say "accept *4:80"; for just v6 say "accept
*6:80".

We can parse these policies from torrc just fine, and we should be
successfully keeping them out of descriptors for now.

We also now include appropriate IPv6 addresses in "reject private:*"
This commit is contained in:
Nick Mathewson 2012-10-24 15:03:29 -04:00
parent 2eb7eafc9d
commit a96c0affcb
9 changed files with 139 additions and 39 deletions

View File

@ -2237,7 +2237,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
} }
if (desc) { if (desc) {
summary = policy_summarize(desc->exit_policy); summary = policy_summarize(desc->exit_policy, AF_INET);
r = tor_snprintf(cp, buf_len - (cp-buf), "p %s\n", summary); r = tor_snprintf(cp, buf_len - (cp-buf), "p %s\n", summary);
if (r<0) { if (r<0) {
log_warn(LD_BUG, "Not enough space in buffer."); log_warn(LD_BUG, "Not enough space in buffer.");

View File

@ -3552,7 +3552,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
if (crypto_pk_write_public_key_to_string(ri->onion_pkey, &key, &keylen)<0) if (crypto_pk_write_public_key_to_string(ri->onion_pkey, &key, &keylen)<0)
goto done; goto done;
summary = policy_summarize(ri->exit_policy); summary = policy_summarize(ri->exit_policy, AF_INET);
if (ri->declared_family) if (ri->declared_family)
family = smartlist_join_strings(ri->declared_family, " ", 0, NULL); family = smartlist_join_strings(ri->declared_family, " ", 0, NULL);

View File

@ -1730,7 +1730,15 @@ typedef struct addr_policy_t {
maskbits_t maskbits; /**< Accept/reject all addresses <b>a</b> such that the maskbits_t maskbits; /**< Accept/reject all addresses <b>a</b> such that the
* first <b>maskbits</b> bits of <b>a</b> match * first <b>maskbits</b> bits of <b>a</b> match
* <b>addr</b>. */ * <b>addr</b>. */
tor_addr_t addr; /**< Base address to accept or reject. */ /** Base address to accept or reject.
*
* Note that wildcards are treated
* differntly depending on address family. An AF_UNSPEC address means
* "All addresses, IPv4 or IPv6." An AF_INET address with maskbits==0 means
* "All IPv4 addresses" and an AF_INET6 address with maskbits == 0 means
* "All IPv6 addresses".
**/
tor_addr_t addr;
uint16_t prt_min; /**< Lowest port number to accept/reject. */ uint16_t prt_min; /**< Lowest port number to accept/reject. */
uint16_t prt_max; /**< Highest port number to accept/reject. */ uint16_t prt_max; /**< Highest port number to accept/reject. */
} addr_policy_t; } addr_policy_t;

View File

@ -59,8 +59,10 @@ typedef struct policy_summary_item_t {
static const char *private_nets[] = { static const char *private_nets[] = {
"0.0.0.0/8", "169.254.0.0/16", "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", "127.0.0.0/8", "192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12",
// "fc00::/7", "fe80::/10", "fec0::/10", "::/127", "[::]/8",
NULL }; "[fc00::]/7", "[fe80::]/10", "[fec0::]/10", "[ff00::]/8", "[::]/127",
NULL
};
/** Replace all "private" entries in *<b>policy</b> with their expanded /** Replace all "private" entries in *<b>policy</b> with their expanded
* equivalents. */ * equivalents. */
@ -101,6 +103,49 @@ policy_expand_private(smartlist_t **policy)
*policy = tmp; *policy = tmp;
} }
/** Expand each of the AF_UNSPEC elements in *<b>policy</b> (which indicate
* protocol-neutral wildcards) into a pair of wildcard elements: one IPv4-
* specific and one IPv6-specific. */
void
policy_expand_unspec(smartlist_t **policy)
{
smartlist_t *tmp;
if (!*policy)
return;
tmp = smartlist_new();
SMARTLIST_FOREACH_BEGIN(*policy, addr_policy_t *, p) {
sa_family_t family = tor_addr_family(&p->addr);
if (family == AF_INET6 || family == AF_INET || p->is_private) {
smartlist_add(tmp, p);
} else if (family == AF_UNSPEC) {
addr_policy_t newpolicy_ipv4;
addr_policy_t newpolicy_ipv6;
memcpy(&newpolicy_ipv4, p, sizeof(addr_policy_t));
memcpy(&newpolicy_ipv6, p, sizeof(addr_policy_t));
newpolicy_ipv4.is_canonical = 0;
newpolicy_ipv6.is_canonical = 0;
if (p->maskbits != 0) {
log_warn(LD_BUG, "AF_UNSPEC policy with maskbits==%d", p->maskbits);
newpolicy_ipv4.maskbits = 0;
newpolicy_ipv6.maskbits = 0;
}
tor_addr_from_ipv4h(&newpolicy_ipv4.addr, 0);
tor_addr_from_ipv6_bytes(&newpolicy_ipv6.addr,
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy_ipv4));
smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy_ipv6));
addr_policy_free(p);
} else {
log_warn(LD_BUG, "Funny-looking address policy with family %d", family);
smartlist_add(tmp, p);
}
} SMARTLIST_FOREACH_END(p);
smartlist_free(*policy);
*policy = tmp;
}
/** /**
* Given a linked list of config lines containing "allow" and "deny" * Given a linked list of config lines containing "allow" and "deny"
* tokens, parse them and append the result to <b>dest</b>. Return -1 * tokens, parse them and append the result to <b>dest</b>. Return -1
@ -145,6 +190,7 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
addr_policy_list_free(result); addr_policy_list_free(result);
} else { } else {
policy_expand_private(&result); policy_expand_private(&result);
policy_expand_unspec(&result);
if (*dest) { if (*dest) {
smartlist_add_all(*dest, result); smartlist_add_all(*dest, result);
@ -735,6 +781,10 @@ compare_tor_addr_to_addr_policy(const tor_addr_t *addr, uint16_t port,
static int static int
addr_policy_covers(addr_policy_t *a, addr_policy_t *b) addr_policy_covers(addr_policy_t *a, addr_policy_t *b)
{ {
if (tor_addr_family(&a->addr) != tor_addr_family(&b->addr)) {
/* You can't cover a different family. */
return 0;
}
/* We can ignore accept/reject, since "accept *:80, reject *:80" reduces /* We can ignore accept/reject, since "accept *:80, reject *:80" reduces
* to "accept *:80". */ * to "accept *:80". */
if (a->maskbits > b->maskbits) { if (a->maskbits > b->maskbits) {
@ -790,20 +840,32 @@ append_exit_policy_string(smartlist_t **policy, const char *more)
static void static void
exit_policy_remove_redundancies(smartlist_t *dest) exit_policy_remove_redundancies(smartlist_t *dest)
{ {
addr_policy_t *ap, *tmp, *victim; addr_policy_t *ap, *tmp;
int i, j; int i, j;
/* Step one: find a *:* entry and cut off everything after it. */ /* Step one: kill every ipv4 thing after *4:*, every IPv6 thing after *6:*
*/
{
int kill_v4=0, kill_v6=0;
for (i = 0; i < smartlist_len(dest); ++i) { for (i = 0; i < smartlist_len(dest); ++i) {
sa_family_t family;
ap = smartlist_get(dest, i); ap = smartlist_get(dest, i);
family = tor_addr_family(&ap->addr);
if ((family == AF_INET && kill_v4) ||
(family == AF_INET6 && kill_v6)) {
smartlist_del_keeporder(dest, i--);
addr_policy_free(ap);
continue;
}
if (ap->maskbits == 0 && ap->prt_min <= 1 && ap->prt_max >= 65535) { if (ap->maskbits == 0 && ap->prt_min <= 1 && ap->prt_max >= 65535) {
/* This is a catch-all line -- later lines are unreachable. */ /* This is a catch-all line -- later lines are unreachable. */
while (i+1 < smartlist_len(dest)) { if (family == AF_INET) {
victim = smartlist_get(dest, i+1); kill_v4 = 1;
smartlist_del(dest, i+1); } else if (family == AF_INET6) {
addr_policy_free(victim); kill_v6 = 1;
}
} }
break;
} }
} }
@ -869,6 +931,10 @@ exit_policy_remove_redundancies(smartlist_t *dest)
* policy afterwards. If <b>rejectprivate</b> is true, prepend * policy afterwards. If <b>rejectprivate</b> is true, prepend
* "reject private:*" to the policy. Return -1 if we can't parse cfg, * "reject private:*" to the policy. Return -1 if we can't parse cfg,
* else return 0. * 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,
* see router_add_exit_policy.
*/ */
int int
policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest, policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
@ -885,10 +951,12 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
} }
if (parse_addr_policy(cfg, dest, -1)) if (parse_addr_policy(cfg, dest, -1))
return -1; return -1;
if (add_default_policy) if (add_default_policy) {
append_exit_policy_string(dest, DEFAULT_EXIT_POLICY); append_exit_policy_string(dest, DEFAULT_EXIT_POLICY);
else } else {
append_exit_policy_string(dest, "reject *:*"); append_exit_policy_string(dest, "reject *4:*");
append_exit_policy_string(dest, "reject *6:*");
}
exit_policy_remove_redundancies(*dest); exit_policy_remove_redundancies(*dest);
return 0; return 0;
@ -899,7 +967,8 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
void void
policies_exit_policy_append_reject_star(smartlist_t **dest) policies_exit_policy_append_reject_star(smartlist_t **dest)
{ {
append_exit_policy_string(dest, "reject *:*"); append_exit_policy_string(dest, "reject *4:*");
append_exit_policy_string(dest, "reject *6:*");
} }
/** Replace the exit policy of <b>node</b> with reject *:* */ /** Replace the exit policy of <b>node</b> with reject *:* */
@ -1001,17 +1070,26 @@ policy_write_item(char *buf, size_t buflen, addr_policy_t *policy,
const char *addrpart; const char *addrpart;
int result; int result;
const int is_accept = policy->policy_type == ADDR_POLICY_ACCEPT; const int is_accept = policy->policy_type == ADDR_POLICY_ACCEPT;
const int is_ip6 = tor_addr_family(&policy->addr) == AF_INET6; const sa_family_t family = tor_addr_family(&policy->addr);
const int is_ip6 = (family == AF_INET6);
tor_addr_to_str(addrbuf, &policy->addr, sizeof(addrbuf), 1); tor_addr_to_str(addrbuf, &policy->addr, sizeof(addrbuf), 1);
/* write accept/reject 1.2.3.4 */ /* write accept/reject 1.2.3.4 */
if (policy->is_private) if (policy->is_private) {
addrpart = "private"; addrpart = "private";
else if (policy->maskbits == 0) } else if (policy->maskbits == 0) {
if (format_for_desc)
addrpart = "*"; addrpart = "*";
else if (family == AF_INET6)
addrpart = "*6";
else if (family == AF_INET)
addrpart = "*4";
else else
addrpart = "*";
} else {
addrpart = addrbuf; addrpart = addrbuf;
}
result = tor_snprintf(buf, buflen, "%s%s %s", result = tor_snprintf(buf, buflen, "%s%s %s",
is_accept ? "accept" : "reject", is_accept ? "accept" : "reject",
@ -1220,7 +1298,7 @@ policy_summary_add_item(smartlist_t *summary, addr_policy_t *p)
* is an exception to the shorter-representation-wins rule). * is an exception to the shorter-representation-wins rule).
*/ */
char * char *
policy_summarize(smartlist_t *policy) policy_summarize(smartlist_t *policy, sa_family_t family)
{ {
smartlist_t *summary = policy_summary_create(); smartlist_t *summary = policy_summary_create();
smartlist_t *accepts, *rejects; smartlist_t *accepts, *rejects;
@ -1232,9 +1310,16 @@ policy_summarize(smartlist_t *policy)
tor_assert(policy); tor_assert(policy);
/* Create the summary list */ /* Create the summary list */
SMARTLIST_FOREACH(policy, addr_policy_t *, p, { SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, p) {
sa_family_t f = tor_addr_family(&p->addr);
if (f != AF_INET && f != AF_INET6) {
log_warn(LD_BUG, "Weird family when summarizing address policy");
}
if (f != family)
continue;
/* XXXX-ipv6 More family work is needed */
policy_summary_add_item(summary, p); policy_summary_add_item(summary, p);
}); } SMARTLIST_FOREACH_END(p);
/* Now create two lists of strings, one for accepted and one /* Now create two lists of strings, one for accepted and one
* for rejected ports. We take care to merge ranges so that * for rejected ports. We take care to merge ranges so that

View File

@ -31,6 +31,7 @@ int authdir_policy_badexit_address(uint32_t addr, uint16_t port);
int validate_addr_policies(const or_options_t *options, char **msg); int validate_addr_policies(const or_options_t *options, char **msg);
void policy_expand_private(smartlist_t **policy); void policy_expand_private(smartlist_t **policy);
void policy_expand_unspec(smartlist_t **policy);
int policies_parse_from_options(const or_options_t *options); int policies_parse_from_options(const or_options_t *options);
addr_policy_t *addr_policy_get_canonical_entry(addr_policy_t *ent); addr_policy_t *addr_policy_get_canonical_entry(addr_policy_t *ent);
@ -58,7 +59,7 @@ void addr_policy_list_free(smartlist_t *p);
void addr_policy_free(addr_policy_t *p); void addr_policy_free(addr_policy_t *p);
void policies_free_all(void); void policies_free_all(void);
char *policy_summarize(smartlist_t *policy); char *policy_summarize(smartlist_t *policy, sa_family_t family);
short_policy_t *parse_short_policy(const char *summary); short_policy_t *parse_short_policy(const char *summary);
char *write_short_policy(const short_policy_t *policy); char *write_short_policy(const short_policy_t *policy);

View File

@ -2001,7 +2001,6 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
size_t onion_pkeylen, identity_pkeylen; size_t onion_pkeylen, identity_pkeylen;
size_t written; size_t written;
int result=0; int result=0;
addr_policy_t *tmpe;
char *family_line; char *family_line;
char *extra_or_address = NULL; char *extra_or_address = NULL;
const or_options_t *options = get_options(); const or_options_t *options = get_options();
@ -2130,11 +2129,12 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
if (!router->exit_policy || !smartlist_len(router->exit_policy)) { if (!router->exit_policy || !smartlist_len(router->exit_policy)) {
strlcat(s+written, "reject *:*\n", maxlen-written); strlcat(s+written, "reject *:*\n", maxlen-written);
written += strlen("reject *:*\n"); written += strlen("reject *:*\n");
tmpe = NULL;
} else if (router->exit_policy) { } else if (router->exit_policy) {
int i; int i;
for (i = 0; i < smartlist_len(router->exit_policy); ++i) { for (i = 0; i < smartlist_len(router->exit_policy); ++i) {
tmpe = smartlist_get(router->exit_policy, i); addr_policy_t *tmpe = smartlist_get(router->exit_policy, i);
if (tor_addr_family(&tmpe->addr) == AF_INET6)
continue; /* Don't include IPv6 parts of address policy */
result = policy_write_item(s+written, maxlen-written, tmpe, 1); result = policy_write_item(s+written, maxlen-written, tmpe, 1);
if (result < 0) { if (result < 0) {
log_warn(LD_BUG,"descriptor policy_write_item ran out of room!"); log_warn(LD_BUG,"descriptor policy_write_item ran out of room!");

View File

@ -535,7 +535,8 @@ static token_rule_t microdesc_token_table[] = {
/* static function prototypes */ /* static function prototypes */
static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok); static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok);
static addr_policy_t *router_parse_addr_policy(directory_token_t *tok); static addr_policy_t *router_parse_addr_policy(directory_token_t *tok,
unsigned fmt_flags);
static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok); static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok);
static int router_get_hash_impl(const char *s, size_t s_len, char *digest, static int router_get_hash_impl(const char *s, size_t s_len, char *digest,
@ -3633,6 +3634,10 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
/** Parse the addr policy in the string <b>s</b> and return it. If /** Parse the addr policy in the string <b>s</b> and return it. If
* assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or * assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
* ADDR_POLICY_REJECT) for items that specify no action. * ADDR_POLICY_REJECT) for items that specify no action.
*
* The addr_policy_t returned by this function can have its address set to
* AF_UNSPEC for '*'. Use policy_expand_unspec() to turn this into a pair
* of AF_INET and AF_INET6 items.
*/ */
addr_policy_t * addr_policy_t *
router_parse_addr_policy_item_from_string(const char *s, int assume_action) router_parse_addr_policy_item_from_string(const char *s, int assume_action)
@ -3672,7 +3677,7 @@ router_parse_addr_policy_item_from_string(const char *s, int assume_action)
goto err; goto err;
} }
r = router_parse_addr_policy(tok); r = router_parse_addr_policy(tok, TAPMP_EXTENDED_STAR);
goto done; goto done;
err: err:
r = NULL; r = NULL;
@ -3691,7 +3696,7 @@ static int
router_add_exit_policy(routerinfo_t *router, directory_token_t *tok) router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
{ {
addr_policy_t *newe; addr_policy_t *newe;
newe = router_parse_addr_policy(tok); newe = router_parse_addr_policy(tok, 0);
if (!newe) if (!newe)
return -1; return -1;
if (! router->exit_policy) if (! router->exit_policy)
@ -3716,7 +3721,7 @@ router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
/** Given a K_ACCEPT or K_REJECT token and a router, create and return /** Given a K_ACCEPT or K_REJECT token and a router, create and return
* a new exit_policy_t corresponding to the token. */ * a new exit_policy_t corresponding to the token. */
static addr_policy_t * static addr_policy_t *
router_parse_addr_policy(directory_token_t *tok) router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
{ {
addr_policy_t newe; addr_policy_t newe;
char *arg; char *arg;
@ -3738,7 +3743,7 @@ router_parse_addr_policy(directory_token_t *tok)
else else
newe.policy_type = ADDR_POLICY_ACCEPT; newe.policy_type = ADDR_POLICY_ACCEPT;
if (tor_addr_parse_mask_ports(arg, 0, &newe.addr, &newe.maskbits, if (tor_addr_parse_mask_ports(arg, fmt_flags, &newe.addr, &newe.maskbits,
&newe.prt_min, &newe.prt_max) < 0) { &newe.prt_min, &newe.prt_max) < 0) {
log_warn(LD_DIR,"Couldn't parse line %s. Dropping", escaped(arg)); log_warn(LD_DIR,"Couldn't parse line %s. Dropping", escaped(arg));
return NULL; return NULL;

View File

@ -148,6 +148,7 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
SMARTLIST_DEL_CURRENT(list, nick); SMARTLIST_DEL_CURRENT(list, nick);
} }
} SMARTLIST_FOREACH_END(nick); } SMARTLIST_FOREACH_END(nick);
policy_expand_unspec(&target->policies);
smartlist_add_all(target->list, list); smartlist_add_all(target->list, list);
smartlist_free(list); smartlist_free(list);
if (added_countries) if (added_countries)

View File

@ -1046,7 +1046,7 @@ test_policy_summary_helper(const char *policy_str,
r = policies_parse_exit_policy(&line, &policy, 0, NULL, 1); r = policies_parse_exit_policy(&line, &policy, 0, NULL, 1);
test_eq(r, 0); test_eq(r, 0);
summary = policy_summarize(policy); summary = policy_summarize(policy, AF_INET);
test_assert(summary != NULL); test_assert(summary != NULL);
test_streq(summary, expected_summary); test_streq(summary, expected_summary);
@ -1192,7 +1192,7 @@ test_policies(void)
test_assert(policy); test_assert(policy);
//test_streq(policy->string, "accept *:80"); //test_streq(policy->string, "accept *:80");
//test_streq(policy->next->string, "reject *:*"); //test_streq(policy->next->string, "reject *:*");
test_eq(smartlist_len(policy), 2); test_eq(smartlist_len(policy), 4);
/* test policy summaries */ /* test policy summaries */
/* check if we properly ignore private IP addresses */ /* check if we properly ignore private IP addresses */