aboutsummaryrefslogtreecommitdiff
path: root/src/feature/dirparse/policy_parse.c
blob: 8d30410f58f36911e2bfe802289761e0a1e8e1b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/* 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 <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;
  }

  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;

  /* Safeguard: always flag non canonical because it is a stack allocated
   * object and thus should not be considered a copy stored in a map. */
  result.is_canonical = 0;

  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);
}