Browse Source

Merge branch 'bug17027-reject-private-all-interfaces-v2' into bug16069-bug17027

src/test/test_policy.c:
Merged calls to policies_parse_exit_policy by adding additional arguments.
fixup to remaining instance of ~EXIT_POLICY_IPV6_ENABLED.
Compacting logic test now produces previous list length of 4, corrected this.

src/config/torrc.sample.in:
src/config/torrc.minimal.in-staging:
Merged torrc modification dates in favour of latest.
teor (Tim Wilson-Brown) 8 years ago
parent
commit
a659a3fced

+ 6 - 0
changes/bug17027-reject-private-all-interfaces

@@ -0,0 +1,6 @@
+  o Minor bug fixes (security, exit policies):
+    - ExitPolicyRejectPrivate rejects more private addresses by default:
+      * the relay's published IPv6 address (if any), and
+      * any publicly routable IPv4 or IPv6 addresses on any local interfaces.
+      Resolves ticket 17027. Patch by "teor".
+      Patch on 42b8fb5a1523 (11 Nov 2007), released in 0.2.0.11-alpha.

+ 5 - 2
doc/tor.1.txt

@@ -1590,8 +1590,11 @@ is non-zero):
     IPv4 and IPv6 addresses.
 
 [[ExitPolicyRejectPrivate]] **ExitPolicyRejectPrivate** **0**|**1**::
-    Reject all private (local) networks, along with your own public IP address,
-    at the beginning of your exit policy. See above entry on ExitPolicy.
+    Reject all private (local) networks, along with your own configured public
+    IPv4 and IPv6 addresses, at the beginning of your exit policy. Also reject
+    any public IPv4 and IPv6 addresses on any interface on the relay. (If
+    IPv6Exit is not set, all IPv6 addresses will be rejected anyway.)
+    See above entry on ExitPolicy.
     (Default: 1)
 
 [[IPv6Exit]] **IPv6Exit** **0**|**1**::

+ 91 - 23
src/common/address.c

@@ -1527,7 +1527,7 @@ get_interface_addresses_raw(int severity)
 }
 
 /** Return true iff <b>a</b> is a multicast address.  */
-static int
+STATIC int
 tor_addr_is_multicast(const tor_addr_t *a)
 {
   sa_family_t family = tor_addr_family(a);
@@ -1617,43 +1617,108 @@ get_interface_address6_via_udp_socket_hack(int severity,
   return r;
 }
 
-/** Set *<b>addr</b> to the IP address (if any) of whatever interface
- * connects to the Internet.  This address should only be used in checking
- * whether our address has changed.  Return 0 on success, -1 on failure.
+/** Set *<b>addr</b> to an arbitrary IP address (if any) of an interface that
+ * connects to the Internet.  Prefer public IP addresses to internal IP
+ * addresses.  This address should only be used in checking whether our
+ * address has changed, as it may be an internal IP address.  Return 0 on
+ * success, -1 on failure.
+ * Prefer get_interface_address6_list for a list of all addresses on all
+ * interfaces which connect to the Internet.
  */
 MOCK_IMPL(int,
 get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
 {
-  /* XXX really, this function should yield a smartlist of addresses. */
   smartlist_t *addrs;
+  int rv = -1;
   tor_assert(addr);
 
+  /* Get a list of public or internal IPs in arbitrary order */
+  addrs = get_interface_address6_list(severity, family, 1);
+
+  /* Find the first non-internal address, or the last internal address
+   * Ideally, we want the default route, see #12377 for details */
+  SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
+    tor_addr_copy(addr, a);
+    rv = 0;
+
+    /* If we found a non-internal address, declare success.  Otherwise,
+     * keep looking. */
+    if (!tor_addr_is_internal(a, 0))
+      break;
+  } SMARTLIST_FOREACH_END(a);
+
+  free_interface_address6_list(addrs);
+  return rv;
+}
+
+/** Free a smartlist of IP addresses returned by get_interface_address6_list.
+ */
+void
+free_interface_address6_list(smartlist_t *addrs)
+{
+  SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a));
+  smartlist_free(addrs);
+}
+
+/** Return a smartlist of the IP addresses of type family from all interfaces
+ * on the server. Excludes loopback and multicast addresses. Only includes
+ * internal addresses if include_internal is true. (Note that a relay behind
+ * NAT may use an internal address to connect to the Internet.)
+ * An empty smartlist means that there are no addresses of the selected type
+ * matching these criteria.
+ * Returns NULL on failure.
+ * Use free_interface_address6_list to free the returned list.
+ */
+MOCK_IMPL(smartlist_t *,get_interface_address6_list,(int severity,
+                                                     sa_family_t family,
+                                                     int include_internal))
+{
+  smartlist_t *addrs;
+  tor_addr_t addr;
+
   /* Try to do this the smart way if possible. */
   if ((addrs = get_interface_addresses_raw(severity))) {
-    int rv = -1;
-    SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
-      if (family != AF_UNSPEC && family != tor_addr_family(a))
+    SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a)
+    {
+      if (family != AF_UNSPEC && family != tor_addr_family(a)) {
+        SMARTLIST_DEL_CURRENT(addrs, a);
+        tor_free(a);
         continue;
+      }
+
       if (tor_addr_is_loopback(a) ||
-          tor_addr_is_multicast(a))
+          tor_addr_is_multicast(a)) {
+        SMARTLIST_DEL_CURRENT(addrs, a);
+        tor_free(a);
         continue;
+      }
 
-      tor_addr_copy(addr, a);
-      rv = 0;
-
-      /* If we found a non-internal address, declare success.  Otherwise,
-       * keep looking. */
-      if (!tor_addr_is_internal(a, 0))
-        break;
+      if (!include_internal && tor_addr_is_internal(a, 0)) {
+        SMARTLIST_DEL_CURRENT(addrs, a);
+        tor_free(a);
+        continue;
+      }
     } SMARTLIST_FOREACH_END(a);
+  }
+
+  if (addrs && smartlist_len(addrs) > 0) {
+    return addrs;
+  }
 
-    SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a));
+  /* if we removed all entries as unsuitable */
+  if (addrs) {
     smartlist_free(addrs);
-    return rv;
   }
 
   /* Okay, the smart way is out. */
-  return get_interface_address6_via_udp_socket_hack(severity,family,addr);
+  get_interface_address6_via_udp_socket_hack(severity,family,&addr);
+  if (!include_internal && tor_addr_is_internal(&addr, 0)) {
+    return smartlist_new();
+  } else {
+    addrs = smartlist_new();
+    smartlist_add(addrs, tor_dup_addr(&addr));
+    return addrs;
+  }
 }
 
 /* ======
@@ -1895,10 +1960,13 @@ tor_dup_ip(uint32_t addr)
 }
 
 /**
- * Set *<b>addr</b> to the host-order IPv4 address (if any) of whatever
- * interface connects to the Internet.  This address should only be used in
- * checking whether our address has changed.  Return 0 on success, -1 on
- * failure.
+ * Set *<b>addr</b> to a host-order IPv4 address (if any) of an
+ * interface that connects to the Internet.  Prefer public IP addresses to
+ * internal IP addresses.  This address should only be used in checking
+ * whether our address has changed, as it may be an internal IPv4 address.
+ * Return 0 on success, -1 on failure.
+ * Prefer get_interface_address_list6 for a list of all IPv4 and IPv6
+ * addresses on all interfaces which connect to the Internet.
  */
 MOCK_IMPL(int,
 get_interface_address,(int severity, uint32_t *addr))

+ 27 - 1
src/common/address.h

@@ -15,6 +15,7 @@
 #include "orconfig.h"
 #include "torint.h"
 #include "compat.h"
+#include "container.h"
 
 #ifdef ADDRESS_PRIVATE
 
@@ -43,7 +44,6 @@
 #endif
 
 // TODO win32 specific includes
-#include "container.h"
 #endif // ADDRESS_PRIVATE
 
 /** The number of bits from an address to consider while doing a masked
@@ -190,8 +190,13 @@ char *tor_dup_addr(const tor_addr_t *addr) ATTR_MALLOC;
 const char *fmt_addr_impl(const tor_addr_t *addr, int decorate);
 const char *fmt_addrport(const tor_addr_t *addr, uint16_t port);
 const char * fmt_addr32(uint32_t addr);
+
 MOCK_DECL(int,get_interface_address6,(int severity, sa_family_t family,
 tor_addr_t *addr));
+void free_interface_address6_list(smartlist_t * addrs);
+MOCK_DECL(smartlist_t *,get_interface_address6_list,(int severity,
+                                                     sa_family_t family,
+                                                     int include_internal));
 
 /** Flag to specify how to do a comparison between addresses.  In an "exact"
  * comparison, addresses are equivalent only if they are in the same family
@@ -281,11 +286,32 @@ int addr_mask_get_bits(uint32_t mask);
 int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len);
 char *tor_dup_ip(uint32_t addr) ATTR_MALLOC;
 MOCK_DECL(int,get_interface_address,(int severity, uint32_t *addr));
+/** Free a smartlist of IP addresses returned by get_interface_address_list.
+ */
+static INLINE void
+free_interface_address_list(smartlist_t *addrs)
+{
+  free_interface_address6_list(addrs);
+}
+/** Return a smartlist of the IPv4 addresses of all interfaces on the server.
+ * Excludes loopback and multicast addresses. Only includes internal addresses
+ * if include_internal is true. (Note that a relay behind NAT may use an
+ * internal address to connect to the Internet.)
+ * An empty smartlist means that there are no IPv4 addresses.
+ * Returns NULL on failure.
+ * Use free_interface_address_list to free the returned list.
+ */
+static INLINE smartlist_t *
+get_interface_address_list(int severity, int include_internal)
+{
+  return get_interface_address6_list(severity, AF_INET, include_internal);
+}
 
 tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port);
 
 #ifdef ADDRESS_PRIVATE
 STATIC smartlist_t *get_interface_addresses_raw(int severity);
+STATIC int tor_addr_is_multicast(const tor_addr_t *a);
 STATIC int get_interface_address6_via_udp_socket_hack(int severity,
                                                       sa_family_t family,
                                                       tor_addr_t *addr);

+ 5 - 3
src/config/torrc.minimal.in-staging

@@ -1,5 +1,5 @@
 ## Configuration file for a typical Tor user
-## Last updated 11 September 2015 for Tor 0.2.7.3-alpha.
+## Last updated 15 September 2015 for Tor 0.2.7.3-alpha.
 ## (may or may not work for much older or much newer versions of Tor.)
 ##
 ## Lines that begin with "## " try to explain what's going on. Lines
@@ -172,8 +172,10 @@
 ## users will be told that those destinations are down.
 ##
 ## For security, by default Tor rejects connections to private (local)
-## networks, including to your public IP address. See the man page entry
-## for ExitPolicyRejectPrivate if you want to allow "exit enclaving".
+## networks, including to the configured public IPv4 and IPv6 addresses,
+## and any public IPv4 and IPv6 addresses on any interface on the relay.
+## See the man page entry for ExitPolicyRejectPrivate if you want to allow
+## "exit enclaving".
 ##
 #ExitPolicy accept *:6660-6667,reject *:* # allow irc ports on IPv4 and IPv6 but no more
 #ExitPolicy accept *:119 # accept nntp ports on IPv4 and IPv6 as well as default exit policy

+ 5 - 3
src/config/torrc.sample.in

@@ -1,5 +1,5 @@
 ## Configuration file for a typical Tor user
-## Last updated 11 September 2015 for Tor 0.2.7.3-alpha.
+## Last updated 15 September 2015 for Tor 0.2.7.3-alpha.
 ## (may or may not work for much older or much newer versions of Tor.)
 ##
 ## Lines that begin with "## " try to explain what's going on. Lines
@@ -172,8 +172,10 @@
 ## users will be told that those destinations are down.
 ##
 ## For security, by default Tor rejects connections to private (local)
-## networks, including to your public IP address. See the man page entry
-## for ExitPolicyRejectPrivate if you want to allow "exit enclaving".
+## networks, including to the configured public IPv4 and IPv6 addresses,
+## and any public IPv4 and IPv6 addresses on any interface on the relay.
+## See the man page entry for ExitPolicyRejectPrivate if you want to allow
+## "exit enclaving".
 ##
 #ExitPolicy accept *:6660-6667,reject *:* # allow irc ports on IPv4 and IPv6 but no more
 #ExitPolicy accept *:119 # accept nntp ports on IPv4 and IPv6 as well as default exit policy

+ 113 - 18
src/or/policies.c

@@ -67,6 +67,8 @@ static int policies_parse_exit_policy_internal(config_line_t *cfg,
                                                int ipv6_exit,
                                                int rejectprivate,
                                                uint32_t local_address,
+                                               tor_addr_t *ipv6_local_address,
+                                               int reject_interface_addresses,
                                                int add_default_policy);
 
 /** Replace all "private" entries in *<b>policy</b> with their expanded
@@ -441,7 +443,7 @@ validate_addr_policies(const or_options_t *options, char **msg)
   smartlist_t *addr_policy=NULL;
   *msg = NULL;
 
-  if (policies_parse_exit_policy_from_options(options,0,&addr_policy)) {
+  if (policies_parse_exit_policy_from_options(options,0,NULL,0,&addr_policy)) {
     REJECT("Error in ExitPolicy entry.");
   }
 
@@ -982,12 +984,24 @@ exit_policy_remove_redundancies(smartlist_t *dest)
   "reject *:563,reject *:1214,reject *:4661-4666,"                  \
   "reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*"
 
-/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>. If
- * cfg doesn't end in an absolute accept or reject and if
+/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>.
+ *
+ * If <b>ipv6_exit</b> is true, prepend "reject *6:*" to the policy.
+ *
+ * If <b>rejectprivate</b> is true:
+ *   - prepend "reject private:*" to the policy.
+ *   - if local_address is non-zero, treat it as a host-order IPv4 address,
+ *     and prepend an entry that rejects it as a destination.
+ *   - if ipv6_local_address is non-NULL, prepend an entry that rejects it as
+ *     a destination.
+ *   - if reject_interface_addresses is true, prepend entries that reject each
+ *     public IPv4 and IPv6 address of each interface on this machine.
+ *
+ * If cfg doesn't end in an absolute accept or reject and if
  * <b>add_default_policy</b> is true, add the default exit
- * 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.
+ * policy afterwards.
+ *
+ * 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,
@@ -998,17 +1012,78 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
                                     int ipv6_exit,
                                     int rejectprivate,
                                     uint32_t local_address,
+                                    tor_addr_t *ipv6_local_address,
+                                    int reject_interface_addresses,
                                     int add_default_policy)
 {
   if (!ipv6_exit) {
     append_exit_policy_string(dest, "reject *6:*");
   }
   if (rejectprivate) {
+    /* Reject IPv4 and IPv6 reserved private netblocks */
     append_exit_policy_string(dest, "reject private:*");
+    /* Reject our local IPv4 address */
     if (local_address) {
       char buf[POLICY_BUF_LEN];
       tor_snprintf(buf, sizeof(buf), "reject %s:*", fmt_addr32(local_address));
       append_exit_policy_string(dest, buf);
+      log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our published "
+               "IPv4 address", buf);
+    }
+    /* Reject our local IPv6 address */
+    if (ipv6_exit && ipv6_local_address != NULL) {
+      if (tor_addr_is_v4(ipv6_local_address)) {
+        log_warn(LD_CONFIG, "IPv4 address '%s' provided as our IPv6 local "
+                 "address", fmt_addr(ipv6_local_address));
+      } else {
+        char buf6[POLICY_BUF_LEN];
+        tor_snprintf(buf6, sizeof(buf6), "reject %s:*",
+                     fmt_addr(ipv6_local_address));
+        append_exit_policy_string(dest, buf6);
+        log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our "
+                 "published IPv6 address", buf6);
+      }
+    }
+    /* Reject local addresses from public netblocks on any interface,
+     * but don't reject our published addresses twice */
+    if (reject_interface_addresses) {
+      smartlist_t *public_addresses = NULL;
+      char bufif[POLICY_BUF_LEN];
+
+      /* Reject public IPv4 addresses on any interface,
+       * but don't reject our published IPv4 address twice */
+      public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0);
+      SMARTLIST_FOREACH_BEGIN(public_addresses, tor_addr_t *, a) {
+        if (!tor_addr_eq_ipv4h(a, local_address)) {
+          tor_snprintf(bufif, sizeof(bufif), "reject %s:*",
+                       fmt_addr(a));
+          append_exit_policy_string(dest, bufif);
+          log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for a local "
+                   "interface's public IPv4 address", bufif);
+        }
+      } SMARTLIST_FOREACH_END(a);
+      free_interface_address6_list(public_addresses);
+
+      if (ipv6_exit) {
+        /* Reject public IPv6 addresses on any interface,
+         * but don't reject our published IPv6 address (if any) twice */
+        public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0);
+        SMARTLIST_FOREACH_BEGIN(public_addresses, tor_addr_t *, a) {
+          /* if we don't have an IPv6 local address, we won't have rejected
+           * it above. This could happen if a future release does IPv6
+           * autodiscovery, and we are waiting to discover our external IPv6
+           * address */
+          if (ipv6_local_address == NULL
+              || !tor_addr_eq(ipv6_local_address, a)) {
+            tor_snprintf(bufif, sizeof(bufif), "reject6 %s:*",
+                         fmt_addr(a));
+            append_exit_policy_string(dest, bufif);
+            log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for a local "
+                     "interface's public IPv6 address", bufif);
+          }
+        } SMARTLIST_FOREACH_END(a);
+        free_interface_address6_list(public_addresses);
+      }
     }
   }
   if (parse_addr_policy(cfg, dest, -1))
@@ -1086,20 +1161,28 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
 
 /** Parse exit policy in <b>cfg</b> into <b>dest</b> smartlist.
  *
- * Add entry that rejects all IPv6 destinations unless
+ * Prepend an entry that rejects all IPv6 destinations unless
  * <b>EXIT_POLICY_IPV6_ENABLED</b> bit is set in <b>options</b> bitmask.
  *
- * If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>,
- * do add entry that rejects all destinations in private subnetwork
- * Tor is running in.
+ * If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>:
+ *   - prepend an entry that rejects all destinations in all netblocks
+ *     reserved for private use.
+ *   - if local_address is non-zero, treat it as a host-order IPv4 address,
+ *     and prepend an entry that rejects it as a destination.
+ *   - if ipv6_local_address is non-NULL, prepend an entry that rejects it as
+ *     a destination.
+ *   - if reject_interface_addresses is true, prepend entries that reject each
+ *     public IPv4 and IPv6 address of each interface on this machine.
  *
- * Respectively, if <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set, add
+ * If <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set in <b>options</b>, append
  * default exit policy entries to <b>result</b> smartlist.
  */
 int
 policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
                            exit_policy_parser_cfg_t options,
-                           uint32_t local_address)
+                           uint32_t local_address,
+                           tor_addr_t *ipv6_local_address,
+                           int reject_interface_addresses)
 {
   int ipv6_enabled = (options & EXIT_POLICY_IPV6_ENABLED) ? 1 : 0;
   int reject_private = (options & EXIT_POLICY_REJECT_PRIVATE) ? 1 : 0;
@@ -1108,19 +1191,27 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
   return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled,
                                              reject_private,
                                              local_address,
+                                             ipv6_local_address,
+                                             reject_interface_addresses,
                                              add_default);
 }
 
 /** Parse <b>ExitPolicy</b> member of <b>or_options</b> into <b>result</b>
  * smartlist.
- * If <b>or_options->IPv6Exit</b> is false, add an entry that
+ * If <b>or_options->IPv6Exit</b> is false, prepend an entry that
  * rejects all IPv6 destinations.
  *
- * If <b>or_options->ExitPolicyRejectPrivate</b> is true, add entry that
- * rejects all destinations in the private subnetwork of machine Tor
- * instance is running in.
+ * If <b>or_options->ExitPolicyRejectPrivate</b> is true:
+ *  - prepend an entry that rejects all destinations in all netblocks reserved
+ *    for private use.
+ *  - if local_address is non-zero, treat it as a host-order IPv4 address, and
+ *    prepend an entry that rejects it as a destination.
+ *  - if ipv6_local_address is non-NULL, prepend an entry that rejects it as a
+ *    destination.
+ *  - if reject_interface_addresses is true, prepend entries that reject each
+ *    public IPv4 and IPv6 address of each interface on this machine.
  *
- * If <b>or_options->BridgeRelay</b> is false, add entries of default
+ * If <b>or_options->BridgeRelay</b> is false, append entries of default
  * Tor exit policy into <b>result</b> smartlist.
  *
  * If or_options->ExitRelay is false, then make our exit policy into
@@ -1129,6 +1220,8 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
 int
 policies_parse_exit_policy_from_options(const or_options_t *or_options,
                                         uint32_t local_address,
+                                        tor_addr_t *ipv6_local_address,
+                                        int reject_interface_addresses,
                                         smartlist_t **result)
 {
   exit_policy_parser_cfg_t parser_cfg = 0;
@@ -1152,7 +1245,9 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options,
   }
 
   return policies_parse_exit_policy(or_options->ExitPolicy,result,
-                                    parser_cfg,local_address);
+                                    parser_cfg,local_address,
+                                    ipv6_local_address,
+                                    reject_interface_addresses);
 }
 
 /** Add "reject *:*" to the end of the policy in *<b>dest</b>, allocating

+ 5 - 7
src/or/policies.h

@@ -48,18 +48,16 @@ MOCK_DECL(addr_policy_result_t, compare_tor_addr_to_addr_policy,
 addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr,
                               uint16_t port, const node_t *node);
 
-/*
-int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
-                               int ipv6exit,
-                               int rejectprivate, uint32_t local_address,
-                               int add_default_policy);
-*/
 int policies_parse_exit_policy_from_options(const or_options_t *or_options,
                                             uint32_t local_address,
+                                            tor_addr_t *ipv6_local_address,
+                                            int reject_interface_addresses,
                                             smartlist_t **result);
 int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
                                exit_policy_parser_cfg_t options,
