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
This commit is contained in:
Nick Mathewson 2006-12-28 21:29:11 +00:00
parent 4cd302a1eb
commit e5f5b96ca6
8 changed files with 151 additions and 28 deletions

View File

@ -67,6 +67,9 @@ Changes in version 0.1.2.5-xxxx - 200?-??-??
never believe reported remote addresses when they're internal.
- Add client-side caching for reverse DNS lookups.
- 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:
- Stop sending the HttpProxyAuthenticator string to directory

View File

@ -107,8 +107,8 @@ d - Be a DNS proxy.
o address_is_invalid_destination() is the right thing to call here
(and feel free to make that function smarter)
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.
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.

View File

@ -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
clients; and only takes effect if Tor was built with eventdns support.
(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
.PP

View File

@ -232,6 +232,8 @@ static config_var_t _option_vars[] = {
VAR("ServerDNSDetectHijacking",BOOL, ServerDNSDetectHijacking,"1"),
VAR("ServerDNSResolvConfFile", STRING, ServerDNSResolvConfFile, NULL),
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("SocksListenAddress", LINELIST, SocksListenAddress, NULL),
VAR("SocksPolicy", LINELIST, SocksPolicy, NULL),

View File

@ -122,6 +122,7 @@ static void dnsworker_main(void *data);
static int spawn_dnsworker(void);
static int spawn_enough_dnsworkers(void);
#else
static void add_wildcarded_test_address(const char *address);
static int configure_nameservers(int force);
static int answer_is_wildcarded(const char *ip);
#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);
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);
return;
}
@ -1385,9 +1390,15 @@ spawn_enough_dnsworkers(void)
}
void
dns_launch_wildcard_checks(void)
dns_launch_correctness_checks(void)
{
}
int
dns_seems_to_be_broken(void)
{
return 0;
}
#else /* !USE_EVENTDNS */
/** Eventdns helper: return true iff the eventdns result <b>err</b> is
@ -1512,13 +1523,14 @@ configure_nameservers(int force)
*/
static void
evdns_callback(int result, char type, int count, int ttl, void *addresses,
void *arg)
void *arg)
{
char *string_address = arg;
int is_reverse = 0;
int status = DNS_RESOLVE_FAILED_PERMANENT;
uint32_t addr = 0;
const char *hostname = NULL;
int was_wildcarded = 0;
if (result == DNS_ERR_NONE) {
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.",
safe_str(escaped_address),
escaped_safe_str(answer_buf));
was_wildcarded = 1;
addr = 0;
status = DNS_RESOLVE_FAILED_PERMANENT;
} else {
@ -1566,6 +1579,17 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
if (evdns_err_is_transient(result))
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)
dns_found_answer(string_address, is_reverse, addr, hostname, status, ttl);
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.
*/
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
* a hopefully bogus address. */
@ -1641,7 +1672,6 @@ static void
wildcard_increment_answer(const char *id)
{
int *ip;
static int notice_given = 0;
if (!dns_wildcard_response_count)
dns_wildcard_response_count = strmap_new();
@ -1655,14 +1685,40 @@ wildcard_increment_answer(const char *id)
if (*ip > 5 && n_wildcard_requests > 10) {
if (!dns_wildcard_list) dns_wildcard_list = smartlist_create();
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 "
"invalid addresses. Apparently they are hijacking DNS failures. "
"I'll try to correct for this by treating future occurrences of "
"\"%s\" as 'not found'.", id, *ip, 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. */
static void
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;
++n_wildcard_requests;
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));
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 "
"is not supposed to exist. Apparently they are hijacking "
"DNS failures. Trying to correct for this. We've noticed %d possibly "
"bad addresses so far.",
string_address, strmap_size(dns_wildcard_response_count));
notice_given = 1;
dns_wildcard_one_notice_given = 1;
}
tor_free(arg);
}
@ -1721,18 +1776,38 @@ launch_wildcard_check(int min_len, int max_len, const char *suffix)
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
/** 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)
{
int i;
if (!get_options()->ServerDNSDetectHijacking)
return;
log_info(LD_EXIT, "Launching checks to see whether our nameservers like "
"to hijack DNS failures.");
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
* returned in response to requests for nonexistent hostnames. */
static int

View File

@ -729,7 +729,7 @@ run_scheduled_events(time_t now)
static time_t time_to_try_getting_descriptors = 0;
static time_t time_to_reset_descriptor_failures = 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();
int i;
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
* 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 {
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);
}
}

View File

@ -1656,6 +1656,9 @@ typedef struct {
char *ServerDNSResolvConfFile; /**< If provided, we configure our internal
* resolver from the file here rather than from
* /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
* same network zone in the same circuit. */
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 dns_cancel_pending_resolve(const char *question);
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 **********************/

View File

@ -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'. */
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" */
in.s_addr = htonl(tmpe->addr);
tor_inet_ntoa(&in, addrbuf, sizeof(addrbuf));