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 | |
parent | 95e2eb9083d2cd9c79c3f4151850c86cbeaf4cc4 (diff) | |
download | tor-35db3f8162f132cec7afc148eda8f7482dbeeb76.tar.gz tor-35db3f8162f132cec7afc148eda8f7482dbeeb76.zip |
Extract addr-policy parsing code.
Diffstat (limited to 'src/feature/dirparse')
-rw-r--r-- | src/feature/dirparse/parsecommon.c | 4 | ||||
-rw-r--r-- | src/feature/dirparse/parsecommon.h | 4 | ||||
-rw-r--r-- | src/feature/dirparse/policy_parse.c | 218 | ||||
-rw-r--r-- | src/feature/dirparse/policy_parse.h | 25 | ||||
-rw-r--r-- | src/feature/dirparse/routerparse.c | 200 | ||||
-rw-r--r-- | src/feature/dirparse/routerparse.h | 6 |
6 files changed, 254 insertions, 203 deletions
diff --git a/src/feature/dirparse/parsecommon.c b/src/feature/dirparse/parsecommon.c index ab815f585d..c12f199e4e 100644 --- a/src/feature/dirparse/parsecommon.c +++ b/src/feature/dirparse/parsecommon.c @@ -51,7 +51,7 @@ token_clear(directory_token_t *tok) int tokenize_string(memarea_t *area, const char *start, const char *end, smartlist_t *out, - token_rule_t *table, int flags) + const token_rule_t *table, int flags) { const char **s; directory_token_t *tok = NULL; @@ -257,7 +257,7 @@ token_check_object(memarea_t *area, const char *kwd, */ directory_token_t * get_next_token(memarea_t *area, - const char **s, const char *eos, token_rule_t *table) + const char **s, const char *eos, const token_rule_t *table) { /** Reject any object at least this big; it is probably an overflow, an * attack, a bug, or some other nonsense. */ diff --git a/src/feature/dirparse/parsecommon.h b/src/feature/dirparse/parsecommon.h index d0f3810c0b..f14862f04a 100644 --- a/src/feature/dirparse/parsecommon.h +++ b/src/feature/dirparse/parsecommon.h @@ -302,12 +302,12 @@ void token_clear(directory_token_t *tok); int tokenize_string(struct memarea_t *area, const char *start, const char *end, struct smartlist_t *out, - token_rule_t *table, + const token_rule_t *table, int flags); directory_token_t *get_next_token(struct memarea_t *area, const char **s, const char *eos, - token_rule_t *table); + const token_rule_t *table); directory_token_t *find_by_keyword_(struct smartlist_t *s, directory_keyword keyword, 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); +} + diff --git a/src/feature/dirparse/policy_parse.h b/src/feature/dirparse/policy_parse.h new file mode 100644 index 0000000000..887aa9261b --- /dev/null +++ b/src/feature/dirparse/policy_parse.h @@ -0,0 +1,25 @@ +/* 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.h + * \brief Header file for policy_parse.c. + **/ + +#ifndef TOR_POLICY_PARSE_H +#define TOR_POLICY_PARSE_H + +#include "lib/testsupport/testsupport.h" + +struct directory_token_t; + +MOCK_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string, + (const char *s, int assume_action, int *malformed_list)); + +addr_policy_t *router_parse_addr_policy(struct directory_token_t *tok, + unsigned fmt_flags); + +#endif /* !defined(TOR_POLICY_PARSE_H) */ diff --git a/src/feature/dirparse/routerparse.c b/src/feature/dirparse/routerparse.c index 0e97735643..54c0e89480 100644 --- a/src/feature/dirparse/routerparse.c +++ b/src/feature/dirparse/routerparse.c @@ -51,6 +51,7 @@ **/ #define ROUTERPARSE_PRIVATE +#define EXPOSE_ROUTERDESC_TOKEN_TABLE #include "core/or/or.h" #include "app/config/config.h" @@ -61,6 +62,7 @@ #include "feature/dirauth/shared_random.h" #include "feature/dircommon/voting_schedule.h" #include "feature/dirparse/parsecommon.h" +#include "feature/dirparse/policy_parse.h" #include "feature/dirparse/routerparse.h" #include "feature/hs_common/shared_random_client.h" #include "feature/nodelist/authcert.h" @@ -111,7 +113,7 @@ /****************************************************************************/ /** List of tokens recognized in router descriptors */ -static token_rule_t routerdesc_token_table[] = { +const token_rule_t routerdesc_token_table[] = { T0N("reject", K_REJECT, ARGS, NO_OBJ ), T0N("accept", K_ACCEPT, ARGS, NO_OBJ ), T0N("reject6", K_REJECT6, ARGS, NO_OBJ ), @@ -197,10 +199,6 @@ static token_rule_t extrainfo_token_table[] = { /* static function prototypes */ 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, - unsigned fmt_flags); -static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok); - static smartlist_t *find_all_exitpolicy(smartlist_t *s); /** Set <b>digest</b> to the SHA-1 digest of the hash of the first router in @@ -1201,109 +1199,6 @@ extrainfo_parse_entry_from_string(const char *s, const char *end, return extrainfo; } -/** 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; -} - /** Add an exit policy stored in the token <b>tok</b> to the router info in * <b>router</b>. Return 0 on success, -1 on failure. */ static int @@ -1340,95 +1235,6 @@ router_add_exit_policy(routerinfo_t *router, directory_token_t *tok) return 0; } -/** 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 */ -static 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); -} - /** Return a newly allocated smartlist of all accept or reject tokens in * <b>s</b>. */ diff --git a/src/feature/dirparse/routerparse.h b/src/feature/dirparse/routerparse.h index 72f812ee4a..3022d60d36 100644 --- a/src/feature/dirparse/routerparse.h +++ b/src/feature/dirparse/routerparse.h @@ -34,8 +34,6 @@ struct digest_ri_map_t; extrainfo_t *extrainfo_parse_entry_from_string(const char *s, const char *end, int cache_copy, struct digest_ri_map_t *routermap, int *can_dl_again_out); -MOCK_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string, - (const char *s, int assume_action, int *malformed_list)); int find_single_ipv6_orport(const smartlist_t *list, tor_addr_t *addr_out, @@ -44,6 +42,10 @@ int find_single_ipv6_orport(const smartlist_t *list, void routerparse_init(void); void routerparse_free_all(void); +#ifdef EXPOSE_ROUTERDESC_TOKEN_TABLE +extern const struct token_rule_t routerdesc_token_table[]; +#endif + #define ED_DESC_SIGNATURE_PREFIX "Tor router descriptor signature v1" #endif /* !defined(TOR_ROUTERPARSE_H) */ |