[or-cvs] r17636: {tor} Partial backport of DNS address/name checking (r16621), and (in tor/branches/tor-0_2_0-patches: . doc src/or)

nickm at seul.org nickm at seul.org
Wed Dec 17 12:51:37 UTC 2008


Author: nickm
Date: 2008-12-17 07:51:36 -0500 (Wed, 17 Dec 2008)
New Revision: 17636

Modified:
   tor/branches/tor-0_2_0-patches/ChangeLog
   tor/branches/tor-0_2_0-patches/doc/TODO.020
   tor/branches/tor-0_2_0-patches/src/or/config.c
   tor/branches/tor-0_2_0-patches/src/or/dns.c
   tor/branches/tor-0_2_0-patches/src/or/eventdns.c
   tor/branches/tor-0_2_0-patches/src/or/or.h
Log:
Partial backport of DNS address/name checking (r16621), and backport of 0x20 hack (r17171).

Modified: tor/branches/tor-0_2_0-patches/ChangeLog
===================================================================
--- tor/branches/tor-0_2_0-patches/ChangeLog	2008-12-16 22:53:24 UTC (rev 17635)
+++ tor/branches/tor-0_2_0-patches/ChangeLog	2008-12-17 12:51:36 UTC (rev 17636)
@@ -32,7 +32,6 @@
     - Use 64 bits instead of 32 bits for connection identifiers used with
       the controller protocol, to greatly reduce risk of identifier reuse.
 
-
   o Minor features:
     - Report the case where all signatures in a detached set are rejected
       differently than the case where there is an error handling the detached
@@ -40,6 +39,15 @@
     - When we realize that another process has modified our cached
       descriptors, print out a more useful error message rather than
       triggering an assertion. Fixes bug 885.  Patch from Karsten.
+    - Implement the 0x20 hack to better resist DNS poisoning: set the
+      case on outgoing DNS requests randomly, and reject responses that do
+      not match the case correctly. This logic can be disabled with the
+      ServerDNSRamdomizeCase setting, if you are using one of the 0.3%
+      of servers that do not reliably preserve case in replies. See
+      "Increased DNS Forgery Resistance through 0x20-Bit Encoding"
+      for more info.
+    - Check DNS replies for more matching fields to better resist DNS
+      poisoning.
 
 
 Changes in version 0.2.0.32 - 2008-11-20

Modified: tor/branches/tor-0_2_0-patches/doc/TODO.020
===================================================================
--- tor/branches/tor-0_2_0-patches/doc/TODO.020	2008-12-16 22:53:24 UTC (rev 17635)
+++ tor/branches/tor-0_2_0-patches/doc/TODO.020	2008-12-17 12:51:36 UTC (rev 17636)
@@ -8,8 +8,10 @@
 Backport for 0.2.0 once better tested:
   o r16136: prevent circid collision.  [Also backport to 0.1.2.x??]
   o r16558: Avoid mis-routing CREATED cells.