-                               uint32_t local_address);
+                               uint32_t local_address,
+                               tor_addr_t *ipv6_local_address,
+                               int reject_interface_addresses);
 void policies_exit_policy_append_reject_star(smartlist_t **dest);
 void addr_policy_append_reject_addr(smartlist_t **dest,
                                     const tor_addr_t *addr);

+ 1 - 1
src/or/router.c

@@ -1922,7 +1922,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
     /* DNS is screwed up; don't claim to be an exit. */
     policies_exit_policy_append_reject_star(&ri->exit_policy);
   } else {
-    policies_parse_exit_policy_from_options(options,ri->addr,
+    policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr,1,
                                             &ri->exit_policy);
   }
   ri->policy_is_reject_star =

+ 215 - 15
src/test/test_address.c

@@ -74,35 +74,79 @@ sockaddr_in_from_string(const char *ip_str, struct sockaddr_in *out)
 }
 
 /** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
- * that points to 127.0.0.1. Otherwise, return 0.
+ * that is an IPv4 or IPv6 localhost address. Otherwise, return 0.
  */
 static int
 smartlist_contains_localhost_tor_addr(smartlist_t *smartlist)
 {
-  int found_localhost = 0;
+  SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
+    if (tor_addr_is_loopback(tor_addr)) {
+      return 1;
+    }
+  } SMARTLIST_FOREACH_END(tor_addr);
 
-  struct sockaddr_in *sockaddr_localhost;
-  struct sockaddr_storage *sockaddr_to_check;
+  return 0;
+}
 
-  sockaddr_localhost = sockaddr_in_from_string("127.0.0.1",NULL);
+/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
+ * that is an IPv4 or IPv6 multicast address. Otherwise, return 0.
+ */
+static int
+smartlist_contains_multicast_tor_addr(smartlist_t *smartlist)
+{
+  SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
+    if (tor_addr_is_multicast(tor_addr)) {
+      return 1;
+    }
+  } SMARTLIST_FOREACH_END(tor_addr);
 
-  sockaddr_to_check = tor_malloc(sizeof(struct sockaddr_in));
+  return 0;
+}
 
+/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
+ * that is an IPv4 or IPv6 internal address. Otherwise, return 0.
+ */
+static int
+smartlist_contains_internal_tor_addr(smartlist_t *smartlist)
+{
   SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
-    tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
-                         sizeof(struct sockaddr_in));
+    if (tor_addr_is_internal(tor_addr, 0)) {
+      return 1;
+    }
+  } SMARTLIST_FOREACH_END(tor_addr);
 
-    if (sockaddr_in_are_equal((struct sockaddr_in *)sockaddr_to_check,
-                              sockaddr_localhost)) {
-      found_localhost = 1;
-      break;
+  return 0;
+}
+
+/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
+ * that is an IPv4 address. Otherwise, return 0.
+ */
+static int
+smartlist_contains_ipv4_tor_addr(smartlist_t *smartlist)
+{
+  SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
+    if (tor_addr_is_v4(tor_addr)) {
+      return 1;
     }
   } SMARTLIST_FOREACH_END(tor_addr);
 
-  tor_free(sockaddr_localhost);
-  tor_free(sockaddr_to_check);
+  return 0;
+}
 
-  return found_localhost;
+/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
+ * that is an IPv6 address. Otherwise, return 0.
+ */
+static int
+smartlist_contains_ipv6_tor_addr(smartlist_t *smartlist)
+{
+  SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
+    /* Since there's no tor_addr_is_v6, assume all non-v4s are v6 */
+    if (!tor_addr_is_v4(tor_addr)) {
+      return 1;
+    }
+  } SMARTLIST_FOREACH_END(tor_addr);
+
+  return 0;
 }
 
 #ifdef HAVE_IFADDRS_TO_SMARTLIST
@@ -634,12 +678,168 @@ test_address_udp_socket_trick_blackbox(void *arg)
   return;
 }
 
