diff options
author | Nick Mathewson <nickm@torproject.org> | 2018-10-01 11:08:09 -0500 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2018-10-01 11:17:19 -0500 |
commit | 35db3f8162f132cec7afc148eda8f7482dbeeb76 (patch) | |
tree | 7c00b06286d5c75f5ab5d854a201031c7e7a0f9a /src/feature/dirparse/policy_parse.c | |
parent | 95e2eb9083d2cd9c79c3f4151850c86cbeaf4cc4 (diff) | |
download | tor-35db3f8162f132cec7afc148eda8f7482dbeeb76.tar.gz tor-35db3f8162f132cec7afc148eda8f7482dbeeb76.zip |
Extract addr-policy parsing code.
Diffstat (limited to 'src/feature/dirparse/policy_parse.c')
-rw-r--r-- | src/feature/dirparse/policy_parse.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/src/feature/dirparse/policy_parse.c b/src/feature/dirparse/policy_parse.c new file mode 100644 index 0000000000..e102a62282 --- /dev/null +++ b/src/feature/dirparse/policy_parse.c @@ -0,0 +1,218 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file policy_parse.c + * \brief Code to parse address policies. + **/ + +#define EXPOSE_ROUTERDESC_TOKEN_TABLE + +#include "core/or/or.h" + +#include "core/or/policies.h" +#include "feature/dirparse/parsecommon.h" +#include "feature/dirparse/policy_parse.h" +#include "feature/dirparse/routerparse.h" +#include "feature/dirparse/unparseable.h" +#include "lib/memarea/memarea.h" + +#include "core/or/addr_policy_st.h" + +static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok); + +/** 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 + * ADDR_POLICY_REJECT) for items that specify no action. + * + * Returns NULL on policy errors. + * + * Set *<b>malformed_list</b> to true if the entire policy list should be + * discarded. Otherwise, set it to false, and only this item should be ignored + * on error - the rest of the policy list can continue to be processed and + * used. + * + * 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. + */ +MOCK_IMPL(addr_policy_t *, +router_parse_addr_policy_item_from_string,(const char *s, int assume_action, + int *malformed_list)) +{ + directory_token_t *tok = NULL; + const char *cp, *eos; + /* Longest possible policy is + * "accept6 [ffff:ffff:..255]/128:10000-65535", + * which contains a max-length IPv6 address, plus 26 characters. + * But note that there can be an arbitrary amount of space between the + * accept and the address:mask/port element. + * We don't need to multiply TOR_ADDR_BUF_LEN by 2, as there is only one + * IPv6 address. But making the buffer shorter might cause valid long lines, + * which parsed in previous versions, to fail to parse in new versions. + * (These lines would have to have excessive amounts of whitespace.) */ + char line[TOR_ADDR_BUF_LEN*2 + 32]; + addr_policy_t *r; + memarea_t *area = NULL; + + tor_assert(malformed_list); + *malformed_list = 0; + + s = eat_whitespace(s); + /* We can only do assume_action on []-quoted IPv6, as "a" (accept) + * and ":" (port separator) are ambiguous */ + if ((*s == '*' || *s == '[' || TOR_ISDIGIT(*s)) && assume_action >= 0) { + if (tor_snprintf(line, sizeof(line), "%s %s", + assume_action == ADDR_POLICY_ACCEPT?"accept":"reject", s)<0) { + log_warn(LD_DIR, "Policy %s is too long.", escaped(s)); + return NULL; + } + cp = line; + tor_strlower(line); + } else { /* assume an already well-formed address policy line */ + cp = s; + } + + eos = cp + strlen(cp); + area = memarea_new(); + tok = get_next_token(area, &cp, eos, routerdesc_token_table); + if (tok->tp == ERR_) { + log_warn(LD_DIR, "Error reading address policy: %s", tok->error); + goto err; + } + if (tok->tp != K_ACCEPT && tok->tp != K_ACCEPT6 && + tok->tp != K_REJECT && tok->tp != K_REJECT6) { + log_warn(LD_DIR, "Expected 'accept' or 'reject'."); + goto err; + } + + /* Use the extended interpretation of accept/reject *, + * expanding it into an IPv4 wildcard and an IPv6 wildcard. + * Also permit *4 and *6 for IPv4 and IPv6 only wildcards. */ + r = router_parse_addr_policy(tok, TAPMP_EXTENDED_STAR); + if (!r) { + goto err; + } + + /* Ensure that accept6/reject6 fields are followed by IPv6 addresses. + * AF_UNSPEC addresses are only permitted on the accept/reject field type. + * Unlike descriptors, torrcs exit policy accept/reject can be followed by + * either an IPv4 or IPv6 address. */ + if ((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) && + tor_addr_family(&r->addr) != AF_INET6) { + /* This is a non-fatal error, just ignore this one entry. */ + *malformed_list = 0; + log_warn(LD_DIR, "IPv4 address '%s' with accept6/reject6 field type in " + "exit policy. Ignoring, but continuing to parse rules. (Use " + "accept/reject with IPv4 addresses.)", + tok->n_args == 1 ? tok->args[0] : ""); + addr_policy_free(r); + r = NULL; + goto done; + } + + goto done; + err: + *malformed_list = 1; + r = NULL; + done: + token_clear(tok); + if (area) { + DUMP_AREA(area, "policy item"); + memarea_drop_all(area); + } + return r; +} + +/** Given a K_ACCEPT[6] or K_REJECT[6] token and a router, create and return + * a new exit_policy_t corresponding to the token. If TAPMP_EXTENDED_STAR + * is set in fmt_flags, K_ACCEPT6 and K_REJECT6 tokens followed by * + * expand to IPv6-only policies, otherwise they expand to IPv4 and IPv6 + * policies */ +addr_policy_t * +router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags) +{ + addr_policy_t newe; + char *arg; + + tor_assert(tok->tp == K_REJECT || tok->tp == K_REJECT6 || + tok->tp == K_ACCEPT || tok->tp == K_ACCEPT6); + + if (tok->n_args != 1) + return NULL; + arg = tok->args[0]; + + if (!strcmpstart(arg,"private")) + return router_parse_addr_policy_private(tok); + + memset(&newe, 0, sizeof(newe)); + + if (tok->tp == K_REJECT || tok->tp == K_REJECT6) + newe.policy_type = ADDR_POLICY_REJECT; + 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 ((fmt_flags & TAPMP_EXTENDED_STAR) + && (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)); + return NULL; + } + + return addr_policy_get_canonical_entry(&newe); +} + +/** 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) +{ + const char *arg; + uint16_t port_min, port_max; + addr_policy_t result; + + arg = tok->args[0]; + if (strcmpstart(arg, "private")) + return NULL; + + arg += strlen("private"); + arg = (char*) eat_whitespace(arg); + if (!arg || *arg != ':') + return NULL; + + if (parse_port_range(arg+1, &port_min, &port_max)<0) + return NULL; + + memset(&result, 0, sizeof(result)); + if (tok->tp == K_REJECT || tok->tp == K_REJECT6) + result.policy_type = ADDR_POLICY_REJECT; + else + result.policy_type = ADDR_POLICY_ACCEPT; + result.is_private = 1; + result.prt_min = port_min; + result.prt_max = port_max; + + if (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) { + log_warn(LD_GENERAL, + "'%s' expands into rules which apply to all private IPv4 and " + "IPv6 addresses. (Use accept/reject private:* for IPv4 and " + "IPv6.)", tok->n_args == 1 ? tok->args[0] : ""); + } + + return addr_policy_get_canonical_entry(&result); +} + |