/* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file policy_parse.c * \brief Code to parse address policies. **/ #define ROUTERDESC_TOKEN_TABLE_PRIVATE #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 s 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 *malformed_list 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; } addr_policy_t *result = addr_policy_get_canonical_entry(&newe); /* It would be a nasty error to return 'newe', and sometimes * addr_policy_get_canonical_entry() can return its argument. But in this * case, it won't, since newe is *not* canonical. We assert here to make * sure that the compiler knows it too. */ tor_assert(result != &newe); return result; } /** 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); }