+static void
+test_address_get_if_addrs_list_internal(void *arg)
+{
+  smartlist_t *results = NULL;
+
+  (void)arg;
+
+  results = get_interface_address_list(LOG_ERR, 1);
+
+  tt_assert(results != NULL);
+  /* Assume every system has at least 1 non-local non-multicast IPv4
+   * interface, even if it is an internal one */
+  tt_int_op(smartlist_len(results),>=,1);
+
+  tt_assert(!smartlist_contains_localhost_tor_addr(results));
+  tt_assert(!smartlist_contains_multicast_tor_addr(results));
+  /* The list may or may not contain internal addresses */
+
+  tt_assert(smartlist_contains_ipv4_tor_addr(results));
+  tt_assert(!smartlist_contains_ipv6_tor_addr(results));
+
+ done:
+  free_interface_address_list(results);
+  return;
+}
+
+static void
+test_address_get_if_addrs_list_no_internal(void *arg)
+{
+  smartlist_t *results = NULL;
+
+  (void)arg;
+
+  results = get_interface_address_list(LOG_ERR, 0);
+
+  tt_assert(results != NULL);
+  /* Work even on systems with only internal IPv4 addresses */
+  tt_int_op(smartlist_len(results),>=,0);
+
+  tt_assert(!smartlist_contains_localhost_tor_addr(results));
+  tt_assert(!smartlist_contains_multicast_tor_addr(results));
+  tt_assert(!smartlist_contains_internal_tor_addr(results));
+
+    /* The list may or may not contain IPv4 addresses */
+  tt_assert(!smartlist_contains_ipv6_tor_addr(results));
+
+ done:
+  free_interface_address_list(results);
+  return;
+}
+
+static void
+test_address_get_if_addrs6_list_internal(void *arg)
+{
+  smartlist_t *results = NULL;
+
+  (void)arg;
+
+  results = get_interface_address6_list(LOG_ERR, AF_INET6, 1);
+
+  tt_assert(results != NULL);
+  /* Work even on systems without IPv6 interfaces */
+  tt_int_op(smartlist_len(results),>=,0);
+
+  tt_assert(!smartlist_contains_localhost_tor_addr(results));
+  tt_assert(!smartlist_contains_multicast_tor_addr(results));
+  /* The list may or may not contain internal addresses */
+
+  tt_assert(!smartlist_contains_ipv4_tor_addr(results));
+  /* The list may or may not contain IPv6 addresses */
+
+ done:
+  free_interface_address6_list(results);
+  return;
+}
+
+static void
+test_address_get_if_addrs6_list_no_internal(void *arg)
+{
+  smartlist_t *results = NULL;
+
+  (void)arg;
+
+  results = get_interface_address6_list(LOG_ERR, AF_INET6, 0);
+
+  tt_assert(results != NULL);
+  /* Work even on systems without IPv6 interfaces */
+  tt_int_op(smartlist_len(results),>=,0);
+
+  tt_assert(!smartlist_contains_localhost_tor_addr(results));
+  tt_assert(!smartlist_contains_multicast_tor_addr(results));
+  tt_assert(!smartlist_contains_internal_tor_addr(results));
+
+  tt_assert(!smartlist_contains_ipv4_tor_addr(results));
+  /* The list may or may not contain IPv6 addresses */
+
+ done:
+  free_interface_address6_list(results);
+  return;
+}
+
+static void
+test_address_get_if_addrs(void *arg)
+{
+  int rv;
+  uint32_t addr_h = 0;
+  tor_addr_t tor_addr;
+
+  (void)arg;
+
+  rv = get_interface_address(LOG_ERR, &addr_h);
+
+  /* Assume every system has at least 1 non-local non-multicast IPv4
+   * interface, even if it is an internal one */
+  tt_assert(rv == 0);
+  tor_addr_from_ipv4h(&tor_addr, addr_h);
+
+  tt_assert(!tor_addr_is_loopback(&tor_addr));
+  tt_assert(!tor_addr_is_multicast(&tor_addr));
+  /* The address may or may not be an internal address */
+
+  tt_assert(tor_addr_is_v4(&tor_addr));
+
+ done:
+  return;
+}
+
+static void
+test_address_get_if_addrs6(void *arg)
+{
+  int rv;
+  tor_addr_t tor_addr;
+
+  (void)arg;
+
+  rv = get_interface_address6(LOG_ERR, AF_INET6, &tor_addr);
+
+  /* Work even on systems without IPv6 interfaces */
+  if (rv == 0) {
+    tt_assert(!tor_addr_is_loopback(&tor_addr));
+    tt_assert(!tor_addr_is_multicast(&tor_addr));
+    /* The address may or may not be an internal address */
+
+    tt_assert(!tor_addr_is_v4(&tor_addr));
+  }
+
+ done:
+  return;
+}
+
 #define ADDRESS_TEST(name, flags) \
   { #name, test_address_ ## name, flags, NULL, NULL }
 
 struct testcase_t address_tests[] = {
   ADDRESS_TEST(udp_socket_trick_whitebox, TT_FORK),
   ADDRESS_TEST(udp_socket_trick_blackbox, TT_FORK),
+  ADDRESS_TEST(get_if_addrs_list_internal, 0),
+  ADDRESS_TEST(get_if_addrs_list_no_internal, 0),
+  ADDRESS_TEST(get_if_addrs6_list_internal, 0),
+  ADDRESS_TEST(get_if_addrs6_list_no_internal, 0),
+  ADDRESS_TEST(get_if_addrs, 0),
+  ADDRESS_TEST(get_if_addrs6, 0),
 #ifdef HAVE_IFADDRS_TO_SMARTLIST
   ADDRESS_TEST(get_if_addrs_ifaddrs, TT_FORK),
   ADDRESS_TEST(ifaddrs_to_smartlist, 0),

+ 22 - 8
src/test/test_policy.c

@@ -49,7 +49,7 @@ test_policy_summary_helper(const char *policy_str,
 
   r = policies_parse_exit_policy(&line, &policy,
                                  EXIT_POLICY_IPV6_ENABLED |
-                                 EXIT_POLICY_ADD_DEFAULT ,0);
+                                 EXIT_POLICY_ADD_DEFAULT, 0, NULL, 0);
   tt_int_op(r,OP_EQ, 0);
 
   summary = policy_summarize(policy, AF_INET);
@@ -78,7 +78,7 @@ test_policies_general(void *arg)
   smartlist_t *policy = NULL, *policy2 = NULL, *policy3 = NULL,
               *policy4 = NULL, *policy5 = NULL, *policy6 = NULL,
               *policy7 = NULL, *policy8 = NULL, *policy9 = NULL,
-              *policy10 = NULL, *policy11 = NULL;
+              *policy10 = NULL, *policy11 = NULL, *policy12 = NULL;
   addr_policy_t *p;
   tor_addr_t tar;
   config_line_t line;
@@ -115,10 +115,20 @@ test_policies_general(void *arg)
   tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy2,
                                               EXIT_POLICY_IPV6_ENABLED |
                                               EXIT_POLICY_REJECT_PRIVATE |
-                                              EXIT_POLICY_ADD_DEFAULT, 0));
+                                              EXIT_POLICY_ADD_DEFAULT, 0,
+                                              NULL, 0));
 
   tt_assert(policy2);
 
