Browse Source

r11723@Kushana: nickm | 2006-12-28 13:52:48 -0500
Fix bug 364: check for whether popular hostnames (curently google, yahoo, mit, and slashdot) are getting wildcarded. If they are, we are probably behind a DNS server that is useless: change our exit policy to reject *:*.


svn:r9199

Nick Mathewson 18 years ago
parent
commit
e5f5b96ca6
8 changed files with 151 additions and 28 deletions
  1. 3 0
      ChangeLog
  2. 2 2
      doc/TODO
  3. 8 0
      doc/tor.1.in
  4. 2 0
      src/or/config.c
  5. 117 18
      src/or/dns.c
  6. 6 6
      src/or/main.c
  7. 5 1
      src/or/or.h
  8. 8 1
      src/or/router.c

+ 3 - 0
ChangeLog

@@ -67,6 +67,9 @@ Changes in version 0.1.2.5-xxxx - 200?-??-??
       never believe reported remote addresses when they're internal.
       never believe reported remote addresses when they're internal.
     - Add client-side caching for reverse DNS lookups.
     - Add client-side caching for reverse DNS lookups.
     - Add support to tor-resolve for reverse lookups and SOCKS5.
     - Add support to tor-resolve for reverse lookups and SOCKS5.
+    - We now check for the case when common DNS requests are going to
+      wildcarded addresses, and change our exit policy to reject *:* if
+      it's happening.  (Bug #364)
 
 
   o Security bugfixes:
   o Security bugfixes:
     - Stop sending the HttpProxyAuthenticator string to directory
     - Stop sending the HttpProxyAuthenticator string to directory

+ 2 - 2
doc/TODO

@@ -107,8 +107,8 @@ d   - Be a DNS proxy.
       o address_is_invalid_destination() is the right thing to call here
       o address_is_invalid_destination() is the right thing to call here
         (and feel free to make that function smarter)
         (and feel free to make that function smarter)
       o add a config option to turn it off.
       o add a config option to turn it off.
-      - and a man page for that option
-    - Bug 364: notice when all the DNS requests we get back (including a few
+      o and a man page for that option
+    o Bug 364: notice when all the DNS requests we get back (including a few
       well-known sites) are all going to the same place.
       well-known sites) are all going to the same place.
     o Bug 363: Warn and die if we can't find a nameserver and we're running a
     o Bug 363: Warn and die if we can't find a nameserver and we're running a
       server; don't fall back to 127.0.0.1.
       server; don't fall back to 127.0.0.1.

+ 8 - 0
doc/tor.1.in

@@ -704,6 +704,14 @@ our local nameservers have been configured to hijack failing DNS requests
 this.  This option only affects name lookup for addresses requested by
 this.  This option only affects name lookup for addresses requested by
 clients; and only takes effect if Tor was built with eventdns support.
 clients; and only takes effect if Tor was built with eventdns support.
 (Defaults to "1".)
 (Defaults to "1".)
+.LP
+.TP
+\fBServerDNSTestAddresses \fR\fIaddress\fR,\fIaddress\fR,\fI...\fP
+When we're detecting DNS hijacking, make sure that these \fIvalid\fP
+addresses aren't getting redirected.  If they are, then our DNS is
+completely useless, and we'll reset our exit policy to "reject *:*".
+(Defaults to "www.google.com, www.mit.edu, www.yahoo.com,
+www.slashdot.org".)
 
 
 .SH DIRECTORY SERVER OPTIONS
 .SH DIRECTORY SERVER OPTIONS
 .PP
 .PP

+ 2 - 0
src/or/config.c

@@ -232,6 +232,8 @@ static config_var_t _option_vars[] = {
   VAR("ServerDNSDetectHijacking",BOOL,   ServerDNSDetectHijacking,"1"),
   VAR("ServerDNSDetectHijacking",BOOL,   ServerDNSDetectHijacking,"1"),
   VAR("ServerDNSResolvConfFile", STRING, ServerDNSResolvConfFile, NULL),
   VAR("ServerDNSResolvConfFile", STRING, ServerDNSResolvConfFile, NULL),
   VAR("ServerDNSSearchDomains",  BOOL,   ServerDNSSearchDomains,  "0"),
   VAR("ServerDNSSearchDomains",  BOOL,   ServerDNSSearchDomains,  "0"),
+  VAR("ServerDNSTestAddresses",  CSV,    ServerDNSTestAddresses,
+      "www.google.com,www.mit.edu,www.yahoo.com,www.slashdot.org"),
   VAR("ShutdownWaitLength",  INTERVAL, ShutdownWaitLength,   "30 seconds"),
   VAR("ShutdownWaitLength",  INTERVAL, ShutdownWaitLength,   "30 seconds"),
   VAR("SocksListenAddress",  LINELIST, SocksListenAddress,   NULL),
   VAR("SocksListenAddress",  LINELIST, SocksListenAddress,   NULL),
   VAR("SocksPolicy",         LINELIST, SocksPolicy,          NULL),
   VAR("SocksPolicy",         LINELIST, SocksPolicy,          NULL),

+ 117 - 18
src/or/dns.c

@@ -122,6 +122,7 @@ static void dnsworker_main(void *data);
 static int spawn_dnsworker(void);
 static int spawn_dnsworker(void);
 static int spawn_enough_dnsworkers(void);
 static int spawn_enough_dnsworkers(void);
 #else
 #else
+static void add_wildcarded_test_address(const char *address);
 static int configure_nameservers(int force);
 static int configure_nameservers(int force);
 static int answer_is_wildcarded(const char *ip);
 static int answer_is_wildcarded(const char *ip);
 #endif
 #endif
@@ -902,8 +903,12 @@ dns_found_answer(const char *address, int is_reverse, uint32_t addr,
 
 
   resolve = HT_FIND(cache_map, &cache_root, &search);
   resolve = HT_FIND(cache_map, &cache_root, &search);
   if (!resolve) {
   if (!resolve) {
-    log_info(LD_EXIT,"Resolved unasked address %s; caching anyway.",
-             escaped_safe_str(address));
+    or_options_t *options = get_options();
+    int is_test_address = options->ServerDNSTestAddresses &&
+      smartlist_string_isin_case(options->ServerDNSTestAddresses, address);
+    if (!is_test_address)
+      log_info(LD_EXIT,"Resolved unasked address %s; caching anyway.",
+               escaped_safe_str(address));
     add_answer_to_cache(address, is_reverse, addr, hostname, outcome, ttl);
     add_answer_to_cache(address, is_reverse, addr, hostname, outcome, ttl);
     return;
     return;
   }
   }
@@ -1385,9 +1390,15 @@ spawn_enough_dnsworkers(void)
 }
 }
 
 
 void
 void
-dns_launch_wildcard_checks(void)
+dns_launch_correctness_checks(void)
 {
 {
 }
 }
+
+int
+dns_seems_to_be_broken(void)
+{
+  return 0;
+}
 #else /* !USE_EVENTDNS */
 #else /* !USE_EVENTDNS */
 
 
 /** Eventdns helper: return true iff the eventdns result <b>err</b> is
 /** Eventdns helper: return true iff the eventdns result <b>err</b> is
@@ -1512,13 +1523,14 @@ configure_nameservers(int force)
  */
  */
 static void
 static void
 evdns_callback(int result, char type, int count, int ttl, void *addresses,
 evdns_callback(int result, char type, int count, int ttl, void *addresses,
-                  void *arg)
+               void *arg)
 {
 {
   char *string_address = arg;
   char *string_address = arg;
   int is_reverse = 0;
   int is_reverse = 0;
   int status = DNS_RESOLVE_FAILED_PERMANENT;
   int status = DNS_RESOLVE_FAILED_PERMANENT;
   uint32_t addr = 0;
   uint32_t addr = 0;
   const char *hostname = NULL;
   const char *hostname = NULL;
+  int was_wildcarded = 0;
 
 
   if (result == DNS_ERR_NONE) {
   if (result == DNS_ERR_NONE) {
     if (type == DNS_IPv4_A && count) {
     if (type == DNS_IPv4_A && count) {
@@ -1537,6 +1549,7 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
                   "address %s; treating as a failure.",
                   "address %s; treating as a failure.",
                   safe_str(escaped_address),
                   safe_str(escaped_address),
                   escaped_safe_str(answer_buf));
                   escaped_safe_str(answer_buf));
+        was_wildcarded = 1;
         addr = 0;
         addr = 0;
         status = DNS_RESOLVE_FAILED_PERMANENT;
         status = DNS_RESOLVE_FAILED_PERMANENT;
       } else {
       } else {
@@ -1566,6 +1579,17 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
     if (evdns_err_is_transient(result))
     if (evdns_err_is_transient(result))
       status = DNS_RESOLVE_FAILED_TRANSIENT;
       status = DNS_RESOLVE_FAILED_TRANSIENT;
   }
   }
+  if (was_wildcarded) {
+    or_options_t *options = get_options();
+    int is_test_address = options->ServerDNSTestAddresses &&
+      smartlist_string_isin_case(options->ServerDNSTestAddresses, hostname);
+
+    if (is_test_address) {
+      /* Ick.  We're getting redirected on known-good addresses.  Our DNS
+       * server must really hate us.  */
+      add_wildcarded_test_address(hostname);
+    }
+  }
   if (result != DNS_ERR_SHUTDOWN)
   if (result != DNS_ERR_SHUTDOWN)
     dns_found_answer(string_address, is_reverse, addr, hostname, status, ttl);
     dns_found_answer(string_address, is_reverse, addr, hostname, status, ttl);
   tor_free(string_address);
   tor_free(string_address);
@@ -1634,6 +1658,13 @@ static strmap_t *dns_wildcard_response_count = NULL;
  * nameserver wants to return in response to requests for nonexistent domains.
  * nameserver wants to return in response to requests for nonexistent domains.
  */
  */
 static smartlist_t *dns_wildcard_list = NULL;
 static smartlist_t *dns_wildcard_list = NULL;
+static int dns_wildcard_one_notice_given = 0;
+static int dns_wildcard_notice_given = 0;
+
+/** DOCDOC */
+static smartlist_t *dns_wildcarded_test_address_list = NULL;
+static int dns_wildcarded_test_address_notice_given = 0;
+static int dns_is_completely_invalid = 0;
 
 
 /** Called when we see <b>id</b> (a dotted quad) in response to a request for
 /** Called when we see <b>id</b> (a dotted quad) in response to a request for
  * a hopefully bogus address. */
  * a hopefully bogus address. */
@@ -1641,7 +1672,6 @@ static void
 wildcard_increment_answer(const char *id)
 wildcard_increment_answer(const char *id)
 {
 {
   int *ip;
   int *ip;
-  static int notice_given = 0;
   if (!dns_wildcard_response_count)
   if (!dns_wildcard_response_count)
     dns_wildcard_response_count = strmap_new();
     dns_wildcard_response_count = strmap_new();
 
 
@@ -1655,14 +1685,40 @@ wildcard_increment_answer(const char *id)
   if (*ip > 5 && n_wildcard_requests > 10) {
   if (*ip > 5 && n_wildcard_requests > 10) {
     if (!dns_wildcard_list) dns_wildcard_list = smartlist_create();
     if (!dns_wildcard_list) dns_wildcard_list = smartlist_create();
     if (!smartlist_string_isin(dns_wildcard_list, id)) {
     if (!smartlist_string_isin(dns_wildcard_list, id)) {
-    log(notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
+    log(dns_wildcard_notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
         "Your DNS provider has given \"%s\" as an answer for %d different "
         "Your DNS provider has given \"%s\" as an answer for %d different "
         "invalid addresses. Apparently they are hijacking DNS failures. "
         "invalid addresses. Apparently they are hijacking DNS failures. "
         "I'll try to correct for this by treating future occurrences of "
         "I'll try to correct for this by treating future occurrences of "
         "\"%s\" as 'not found'.", id, *ip, id);
         "\"%s\" as 'not found'.", id, *ip, id);
       smartlist_add(dns_wildcard_list, tor_strdup(id));
       smartlist_add(dns_wildcard_list, tor_strdup(id));
     }
     }
-    notice_given = 1;
+    dns_wildcard_notice_given = 1;
+  }
+}
+
+static void
+add_wildcarded_test_address(const char *address)
+{
+  int n;
+  if (!dns_wildcarded_test_address_list)
+    dns_wildcarded_test_address_list = smartlist_create();
+
+  if (smartlist_string_isin_case(dns_wildcarded_test_address_list, address))
+    return;
+
+  smartlist_add(dns_wildcarded_test_address_list, tor_strdup(address));
+  n = smartlist_len(dns_wildcarded_test_address_list);
+  if (n > smartlist_len(get_options()->ServerDNSTestAddresses)/2) {
+    log(dns_wildcarded_test_address_notice_given ? LOG_INFO : LOG_NOTICE,
+        LD_EXIT, "Your DNS provider tried to redirect \"%s\" to a junk "
+        "address.  It has done this with %d test addresses so far.  I'm "
+        "going to stop being an exit node for now, since our DNS seems so "
+        "broken.", address, n);
+    if (!dns_is_completely_invalid) {
+      dns_is_completely_invalid = 1;
+      mark_my_descriptor_dirty();
+    }
+    dns_wildcarded_test_address_notice_given = 1;
   }
   }
 }
 }
 
 
@@ -1670,9 +1726,8 @@ wildcard_increment_answer(const char *id)
  * for a (hopefully) nonexistent domain. */
  * for a (hopefully) nonexistent domain. */
 static void
 static void
 evdns_wildcard_check_callback(int result, char type, int count, int ttl,
 evdns_wildcard_check_callback(int result, char type, int count, int ttl,
-                                 void *addresses, void *arg)
+                              void *addresses, void *arg)
 {
 {
-  static int notice_given = 0;
   (void)ttl;
   (void)ttl;
   ++n_wildcard_requests;
   ++n_wildcard_requests;
   if (result == DNS_ERR_NONE && type == DNS_IPv4_A && count) {
   if (result == DNS_ERR_NONE && type == DNS_IPv4_A && count) {
@@ -1686,13 +1741,13 @@ evdns_wildcard_check_callback(int result, char type, int count, int ttl,
       tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
       tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
       wildcard_increment_answer(answer_buf);
       wildcard_increment_answer(answer_buf);
     }
     }
-    log(notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
+    log(dns_wildcard_one_notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
         "Your DNS provider gave an answer for \"%s\", which "
         "Your DNS provider gave an answer for \"%s\", which "
         "is not supposed to exist.  Apparently they are hijacking "
         "is not supposed to exist.  Apparently they are hijacking "
         "DNS failures. Trying to correct for this.  We've noticed %d possibly "
         "DNS failures. Trying to correct for this.  We've noticed %d possibly "
         "bad addresses so far.",
         "bad addresses so far.",
         string_address, strmap_size(dns_wildcard_response_count));
         string_address, strmap_size(dns_wildcard_response_count));
-    notice_given = 1;
+    dns_wildcard_one_notice_given = 1;
   }
   }
   tor_free(arg);
   tor_free(arg);
 }
 }
@@ -1721,18 +1776,38 @@ launch_wildcard_check(int min_len, int max_len, const char *suffix)
     tor_free(addr);
     tor_free(addr);
 }
 }
 
 
