mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-10 13:04:23 -04:00
Implement increased DSN-poisoning resistance via the 0x20 hack.
svn:r958
This commit is contained in:
parent
1eeb96aa88
commit
dd73168556
@ -130,6 +130,8 @@ 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 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 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 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:
|
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 allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr.
|
||||||
|
77
evdns.c
77
evdns.c
@ -103,6 +103,7 @@
|
|||||||
#include "evutil.h"
|
#include "evutil.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mm-internal.h"
|
#include "mm-internal.h"
|
||||||
|
#include "strlcpy-internal.h"
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.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 */
|
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 */
|
/* number of timeouts in a row before we consider this server to be down */
|
||||||
int global_max_nameserver_timeout;
|
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;
|
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 */
|
u16 _t; /* used by the macros */
|
||||||
u32 _t32; /* used by the macros */
|
u32 _t32; /* used by the macros */
|
||||||
char tmp_name[256], cmp_name[256]; /* 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 trans_id, questions, answers, authority, additional, datalength;
|
||||||
u16 flags = 0;
|
u16 flags = 0;
|
||||||
@ -858,8 +862,13 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
|
|||||||
goto err; \
|
goto err; \
|
||||||
if (name_parse(req->request, req->request_len, &k, cmp_name, sizeof(cmp_name))<0) \
|
if (name_parse(req->request, req->request_len, &k, cmp_name, sizeof(cmp_name))<0) \
|
||||||
goto err; \
|
goto err; \
|
||||||
if (memcmp(tmp_name, cmp_name, strlen (tmp_name)) != 0) \
|
if (base->global_randomize_case) { \
|
||||||
return (-1); /* we ignore mismatching names */ \
|
if (strcmp(tmp_name, cmp_name) == 0) \
|
||||||
|
name_matches = 1; \
|
||||||
|
} else { \
|
||||||
|
if (strcasecmp(tmp_name, cmp_name) == 0) \
|
||||||
|
name_matches = 1; \
|
||||||
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
reply.type = req->request_type;
|
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 (j > length) goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!name_matches)
|
||||||
|
goto err;
|
||||||
|
|
||||||
/* now we have the answer section which looks like
|
/* now we have the answer section which looks like
|
||||||
* <label:name><u16:type><u16:class><u32:ttl><u16:len><data...>
|
* <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 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
|
void
|
||||||
evdns_set_transaction_id_fn(ev_uint16_t (*fn)(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;
|
trans_id_function = fn;
|
||||||
else
|
else
|
||||||
trans_id_function = default_transaction_id_fn;
|
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 */
|
/* 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;
|
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 *
|
static struct evdns_request *
|
||||||
request_new(struct evdns_base *base, int type, const char *name, int flags,
|
request_new(struct evdns_base *base, int type, const char *name, int flags,
|
||||||
evdns_callback_type callback, void *user_ptr) {
|
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 *const req =
|
||||||
(struct evdns_request *) mm_malloc(sizeof(struct evdns_request) + request_max_len);
|
(struct evdns_request *) mm_malloc(sizeof(struct evdns_request) + request_max_len);
|
||||||
int rlen;
|
int rlen;
|
||||||
|
char namebuf[256];
|
||||||
(void) flags;
|
(void) flags;
|
||||||
|
|
||||||
if (!req) return NULL;
|
if (!req) return NULL;
|
||||||
memset(req, 0, sizeof(struct evdns_request));
|
memset(req, 0, sizeof(struct evdns_request));
|
||||||
req->base = base;
|
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 */
|
/* request data lives just after the header */
|
||||||
req->request = ((u8 *) req) + sizeof(struct evdns_request);
|
req->request = ((u8 *) req) + sizeof(struct evdns_request);
|
||||||
/* denotes that the request data shouldn't be free()ed */
|
/* 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);
|
type, CLASS_INET, req->request, request_max_len);
|
||||||
if (rlen < 0)
|
if (rlen < 0)
|
||||||
goto err1;
|
goto err1;
|
||||||
|
|
||||||
req->request_len = rlen;
|
req->request_len = rlen;
|
||||||
req->trans_id = trans_id;
|
req->trans_id = trans_id;
|
||||||
req->tx_count = 0;
|
req->tx_count = 0;
|
||||||
@ -2821,6 +2889,10 @@ evdns_base_set_option(struct evdns_base *base,
|
|||||||
if (!(flags & DNS_OPTION_MISC)) return 0;
|
if (!(flags & DNS_OPTION_MISC)) return 0;
|
||||||
log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries);
|
log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries);
|
||||||
base->global_max_retransmits = 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;
|
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_retransmits = 3;
|
||||||
base->global_max_nameserver_timeout = 3;
|
base->global_max_nameserver_timeout = 3;
|
||||||
base->global_search_state = NULL;
|
base->global_search_state = NULL;
|
||||||
|
base->global_randomize_case = 1;
|
||||||
if (initialize_nameservers) {
|
if (initialize_nameservers) {
|
||||||
int r;
|
int r;
|
||||||
#ifdef WIN32
|
#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:
|
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 base the evdns_base to which to apply this operation
|
||||||
@param option the name of the configuration option to be modified
|
@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
|
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.
|
@param fn the new callback, or NULL to use the default.
|
||||||
*/
|
*/
|
||||||
void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void));
|
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
|
#define DNS_NO_SEARCH 1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -198,23 +198,26 @@ dns_server_request_cb(struct evdns_server_request *req, void *data)
|
|||||||
ans.s_addr = htonl(0xc0a80b0bUL); /* 192.168.11.11 */
|
ans.s_addr = htonl(0xc0a80b0bUL); /* 192.168.11.11 */
|
||||||
if (req->questions[i]->type == EVDNS_TYPE_A &&
|
if (req->questions[i]->type == EVDNS_TYPE_A &&
|
||||||
req->questions[i]->dns_question_class == EVDNS_CLASS_INET &&
|
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")) {
|
||||||
r = evdns_server_request_add_a_reply(req, "zz.example.com",
|
r = evdns_server_request_add_a_reply(req,
|
||||||
|
req->questions[i]->name,
|
||||||
1, &ans.s_addr, 12345);
|
1, &ans.s_addr, 12345);
|
||||||
if (r<0)
|
if (r<0)
|
||||||
dns_ok = 0;
|
dns_ok = 0;
|
||||||
} else if (req->questions[i]->type == EVDNS_TYPE_AAAA &&
|
} else if (req->questions[i]->type == EVDNS_TYPE_AAAA &&
|
||||||
req->questions[i]->dns_question_class == EVDNS_CLASS_INET &&
|
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";
|
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);
|
1, addr6, 123);
|
||||||
if (r<0)
|
if (r<0)
|
||||||
dns_ok = 0;
|
dns_ok = 0;
|
||||||
} else if (req->questions[i]->type == EVDNS_TYPE_PTR &&
|
} else if (req->questions[i]->type == EVDNS_TYPE_PTR &&
|
||||||
req->questions[i]->dns_question_class == EVDNS_CLASS_INET &&
|
req->questions[i]->dns_question_class == EVDNS_CLASS_INET &&
|
||||||
!strcmp(req->questions[i]->name, TEST_ARPA)) {
|
!strcasecmp(req->questions[i]->name, TEST_ARPA)) {
|
||||||
r = evdns_server_request_add_ptr_reply(req, NULL, TEST_ARPA,
|
r = evdns_server_request_add_ptr_reply(req, NULL,
|
||||||
|
req->questions[i]->name,
|
||||||
"ZZ.EXAMPLE.COM", 54321);
|
"ZZ.EXAMPLE.COM", 54321);
|
||||||
if (r<0)
|
if (r<0)
|
||||||
dns_ok = 0;
|
dns_ok = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user