+  tor_addr_parse(&tar, "[2000::1234]");
+  tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy12,
+                                                 EXIT_POLICY_IPV6_ENABLED |
+                                                 EXIT_POLICY_REJECT_PRIVATE |
+                                                 EXIT_POLICY_ADD_DEFAULT,
+                                                 0x0306090cu, &tar, 1));
+
+  tt_assert(policy12);
+
   policy3 = smartlist_new();
   p = router_parse_addr_policy_item_from_string("reject *:*", -1,
                                                 &malformed_list);
@@ -196,13 +206,15 @@ test_policies_general(void *arg)
   tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy8,
                                                  EXIT_POLICY_IPV6_ENABLED |
                                                  EXIT_POLICY_REJECT_PRIVATE |
-                                                 EXIT_POLICY_ADD_DEFAULT, 0));
+                                                 EXIT_POLICY_ADD_DEFAULT, 0,
+                                                 NULL, 0));
 
   tt_assert(policy8);
 
   tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy9,
                                                  EXIT_POLICY_REJECT_PRIVATE |
-                                                 EXIT_POLICY_ADD_DEFAULT, 0));
+                                                 EXIT_POLICY_ADD_DEFAULT, 0,
+                                                 NULL, 0));
 
   tt_assert(policy9);
 
@@ -255,13 +267,14 @@ test_policies_general(void *arg)
   line.value = (char*)"accept *:80,reject private:*,reject *:*";
   line.next = NULL;
   tt_int_op(0, OP_EQ, policies_parse_exit_policy(&line,&policy,
-                                              ~EXIT_POLICY_IPV6_ENABLED |
-                                              EXIT_POLICY_ADD_DEFAULT,0));
+                                              EXIT_POLICY_IPV6_ENABLED |
+                                              EXIT_POLICY_ADD_DEFAULT, 0,
+                                              NULL, 0));
   tt_assert(policy);
 
   //test_streq(policy->string, "accept *:80");
   //test_streq(policy->next->string, "reject *:*");
-  tt_int_op(smartlist_len(policy),OP_EQ, 9);
+  tt_int_op(smartlist_len(policy),OP_EQ, 4);
 
   /* test policy summaries */
   /* check if we properly ignore private IP addresses */
@@ -467,6 +480,7 @@ test_policies_general(void *arg)
   addr_policy_list_free(policy9);
   addr_policy_list_free(policy10);
   addr_policy_list_free(policy11);
+  addr_policy_list_free(policy12);
   tor_free(policy_str);
   if (sm) {
     SMARTLIST_FOREACH(sm, char *, s, tor_free(s));