+static void
+launch_test_addresses(int fd, short event, void *args)
+{
+  or_options_t *options = get_options();
+  (void)fd;
+  (void)event;
+  (void)args;
+
+  log_info(LD_EXIT, "Launching checks to see whether our nameservers like to "
+           "hijack *everything*.");
+  /* This situation is worse than the failure-hijacking situation.  When this
+   * happens, we're no good for DNS requests at all, and we shouldn't really
+   * be an exit server.*/
+  if (!options->ServerDNSTestAddresses)
+    return;
+  SMARTLIST_FOREACH(options->ServerDNSTestAddresses, const char *, address,
+    {
+      evdns_resolve_ipv4(address, DNS_QUERY_NO_SEARCH, evdns_callback,
+                         tor_strdup(address));
+    });
+}
+
 #define N_WILDCARD_CHECKS 2
 #define N_WILDCARD_CHECKS 2
 
 
-/** Launch DNS requests for a few nonexistent hostnames, and see if we can
- * catch our nameserver trying to hijack them and map them to a stupid "I
- * couldn't find ggoogle.com but maybe you'd like to buy these lovely
- * encyclopedias" page. */
-void
+/** Launch DNS requests for a few nonexistent hostnames and a few well-known
+ * hostnames, and see if we can catch our nameserver trying to hijack them and
+ * map them to a stupid "I couldn't find ggoogle.com but maybe you'd like to
+ * buy these lovely encyclopedias" page. */
+static void
 dns_launch_wildcard_checks(void)
 dns_launch_wildcard_checks(void)
 {
 {
   int i;
   int i;
-  if (!get_options()->ServerDNSDetectHijacking)
-    return;
   log_info(LD_EXIT, "Launching checks to see whether our nameservers like "
   log_info(LD_EXIT, "Launching checks to see whether our nameservers like "
            "to hijack DNS failures.");
            "to hijack DNS failures.");
   for (i = 0; i < N_WILDCARD_CHECKS; ++i) {
   for (i = 0; i < N_WILDCARD_CHECKS; ++i) {
@@ -1756,6 +1831,30 @@ dns_launch_wildcard_checks(void)
   }
   }
 }
 }
 
 