-  - r16621: Make some DNS code more robust (partial; see also libevent
+  Xo r16621: Make some DNS code more robust (partial; see also libevent
             approach). (Also maybe r16674)
+    [Partially backported.  Instead of the basic name checking, I backported
+     r17171 instead, to be even more resistant to poisoning.]
   - r17091: distinguish "no routers support pending circuits" from
             "no circuits are pending." See also r17181 and r17184.
   - r17137: send END cell in response to connect to nonexistent hidserv port.

Modified: tor/branches/tor-0_2_0-patches/src/or/config.c
===================================================================
--- tor/branches/tor-0_2_0-patches/src/or/config.c	2008-12-16 22:53:24 UTC (rev 17635)
+++ tor/branches/tor-0_2_0-patches/src/or/config.c	2008-12-17 12:51:36 UTC (rev 17636)
@@ -274,6 +274,7 @@
   V(ServerDNSAllowBrokenResolvConf, BOOL,  "1"),
   V(ServerDNSAllowNonRFC953Hostnames, BOOL,"0"),
   V(ServerDNSDetectHijacking,    BOOL,     "1"),
+  V(ServerDNSRandomizeCase,      BOOL,     "1"),
   V(ServerDNSResolvConfFile,     STRING,   NULL),
   V(ServerDNSSearchDomains,      BOOL,     "0"),
   V(ServerDNSTestAddresses,      CSV,

Modified: tor/branches/tor-0_2_0-patches/src/or/dns.c
===================================================================
--- tor/branches/tor-0_2_0-patches/src/or/dns.c	2008-12-16 22:53:24 UTC (rev 17635)
+++ tor/branches/tor-0_2_0-patches/src/or/dns.c	2008-12-17 12:51:36 UTC (rev 17636)
@@ -198,6 +198,10 @@
 {
   init_cache_map();
   evdns_set_transaction_id_fn(dns_get_transaction_id);
+  if (get_options()->ServerDNSRandomizeCase)
+    evdns_set_option("randomize-case", "1", DNS_OPTIONS_ALL);
+  else
+    evdns_set_option("randomize-case", "0", DNS_OPTIONS_ALL);
   if (server_mode(get_options()))
     return configure_nameservers(1);
   return 0;

Modified: tor/branches/tor-0_2_0-patches/src/or/eventdns.c
===================================================================
--- tor/branches/tor-0_2_0-patches/src/or/eventdns.c	2008-12-16 22:53:24 UTC (rev 17635)
+++ tor/branches/tor-0_2_0-patches/src/or/eventdns.c	2008-12-17 12:51:36 UTC (rev 17636)
@@ -307,6 +307,9 @@
 /* number of timeouts in a row before we consider this server to be down */
 static int global_max_nameserver_timeout = 3;
 
+/* DOCDOC */
+static int global_randomize_case = 1;
+
 /* These are the timeout values for nameservers. If we find a nameserver is down */
 /* we try to probe it at intervals as given below. Values are in seconds. */
 static const struct timeval global_nameserver_timeouts[] = {{10, 0}, {60, 0}, {300, 0}, {900, 0}, {3600, 0}};
@@ -377,6 +380,9 @@
 
 #define ISSPACE(c) isspace((int)(unsigned char)(c))
 #define ISDIGIT(c) isdigit((int)(unsigned char)(c))
+#define ISALPHA(c) isalpha((int)(unsigned char)(c))
+#define TOLOWER(c) (char)tolower((int)(unsigned char)(c))
+#define TOUPPER(c) (char)toupper((int)(unsigned char)(c))
 
 #ifndef NDEBUG
 static const char *
@@ -813,9 +819,10 @@
 static int
 reply_parse(u8 *packet, int length) {
 	int j = 0;	/* index into packet */
+	int k;
 	u16 _t;	 /* used by the macros */
 	u32 _t32;  /* used by the macros */
-	char tmp_name[256]; /* used by the macros */
+	char tmp_name[256], cmp_name[256]; /* used by the macros */
 
 	u16 trans_id, questions, answers, authority, additional, datalength;
 	u16 flags = 0;
@@ -823,6 +830,7 @@
 	struct reply reply;
 	struct request *req = NULL;
 	unsigned int i;
+	int name_matches = 0;
 
 	GET16(trans_id);
 	GET16(flags);
@@ -848,12 +856,29 @@
 	/* if (!answers) return; */  /* must have an answer of some form */
 
 	/* This macro skips a name in the DNS reply. */
-#define SKIP_NAME														\
+#define GET_NAME														\
 	do { tmp_name[0] = '\0';											\
 		if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) \
 			goto err;													\
 	} while(0);
+#define TEST_NAME														\
+	do { tmp_name[0] = '\0';											\
+		cmp_name[0] = '\0';												\
+		k = j;															\
+		if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) \
+			goto err;													\
+		if (name_parse(req->request, req->request_len, &k, cmp_name, sizeof(cmp_name))<0) \
+			goto err;													\
+		if (global_randomize_case) {									\
+			if (strcmp(tmp_name, cmp_name) == 0)						\
+				name_matches = 1; /* we ignore mismatching names */		\
+		} else {														\
+			if (strcasecmp(tmp_name, cmp_name) == 0)					\
+				name_matches = 1;										\
+		}																\
+	} while(0)
 
+
 	reply.type = req->request_type;
 
 	/* skip over each question in the reply */
@@ -861,11 +886,14 @@
 		/* the question looks like
 		 * <label:name><u16:type><u16:class>
 		 */
-		SKIP_NAME;
+	    TEST_NAME;
 		j += 4;
 		if (j >= length) goto err;
 	}
 
+	if (!name_matches)
+		goto err;
+
 	/* now we have the answer section which looks like
 	 * <label:name><u16:type><u16:class><u32:ttl><u16:len><data...>
 	 */
@@ -875,7 +903,7 @@
 
 		/* XXX I'd be more comfortable if we actually checked the name */
 		/* here. -NM */
-		SKIP_NAME;
+		GET_NAME;
 		GET16(type);
 		GET16(class);
 		GET32(ttl);
@@ -1082,6 +1110,22 @@
 		trans_id_function = default_transaction_id_fn;
 }
 
