From: <ni...@us...> - 2008-12-03 20:09:18
|
Revision: 958 http://levent.svn.sourceforge.net/levent/?rev=958&view=rev Author: nickm Date: 2008-12-03 20:09:13 +0000 (Wed, 03 Dec 2008) Log Message: ----------- Implement increased DSN-poisoning resistance via the 0x20 hack. Modified Paths: -------------- trunk/libevent/ChangeLog trunk/libevent/evdns.c trunk/libevent/include/event2/dns.h trunk/libevent/test/regress_dns.c Modified: trunk/libevent/ChangeLog =================================================================== --- trunk/libevent/ChangeLog 2008-11-29 01:12:41 UTC (rev 957) +++ trunk/libevent/ChangeLog 2008-12-03 20:09:13 UTC (rev 958) @@ -130,7 +130,9 @@ o Allow setting of local port for evhttp connections to support millions of connections from a single system; from Richard Jones. o Clear the timer cache when leaving the event loop; reported by Robin Haberkorn o Fix a typo in setting the global event base; reported by lance. - + o Set the 0x20 bit on outgoing alphabetic characters in DNS requests randomly, and insist on a match in replies. This helps resist DNS poisoning attacks. + + Changes in 1.4.0: o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr. o demote most http warnings to debug messages Modified: trunk/libevent/evdns.c =================================================================== --- trunk/libevent/evdns.c 2008-11-29 01:12:41 UTC (rev 957) +++ trunk/libevent/evdns.c 2008-12-03 20:09:13 UTC (rev 958) @@ -103,6 +103,7 @@ #include "evutil.h" #include "log.h" #include "mm-internal.h" +#include "strlcpy-internal.h" #ifdef WIN32 #include <winsock2.h> #include <windows.h> @@ -317,6 +318,8 @@ int global_max_retransmits; /* number of times we'll retransmit a request which timed out */ /* number of timeouts in a row before we consider this server to be down */ int global_max_nameserver_timeout; + /* true iff we will use the 0x20 hack to prevent poisoning attacks. */ + int global_randomize_case; struct search_state *global_search_state; }; @@ -813,6 +816,7 @@ u16 _t; /* used by the macros */ u32 _t32; /* used by the macros */ char tmp_name[256], cmp_name[256]; /* used by the macros */ + int name_matches = 0; u16 trans_id, questions, answers, authority, additional, datalength; u16 flags = 0; @@ -858,8 +862,13 @@ goto err; \ if (name_parse(req->request, req->request_len, &k, cmp_name, sizeof(cmp_name))<0) \ goto err; \ - if (memcmp(tmp_name, cmp_name, strlen (tmp_name)) != 0) \ - return (-1); /* we ignore mismatching names */ \ + if (base->global_randomize_case) { \ + if (strcmp(tmp_name, cmp_name) == 0) \ + name_matches = 1; \ + } else { \ + if (strcasecmp(tmp_name, cmp_name) == 0) \ + name_matches = 1; \ + } \ } while(0) reply.type = req->request_type; @@ -874,6 +883,9 @@ 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...> */ @@ -1085,6 +1097,32 @@ static ev_uint16_t (*trans_id_function)(void) = default_transaction_id_fn; +static void +default_random_bytes_fn(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; + } +} + +static void (*rand_bytes_function)(char *buf, size_t n) = + default_random_bytes_fn; + +static u16 +trans_id_from_random_bytes_fn(void) +{ + u16 tid; + rand_bytes_function((char*) &tid, sizeof(tid)); + return tid; +} + void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void)) { @@ -1092,8 +1130,16 @@ trans_id_function = fn; else trans_id_function = default_transaction_id_fn; + rand_bytes_function = default_random_bytes_fn; } +void +evdns_set_random_bytes_fn(void (*fn)(char *, size_t)) +{ + rand_bytes_function = fn; + trans_id_function = trans_id_from_random_bytes_fn; +} + /* Try to choose a strong transaction id which isn't already in flight */ static u16 transaction_id_pick(struct evdns_base *base) { @@ -2276,6 +2322,10 @@ return count; } +/* Helper: provide a working isalpha implementation on platforms with funny + * ideas about character types and isalpha behavior. */ +#define ISALPHA(c) isalpha((int)(unsigned char)(c)) + static struct evdns_request * request_new(struct evdns_base *base, int type, const char *name, int flags, evdns_callback_type callback, void *user_ptr) { @@ -2289,12 +2339,29 @@ struct evdns_request *const req = (struct evdns_request *) mm_malloc(sizeof(struct evdns_request) + request_max_len); int rlen; + char namebuf[256]; (void) flags; if (!req) return NULL; memset(req, 0, sizeof(struct evdns_request)); req->base = base; + if (base->global_randomize_case) { + unsigned i; + char randbits[(sizeof(namebuf)+7)/8]; + strlcpy(namebuf, name, sizeof(namebuf)); + rand_bytes_function(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] |= 0x20; + else + namebuf[i] &= ~0x20; + } + } + name = namebuf; + } + /* request data lives just after the header */ req->request = ((u8 *) req) + sizeof(struct evdns_request); /* denotes that the request data shouldn't be free()ed */ @@ -2303,6 +2370,7 @@ type, CLASS_INET, req->request, request_max_len); if (rlen < 0) goto err1; + req->request_len = rlen; req->trans_id = trans_id; req->tx_count = 0; @@ -2821,6 +2889,10 @@ if (!(flags & DNS_OPTION_MISC)) return 0; log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries); base->global_max_retransmits = retries; + } else if (!strncmp(option, "randomize-case:", 15)) { + int randcase = strtoint(val); + if (!(flags & DNS_OPTION_MISC)) return 0; + base->global_randomize_case = randcase; } return 0; } @@ -3168,6 +3240,7 @@ base->global_max_retransmits = 3; base->global_max_nameserver_timeout = 3; base->global_search_state = NULL; + base->global_randomize_case = 1; if (initialize_nameservers) { int r; #ifdef WIN32 Modified: trunk/libevent/include/event2/dns.h =================================================================== --- trunk/libevent/include/event2/dns.h 2008-11-29 01:12:41 UTC (rev 957) +++ trunk/libevent/include/event2/dns.h 2008-12-03 20:09:13 UTC (rev 958) @@ -380,7 +380,7 @@ The currently available configuration options are: - ndots, timeout, max-timeouts, max-inflight, and attempts + ndots, timeout, max-timeouts, max-inflight, attempts, randomize-case. @param base the evdns_base to which to apply this operation @param option the name of the configuration option to be modified @@ -473,12 +473,21 @@ /** Set a callback that will be invoked to generate transaction IDs. By - default, we pick transaction IDs based on the current clock time. + default, we pick transaction IDs based on the current clock time, which + is bad for security. @param fn the new callback, or NULL to use the default. */ void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void)); +/** + Set a callback used to generate random bytes. By default, we use + the same function as passed to evdns_set_transaction_id_fn to generate + bytes two at a time. If a function is provided here, it's also used + to generate transaction IDs. +*/ +void evdns_set_random_bytes_fn(void (*fn)(char *, size_t)); + #define DNS_NO_SEARCH 1 /* Modified: trunk/libevent/test/regress_dns.c =================================================================== --- trunk/libevent/test/regress_dns.c 2008-11-29 01:12:41 UTC (rev 957) +++ trunk/libevent/test/regress_dns.c 2008-12-03 20:09:13 UTC (rev 958) @@ -198,24 +198,27 @@ ans.s_addr = htonl(0xc0a80b0bUL); /* 192.168.11.11 */ if (req->questions[i]->type == EVDNS_TYPE_A && req->questions[i]->dns_question_class == EVDNS_CLASS_INET && - !strcmp(req->questions[i]->name, "zz.example.com")) { - r = evdns_server_request_add_a_reply(req, "zz.example.com", + !strcasecmp(req->questions[i]->name, "zz.example.com")) { + r = evdns_server_request_add_a_reply(req, + req->questions[i]->name, 1, &ans.s_addr, 12345); if (r<0) dns_ok = 0; } else if (req->questions[i]->type == EVDNS_TYPE_AAAA && req->questions[i]->dns_question_class == EVDNS_CLASS_INET && - !strcmp(req->questions[i]->name, "zz.example.com")) { + !strcasecmp(req->questions[i]->name, "zz.example.com")) { char addr6[17] = "abcdefghijklmnop"; - r = evdns_server_request_add_aaaa_reply(req, "zz.example.com", + r = evdns_server_request_add_aaaa_reply(req, + req->questions[i]->name, 1, addr6, 123); if (r<0) dns_ok = 0; } else if (req->questions[i]->type == EVDNS_TYPE_PTR && req->questions[i]->dns_question_class == EVDNS_CLASS_INET && - !strcmp(req->questions[i]->name, TEST_ARPA)) { - r = evdns_server_request_add_ptr_reply(req, NULL, TEST_ARPA, - "ZZ.EXAMPLE.COM", 54321); + !strcasecmp(req->questions[i]->name, TEST_ARPA)) { + r = evdns_server_request_add_ptr_reply(req, NULL, + req->questions[i]->name, + "ZZ.EXAMPLE.COM", 54321); if (r<0) dns_ok = 0; } else { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |