Commit a96c0aff authored by Nick Mathewson's avatar Nick Mathewson 🐘
Browse files

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:*"
parent 2eb7eafc
Loading
Loading
Loading
Loading
+1 −1
Changes for src/or/dirserv.c: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -2237,7 +2237,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
    }

    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);
      if (r<0) {
        log_warn(LD_BUG, "Not enough space in buffer.");
+1 −1
Changes for src/or/dirvote.c: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -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)
    goto done;
  summary = policy_summarize(ri->exit_policy);
  summary = policy_summarize(ri->exit_policy, AF_INET);
  if (ri->declared_family)
    family = smartlist_join_strings(ri->declared_family, " ", 0, NULL);

+9 −1
Changes for src/or/or.h: 9 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -1730,7 +1730,15 @@ typedef struct addr_policy_t {
  maskbits_t maskbits; /**< Accept/reject all addresses <b>a</b> such that the
                 * first <b>maskbits</b> bits of <b>a</b> match
                 * <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_max; /**< Highest port number to accept/reject. */
} addr_policy_t;
+104 −19
Changes for src/or/policies.c: 104 added lines, 19 removed lines.
Original line number Diff line number Diff line
@@ -59,8 +59,10 @@ typedef struct policy_summary_item_t {
static const char *private_nets[] = {
  "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",
  // "fc00::/7", "fe80::/10", "fec0::/10", "::/127",
  NULL };
  "[::]/8",
  "[fc00::]/7", "[fe80::]/10", "[fec0::]/10", "[ff00::]/8", "[::]/127",
  NULL
};

/** Replace all "private" entries in *<b>policy</b> with their expanded
 * equivalents. */
@@ -101,6 +103,49 @@ policy_expand_private(smartlist_t **policy)
  *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"
 * 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);
  } else {
    policy_expand_private(&result);
    policy_expand_unspec(&result);

    if (*dest) {
      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
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
   * to "accept *:80". */
  if (a->maskbits > b->maskbits) {
@@ -790,20 +840,32 @@ append_exit_policy_string(smartlist_t **policy, const char *more)
static void
exit_policy_remove_redundancies(smartlist_t *dest)
{
  addr_policy_t *ap, *tmp, *victim;
  addr_policy_t *ap, *tmp;
  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) {
      sa_family_t family;
      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) {
        /* This is a catch-all line -- later lines are unreachable. */
      while (i+1 < smartlist_len(dest)) {
        victim = smartlist_get(dest, i+1);
        smartlist_del(dest, i+1);
        addr_policy_free(victim);
        if (family == AF_INET) {
          kill_v4 = 1;
        } else if (family == AF_INET6) {
          kill_v6 = 1;
        }
      }
      break;
    }
  }

@@ -869,6 +931,10 @@ exit_policy_remove_redundancies(smartlist_t *dest)
 * policy afterwards. If <b>rejectprivate</b> is true, prepend
 * "reject private:*" to the policy. Return -1 if we can't parse cfg,
 * 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
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))
    return -1;
  if (add_default_policy)
  if (add_default_policy) {
    append_exit_policy_string(dest, DEFAULT_EXIT_POLICY);
  else
    append_exit_policy_string(dest, "reject *:*");
  } else {
    append_exit_policy_string(dest, "reject *4:*");
    append_exit_policy_string(dest, "reject *6:*");
  }
  exit_policy_remove_redundancies(*dest);

  return 0;
@@ -899,7 +967,8 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
void
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 *:* */
@@ -1001,17 +1070,26 @@ policy_write_item(char *buf, size_t buflen, addr_policy_t *policy,
  const char *addrpart;
  int result;
  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);

  /* write accept/reject 1.2.3.4 */
  if (policy->is_private)
  if (policy->is_private) {
    addrpart = "private";
  else if (policy->maskbits == 0)
  } else if (policy->maskbits == 0) {
    if (format_for_desc)
      addrpart = "*";
    else if (family == AF_INET6)
      addrpart = "*6";
    else if (family == AF_INET)
      addrpart = "*4";
    else
      addrpart = "*";
  } else {
    addrpart = addrbuf;
  }

  result = tor_snprintf(buf, buflen, "%s%s %s",
                        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).
 */
char *
policy_summarize(smartlist_t *policy)
policy_summarize(smartlist_t *policy, sa_family_t family)
{
  smartlist_t *summary = policy_summary_create();
  smartlist_t *accepts, *rejects;
@@ -1232,9 +1310,16 @@ policy_summarize(smartlist_t *policy)
  tor_assert(policy);

  /* 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);
  });
  } SMARTLIST_FOREACH_END(p);

  /* Now create two lists of strings, one for accepted and one
   * for rejected ports.  We take care to merge ranges so that
+2 −1
Changes for src/or/policies.h: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -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);
void policy_expand_private(smartlist_t **policy);
void policy_expand_unspec(smartlist_t **policy);
int policies_parse_from_options(const or_options_t *options);

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 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);
char *write_short_policy(const short_policy_t *policy);
Loading