+
+static void
+get_random_bytes(char *buf, size_t n)
+{
+	unsigned i;
+	for (i = 0; i < n-1; i += 2) {
+		u16 tid = trans_id_function();
+		buf[i] = (tid >> 8) & 0xff;
+		buf[i+1] = tid & 0xff;
+	}
+	if (i < n) {
+		u16 tid = trans_id_function();
+		buf[i] = tid & 0xff;
+	}
+}
+
 /* Try to choose a strong transaction id which isn't already in flight */
 static u16
 transaction_id_pick(void) {
@@ -1143,17 +1187,34 @@
 /* this is called when a namesever socket is ready for reading */
 static void
 nameserver_read(struct nameserver *ns) {
+	struct sockaddr_storage ss;
+	struct sockaddr *sa = (struct sockaddr *)&ss;
+	struct sockaddr_in *sin;
+	socklen_t addrlen = sizeof(ss);
 	u8 packet[1500];
 
 	for (;;) {
 		const int r =
-            (int)recv(ns->socket, packet,(socklen_t)sizeof(packet), 0);
+            (int)recvfrom(ns->socket, packet,(socklen_t)sizeof(packet), 0,
+						  sa, &addrlen);
 		if (r < 0) {
 			int err = last_error(ns->socket);
 			if (error_is_eagain(err)) return;
 			nameserver_failed(ns, strerror(err));
 			return;
 		}
+		if (sa->sa_family != AF_INET) {
+			log(EVDNS_LOG_WARN,
+				"Address family mismatch on received DNS packet.");
+			return;
+		}
+		sin = (struct sockaddr_in *)sa;
+		if (sin->sin_addr.s_addr != ns->address) {
+			log(EVDNS_LOG_WARN,
+				"Address mismatch on received DNS packet.  Address was %s.",
+				debug_ntoa(sin->sin_addr.s_addr));
+			return;
+		}
 		ns->timedout = 0;
 		reply_parse(packet, r);
 	}
@@ -2243,12 +2304,29 @@
 	/* the request data is alloced in a single block with the header */
 	struct request *const req =
 		(struct request *) malloc(sizeof(struct request) + request_max_len);
+	char namebuf[256];
 	int rlen;
 	(void) flags;
 
 	if (!req) return NULL;
 	memset(req, 0, sizeof(struct request));
 
+	if (global_randomize_case) {
+		unsigned i;
+		char randbits[32];
+		strlcpy(namebuf, name, sizeof(namebuf));
+		get_random_bytes(randbits, (name_len+7)/8);
+		for (i = 0; i < name_len; ++i) {
+			if (ISALPHA(namebuf[i])) {
+				if ((randbits[i >> 3] & (1<<(i%7))))
+					namebuf[i] = TOLOWER(namebuf[i]);
+				else
+					namebuf[i] = TOUPPER(namebuf[i]);
+			}
+		}
+		name = namebuf;
+	}
+
 	/* request data lives just after the header */
 	req->request = ((u8 *) req) + sizeof(struct request);
 	/* denotes that the request data shouldn't be free()ed */
@@ -2690,7 +2768,13 @@
 		if (!(flags & DNS_OPTION_MISC)) return 0;
 		log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries);
 		global_max_retransmits = retries;
+	} else if (!strncmp(option, "randomize-case:", 15)) {
+		int randcase = strtoint(val);
+		if (!(flags & DNS_OPTION_MISC)) return 0;
+		log(EVDNS_LOG_DEBUG, "Setting randomize_case to %d", randcase);
+		global_randomize_case = randcase;
 	}
+
 	return 0;
 }
 
@@ -3127,7 +3211,7 @@
 		}
 	}
 
-	r = evdns_request_respond(req, 0);
+	r = evdns_server_request_respond(req, 0);
 	if (r<0)
 		printf("eeek, couldn't send reply.\n");
 }

Modified: tor/branches/tor-0_2_0-patches/src/or/or.h
===================================================================
--- tor/branches/tor-0_2_0-patches/src/or/or.h	2008-12-16 22:53:24 UTC (rev 17635)
+++ tor/branches/tor-0_2_0-patches/src/or/or.h	2008-12-17 12:51:36 UTC (rev 17636)
@@ -2297,6 +2297,8 @@
                       * the local domains. */
   int ServerDNSDetectHijacking; /**< Boolean: If true, check for DNS failure
                                  * hijacking. */
+  int ServerDNSRandomizeCase; /**< Boolean: Use the 0x20-hack to prevent
+                               * DNS poisoning attacks. */
   char *ServerDNSResolvConfFile; /**< If provided, we configure our internal
                      * resolver from the file here rather than from
                      * /etc/resolv.conf (Unix) or the registry (Windows). */



More information about the tor-commits mailing list