+/* DOCDOC */
+void
+dns_launch_correctness_checks(void)
+{
+  static struct event launch_event;
+  struct timeval timeout;
+  if (!get_options()->ServerDNSDetectHijacking)
+    return;
+  dns_launch_wildcard_checks();
+
+  /* Wait a while before launching requests for test addresses, so we can
+   * get the results from checking for wildcarding. */
+  evtimer_set(&launch_event, launch_test_addresses, NULL);
+  timeout.tv_sec = 30;
+  timeout.tv_usec = 0;
+  evtimer_add(&launch_event, &timeout);
+}
+
+int
+dns_seems_to_be_broken(void)
+{
+  return dns_is_completely_invalid;
+}
+
 /** Return true iff we have noticed that the dotted-quad <b>ip</b> has been
 /** Return true iff we have noticed that the dotted-quad <b>ip</b> has been
  * returned in response to requests for nonexistent hostnames. */
  * returned in response to requests for nonexistent hostnames. */
 static int
 static int

+ 6 - 6
src/or/main.c

@@ -729,7 +729,7 @@ run_scheduled_events(time_t now)
   static time_t time_to_try_getting_descriptors = 0;
   static time_t time_to_try_getting_descriptors = 0;
   static time_t time_to_reset_descriptor_failures = 0;
   static time_t time_to_reset_descriptor_failures = 0;
   static time_t time_to_add_entropy = 0;
   static time_t time_to_add_entropy = 0;
