mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-09 12:28:19 -04:00
Implement increased DSN-poisoning resistance via the 0x20 hack.
svn:r958
This commit is contained in:
parent
1eeb96aa88
commit
dd73168556
@ -130,7 +130,9 @@ Changes in current version:
|
||||
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
|
||||
|
77
evdns.c
77
evdns.c
@ -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 @@ struct evdns_base {
|
||||
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 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
|
||||
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 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
|
||||
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 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
|
||||
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 @@ default_transaction_id_fn(void)
|
||||
|
||||
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,6 +1130,14 @@ evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void))
|
||||
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 */
|
||||
@ -2276,6 +2322,10 @@ string_num_dots(const char *s) {
|
||||
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 @@ request_new(struct evdns_base *base, int type, const char *name, int flags,
|
||||
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 @@ request_new(struct evdns_base *base, int type, const char *name, int flags,
|
||||
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 @@ evdns_base_set_option(struct evdns_base *base,
|
||||
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 @@ evdns_base_new(struct event_base *event_base, int initialize_nameservers)
|
||||
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
|
||||
|
@ -380,7 +380,7 @@ struct evdns_request *evdns_base_resolve_reverse_ipv6(struct evdns_base *base, s
|
||||
|
||||
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 @@ void evdns_set_log_fn(evdns_debug_log_fn_type fn);
|
||||
|
||||
/**
|
||||
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
|
||||
|
||||
/*
|
||||
|
@ -198,24 +198,27 @@ dns_server_request_cb(struct evdns_server_request *req, void *data)
|
||||
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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user