-  static time_t time_to_check_for_wildcarded_dns = 0;
+  static time_t time_to_check_for_correct_dns = 0;
   or_options_t *options = get_options();
   or_options_t *options = get_options();
   int i;
   int i;
   int have_dir_info;
   int have_dir_info;
@@ -937,12 +937,12 @@ run_scheduled_events(time_t now)
 
 
   /** 9. and if we're a server, check whether our DNS is telling stories to
   /** 9. and if we're a server, check whether our DNS is telling stories to
    * us. */
    * us. */
-  if (server_mode(options) && time_to_check_for_wildcarded_dns < now) {
-    if (!time_to_check_for_wildcarded_dns) {
-      time_to_check_for_wildcarded_dns = now + 60 + crypto_rand_int(120);
+  if (server_mode(options) && time_to_check_for_correct_dns < now) {
+    if (!time_to_check_for_correct_dns) {
+      time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120);
     } else {
     } else {
-      dns_launch_wildcard_checks();
-      time_to_check_for_wildcarded_dns = now + 12*3600 +
+      dns_launch_correctness_checks();
+      time_to_check_for_correct_dns = now + 12*3600 +
         crypto_rand_int(12*3600);
         crypto_rand_int(12*3600);
     }
     }
   }
   }

+ 5 - 1
src/or/or.h

@@ -1656,6 +1656,9 @@ typedef struct {
   char *ServerDNSResolvConfFile; /**< If provided, we configure our internal
   char *ServerDNSResolvConfFile; /**< If provided, we configure our internal
                      * resolver from the file here rather than from
                      * resolver from the file here rather than from
                      * /etc/resolv.conf (Unix) or the registry (Windows). */
                      * /etc/resolv.conf (Unix) or the registry (Windows). */
+  smartlist_t *ServerDNSTestAddresses; /**< A list of addresses that definitely
+                                        * should be resolveable. Used for
+                                        * testing our DNS server. */
   int EnforceDistinctSubnets; /**< If true, don't allow multiple routers in the
   int EnforceDistinctSubnets; /**< If true, don't allow multiple routers in the
                                * same network zone in the same circuit. */
                                * same network zone in the same circuit. */
   int TunnelDirConns; /**< If true, use BEGIN_DIR rather than BEGIN when
   int TunnelDirConns; /**< If true, use BEGIN_DIR rather than BEGIN when
@@ -2318,7 +2321,8 @@ void assert_connection_edge_not_dns_pending(edge_connection_t *conn);
 void assert_all_pending_dns_resolves_ok(void);
 void assert_all_pending_dns_resolves_ok(void);
 void dns_cancel_pending_resolve(const char *question);
 void dns_cancel_pending_resolve(const char *question);
 int dns_resolve(edge_connection_t *exitconn, or_circuit_t *circ);
 int dns_resolve(edge_connection_t *exitconn, or_circuit_t *circ);
-void dns_launch_wildcard_checks(void);
+void dns_launch_correctness_checks(void);
+int dns_seems_to_be_broken(void);
 
 
 /********************************* hibernate.c **********************/
 /********************************* hibernate.c **********************/
 
 

+ 8 - 1
src/or/router.c

@@ -1204,7 +1204,14 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
   }
   }
 
 
   /* Write the exit policy to the end of 's'. */
   /* Write the exit policy to the end of 's'. */
-  for (tmpe=router->exit_policy; tmpe; tmpe=tmpe->next) {
+  tmpe = router->exit_policy;
+  if (dns_seems_to_be_broken()) {
+    /* DNS is screwed up; don't claim to be an exit. */
+    strlcat(s+written, "reject *:*\n", maxlen-written);
+    written += strlen("reject *:*\n");
+    tmpe = NULL;
+  }
+  for ( ; tmpe; tmpe=tmpe->next) {
     /* Write: "accept 1.2.3.4" */
     /* Write: "accept 1.2.3.4" */
     in.s_addr = htonl(tmpe->addr);
     in.s_addr = htonl(tmpe->addr);
     tor_inet_ntoa(&in, addrbuf, sizeof(addrbuf));
     tor_inet_ntoa(&in, addrbuf, sizeof(addrbuf));