mirror of
https://github.com/NLnetLabs/unbound.git
synced 2024-09-21 06:37:08 +00:00
Downstream DNS Cookies a la RFC7873 and RFC9018
Create server cookies for clients that send client cookies. Needs to be turned on in the config file with: answer-cookie: yes A cookie-secret can be configured for anycast setups. Also adds an access control list that will allow queries with either a valid cookie or over a stateful transport.
This commit is contained in:
parent
71f23ef354
commit
75f3fbdd65
@ -128,7 +128,7 @@ util/config_file.c util/configlexer.c util/configparser.c \
|
||||
util/shm_side/shm_main.c services/authzone.c \
|
||||
util/fptr_wlist.c util/locks.c util/log.c util/mini_event.c util/module.c \
|
||||
util/netevent.c util/net_help.c util/random.c util/rbtree.c util/regional.c \
|
||||
util/rtt.c util/edns.c util/storage/dnstree.c util/storage/lookup3.c \
|
||||
util/rtt.c util/siphash.c util/edns.c util/storage/dnstree.c util/storage/lookup3.c \
|
||||
util/storage/lruhash.c util/storage/slabhash.c util/tcp_conn_limit.c \
|
||||
util/timehist.c util/tube.c \
|
||||
util/ub_event.c util/ub_event_pluggable.c util/winsock_event.c \
|
||||
@ -145,7 +145,7 @@ as112.lo msgparse.lo msgreply.lo packed_rrset.lo iterator.lo iter_delegpt.lo \
|
||||
iter_donotq.lo iter_fwd.lo iter_hints.lo iter_priv.lo iter_resptype.lo \
|
||||
iter_scrub.lo iter_utils.lo localzone.lo mesh.lo modstack.lo view.lo \
|
||||
outbound_list.lo alloc.lo config_file.lo configlexer.lo configparser.lo \
|
||||
fptr_wlist.lo edns.lo locks.lo log.lo mini_event.lo module.lo net_help.lo \
|
||||
fptr_wlist.lo siphash.lo edns.lo locks.lo log.lo mini_event.lo module.lo net_help.lo \
|
||||
random.lo rbtree.lo regional.lo rtt.lo dnstree.lo lookup3.lo lruhash.lo \
|
||||
slabhash.lo tcp_conn_limit.lo timehist.lo tube.lo winsock_event.lo \
|
||||
autotrust.lo val_anchor.lo rpz.lo \
|
||||
@ -915,7 +915,8 @@ config_file.lo config_file.o: $(srcdir)/util/config_file.c config.h $(srcdir)/ut
|
||||
configlexer.lo configlexer.o: util/configlexer.c config.h $(srcdir)/util/configyyrename.h \
|
||||
$(srcdir)/util/config_file.h util/configparser.h
|
||||
configparser.lo configparser.o: util/configparser.c config.h $(srcdir)/util/configyyrename.h \
|
||||
$(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/util/log.h
|
||||
$(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/util/log.h $(srcdir)/sldns/str2wire.h \
|
||||
$(srcdir)/sldns/rrdef.h
|
||||
shm_main.lo shm_main.o: $(srcdir)/util/shm_side/shm_main.c config.h $(srcdir)/util/shm_side/shm_main.h \
|
||||
$(srcdir)/libunbound/unbound.h $(srcdir)/daemon/daemon.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
|
||||
$(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \
|
||||
@ -1004,6 +1005,7 @@ rtt.lo rtt.o: $(srcdir)/util/rtt.c config.h $(srcdir)/util/rtt.h $(srcdir)/itera
|
||||
$(srcdir)/services/outbound_list.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/storage/lruhash.h \
|
||||
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/module.h \
|
||||
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h
|
||||
siphash.lo siphash.o: $(srcdir)/util/siphash.c
|
||||
edns.lo edns.o: $(srcdir)/util/edns.c config.h $(srcdir)/util/edns.h $(srcdir)/util/storage/dnstree.h \
|
||||
$(srcdir)/util/rbtree.h $(srcdir)/util/config_file.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
|
||||
$(srcdir)/util/net_help.h $(srcdir)/util/log.h $(srcdir)/util/regional.h \
|
||||
|
@ -109,6 +109,8 @@ parse_acl_access(const char* str, enum acl_access* control)
|
||||
*control = acl_allow_snoop;
|
||||
else if(strcmp(str, "allow_setrd") == 0)
|
||||
*control = acl_allow_setrd;
|
||||
else if (strcmp(str, "allow_cookie") == 0)
|
||||
*control = acl_allow_cookie;
|
||||
else {
|
||||
log_err("access control type %s unknown", str);
|
||||
return 0;
|
||||
|
@ -65,7 +65,9 @@ enum acl_access {
|
||||
/** allow full access for all queries, recursion and cache snooping */
|
||||
acl_allow_snoop,
|
||||
/** allow full access for recursion queries and set RD flag regardless of request */
|
||||
acl_allow_setrd
|
||||
acl_allow_setrd,
|
||||
/** allow full access if valid cookie present or stateful transport */
|
||||
acl_allow_cookie
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1432,8 +1432,10 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
}
|
||||
goto send_reply;
|
||||
}
|
||||
if((ret=parse_edns_from_query_pkt(c->buffer, &edns, worker->env.cfg, c,
|
||||
worker->scratchpad)) != 0) {
|
||||
if((ret=parse_edns_from_query_pkt(
|
||||
c->buffer, &edns, worker->env.cfg, c, repinfo,
|
||||
(worker->env.now ? *worker->env.now : time(NULL)),
|
||||
worker->scratchpad)) != 0) {
|
||||
struct edns_data reply_edns;
|
||||
verbose(VERB_ALGO, "worker parse edns: formerror.");
|
||||
log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
|
||||
@ -1466,6 +1468,44 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
edns.udp_size = NORMAL_UDP_SIZE;
|
||||
}
|
||||
}
|
||||
/* "if, else if" sequence below deals with downstream DNS Cookies */
|
||||
if (acl != acl_allow_cookie)
|
||||
; /* pass; No cookie downstream processing whatsoever */
|
||||
|
||||
else if (edns.cookie_valid)
|
||||
; /* pass; Valid cookie is good! */
|
||||
|
||||
else if (c->type != comm_udp)
|
||||
; /* pass; Stateful transport */
|
||||
|
||||
else if (edns.cookie_present) {
|
||||
/* Cookie present, but not valid: Cookie was bad! */
|
||||
extended_error_encode(c->buffer,
|
||||
LDNS_EXT_RCODE_BADCOOKIE, &qinfo,
|
||||
*(uint16_t*)(void *)
|
||||
sldns_buffer_begin(c->buffer),
|
||||
sldns_buffer_read_u16_at(c->buffer, 2),
|
||||
0, &edns);
|
||||
regional_free_all(worker->scratchpad);
|
||||
goto send_reply;
|
||||
} else {
|
||||
/* Cookie requered, but no cookie present on UDP */
|
||||
verbose(VERB_ALGO, "worker request: "
|
||||
"need cookie or stateful transport");
|
||||
log_addr(VERB_ALGO, "from",
|
||||
&repinfo->addr, repinfo->addrlen);
|
||||
EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out,
|
||||
worker->scratchpad, LDNS_EDE_OTHER,
|
||||
"DNS Cookie needed for UDP replies");
|
||||
error_encode(c->buffer,
|
||||
(LDNS_RCODE_REFUSED|BIT_TC), &qinfo,
|
||||
*(uint16_t*)(void *)
|
||||
sldns_buffer_begin(c->buffer),
|
||||
sldns_buffer_read_u16_at(c->buffer, 2),
|
||||
&edns);
|
||||
regional_free_all(worker->scratchpad);
|
||||
goto send_reply;
|
||||
}
|
||||
if(edns.udp_size > worker->daemon->cfg->max_udp_size &&
|
||||
c->type == comm_udp) {
|
||||
verbose(VERB_QUERY,
|
||||
|
@ -673,9 +673,9 @@ This option is experimental at this time.
|
||||
.B access\-control: \fI<IP netblock> <action>
|
||||
The netblock is given as an IP4 or IP6 address with /size appended for a
|
||||
classless network block. The action can be \fIdeny\fR, \fIrefuse\fR,
|
||||
\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIdeny_non_local\fR or
|
||||
\fIrefuse_non_local\fR.
|
||||
The most specific netblock match is used, if none match \fIrefuse\fR is used.
|
||||
\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIallow_cookie\fR,
|
||||
\fIdeny_non_local\fR or \fIrefuse_non_local\fR.
|
||||
The most specific netblock match is used, if none match \fIdeny\fR is used.
|
||||
The order of the access\-control statements therefore does not matter.
|
||||
.IP
|
||||
The action \fIdeny\fR stops queries from hosts from that netblock.
|
||||
@ -710,6 +710,14 @@ the cache contents (for malicious acts). However, nonrecursive queries can
|
||||
also be a valuable debugging tool (when you want to examine the cache
|
||||
contents). In that case use \fIallow_snoop\fR for your administration host.
|
||||
.IP
|
||||
When the \fBanswer\-cookie\fR option is enabled, the \fIallow_cookie\fR action
|
||||
will allow access to UDP queries that contain a valid Server Cookie as
|
||||
specified in RFC 7873 and RFC9018. UDP queries containing only a Client Cookie
|
||||
and no Server Cookie, will receive a BADCOOKIE response including a Server
|
||||
Cookie, allow clients to retry with that Server Cookie. The \fIallow_cookie\fR
|
||||
will also accept requests over statefull transports, regardless of the precence
|
||||
of a Cookie and regardless the \fBanswer\-cookie\fR setting.
|
||||
.IP
|
||||
By default only localhost is \fIallow\fRed, the rest is \fIrefuse\fRd.
|
||||
The default is \fIrefuse\fRd, because that is protocol\-friendly. The DNS
|
||||
protocol is not designed to handle dropped packets due to policy, and
|
||||
@ -1824,6 +1832,15 @@ Set the number of servers that should be used for fast server selection. Only
|
||||
use the fastest specified number of servers with the fast\-server\-permil
|
||||
option, that turns this on or off. The default is to use the fastest 3 servers.
|
||||
.TP 5
|
||||
.B answer\-cookie: \fI<yes or no>
|
||||
Enable to answer to requests containig DNS Cookies as specified in RFC7873 and
|
||||
RFC9018. Default is no.
|
||||
.TP 5
|
||||
.B cookie\-secret: \fI<128 bit hex string>
|
||||
Server's in an Anycast deployment need to be able to verify each other's
|
||||
Server Cookies. For this they need to share the secret used to construct
|
||||
and verify the Server Cookies.
|
||||
Default is a 128 bits random secret generated at startup time.
|
||||
.B edns\-client\-string: \fI<IP netblock> <string>
|
||||
Include an EDNS0 option containing configured ascii string in queries with
|
||||
destination address matching the configured IP netblock. This configuration
|
||||
|
@ -604,6 +604,8 @@ setup_qinfo_edns(struct libworker* w, struct ctx_query* q,
|
||||
edns->opt_list_out = NULL;
|
||||
edns->opt_list_inplace_cb_out = NULL;
|
||||
edns->padding_block_size = 0;
|
||||
edns->cookie_present = 0;
|
||||
edns->cookie_valid = 0;
|
||||
if(sldns_buffer_capacity(w->back->udp_buff) < 65535)
|
||||
edns->udp_size = (uint16_t)sldns_buffer_capacity(
|
||||
w->back->udp_buff);
|
||||
|
@ -5419,6 +5419,8 @@ xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env)
|
||||
edns.opt_list_out = NULL;
|
||||
edns.opt_list_inplace_cb_out = NULL;
|
||||
edns.padding_block_size = 0;
|
||||
edns.cookie_present = 0;
|
||||
edns.cookie_valid = 0;
|
||||
if(sldns_buffer_capacity(buf) < 65535)
|
||||
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
|
||||
else edns.udp_size = 65535;
|
||||
@ -6612,6 +6614,8 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env)
|
||||
edns.opt_list_out = NULL;
|
||||
edns.opt_list_inplace_cb_out = NULL;
|
||||
edns.padding_block_size = 0;
|
||||
edns.cookie_present = 0;
|
||||
edns.cookie_valid = 0;
|
||||
if(sldns_buffer_capacity(buf) < 65535)
|
||||
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
|
||||
else edns.udp_size = 65535;
|
||||
|
@ -433,6 +433,7 @@ enum sldns_enum_edns_option
|
||||
LDNS_EDNS_DHU = 6, /* RFC6975 */
|
||||
LDNS_EDNS_N3U = 7, /* RFC6975 */
|
||||
LDNS_EDNS_CLIENT_SUBNET = 8, /* RFC7871 */
|
||||
LDNS_EDNS_COOKIE = 10, /* RFC7873 */
|
||||
LDNS_EDNS_KEEPALIVE = 11, /* draft-ietf-dnsop-edns-tcp-keepalive*/
|
||||
LDNS_EDNS_PADDING = 12, /* RFC7830 */
|
||||
LDNS_EDNS_EDE = 15, /* RFC8914 */
|
||||
@ -482,6 +483,9 @@ typedef enum sldns_enum_ede_code sldns_ede_code;
|
||||
#define LDNS_TSIG_ERROR_BADNAME 20
|
||||
#define LDNS_TSIG_ERROR_BADALG 21
|
||||
|
||||
/** DNS Cookie extended rcode */
|
||||
#define LDNS_EXT_RCODE_BADCOOKIE 23
|
||||
|
||||
/**
|
||||
* Contains all information about resource record types.
|
||||
*
|
||||
|
@ -1263,6 +1263,8 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
if(dnssec)
|
||||
edns.bits = EDNS_DO;
|
||||
edns.padding_block_size = 0;
|
||||
edns.cookie_present = 0;
|
||||
edns.cookie_valid = 0;
|
||||
edns.opt_list_in = NULL;
|
||||
edns.opt_list_out = per_upstream_opt_list;
|
||||
edns.opt_list_inplace_cb_out = NULL;
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "util/regional.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/random.h"
|
||||
#include "util/rtt.h"
|
||||
#include "services/cache/infra.h"
|
||||
#include "sldns/wire2str.h"
|
||||
@ -87,6 +88,9 @@ struct config_parser_state* cfg_parser = 0;
|
||||
/** init ports possible for use */
|
||||
static void init_outgoing_availports(int* array, int num);
|
||||
|
||||
/** init cookie with random data */
|
||||
static void init_cookie_secret(uint8_t* cookie_secret,size_t cookie_secret_len);
|
||||
|
||||
struct config_file*
|
||||
config_create(void)
|
||||
{
|
||||
@ -364,6 +368,10 @@ config_create(void)
|
||||
cfg->ipsecmod_whitelist = NULL;
|
||||
cfg->ipsecmod_strict = 0;
|
||||
#endif
|
||||
cfg->do_answer_cookie = 0;
|
||||
memset(cfg->cookie_secret, 0, sizeof(cfg->cookie_secret));
|
||||
cfg->cookie_secret_len = 16;
|
||||
init_cookie_secret(cfg->cookie_secret, cfg->cookie_secret_len);
|
||||
#ifdef USE_CACHEDB
|
||||
if(!(cfg->cachedb_backend = strdup("testframe"))) goto error_exit;
|
||||
if(!(cfg->cachedb_secret = strdup("default"))) goto error_exit;
|
||||
@ -1660,6 +1668,21 @@ config_delete(struct config_file* cfg)
|
||||
free(cfg);
|
||||
}
|
||||
|
||||
static void
|
||||
init_cookie_secret(uint8_t* cookie_secret, size_t cookie_secret_len)
|
||||
{
|
||||
struct ub_randstate *rand = ub_initstate(NULL);
|
||||
|
||||
if (!rand)
|
||||
fatal_exit("could not init random generator");
|
||||
while (cookie_secret_len) {
|
||||
*cookie_secret++ = (uint8_t)ub_random(rand);
|
||||
cookie_secret_len--;
|
||||
}
|
||||
ub_randfree(rand);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
init_outgoing_availports(int* a, int num)
|
||||
{
|
||||
|
@ -688,6 +688,13 @@ struct config_file {
|
||||
int redis_expire_records;
|
||||
#endif
|
||||
#endif
|
||||
/** Downstream DNS Cookies */
|
||||
/** do answer with server cookie when request contained cookie option */
|
||||
int do_answer_cookie;
|
||||
/** cookie secret */
|
||||
uint8_t cookie_secret[40];
|
||||
/** cookie secret length */
|
||||
size_t cookie_secret_len;
|
||||
|
||||
/* ipset module */
|
||||
#ifdef USE_IPSET
|
||||
|
@ -558,6 +558,8 @@ name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) }
|
||||
name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) }
|
||||
udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM) }
|
||||
tcp-connection-limit{COLON} { YDVAR(2, VAR_TCP_CONNECTION_LIMIT) }
|
||||
answer-cookie{COLON} { YDVAR(1, VAR_ANSWER_COOKIE ) }
|
||||
cookie-secret{COLON} { YDVAR(1, VAR_COOKIE_SECRET) }
|
||||
edns-client-string{COLON} { YDVAR(2, VAR_EDNS_CLIENT_STRING) }
|
||||
edns-client-string-opcode{COLON} { YDVAR(1, VAR_EDNS_CLIENT_STRING_OPCODE) }
|
||||
nsid{COLON} { YDVAR(1, VAR_NSID ) }
|
||||
|
@ -42,11 +42,13 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "util/configyyrename.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
#include "sldns/str2wire.h"
|
||||
|
||||
int ub_c_lex(void);
|
||||
void ub_c_error(const char *message);
|
||||
@ -181,6 +183,7 @@ extern struct config_parser_state* cfg_parser;
|
||||
%token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL
|
||||
%token VAR_FAST_SERVER_PERMIL VAR_FAST_SERVER_NUM
|
||||
%token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT VAR_TCP_CONNECTION_LIMIT
|
||||
%token VAR_ANSWER_COOKIE VAR_COOKIE_SECRET
|
||||
%token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY
|
||||
%token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY
|
||||
%token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES VAR_TLS_USE_SNI
|
||||
@ -316,6 +319,7 @@ content_server: server_num_threads | server_verbosity | server_port |
|
||||
server_unknown_server_time_limit | server_log_tag_queryreply |
|
||||
server_stream_wait_size | server_tls_ciphers |
|
||||
server_tls_ciphersuites | server_tls_session_ticket_keys |
|
||||
server_answer_cookie | server_cookie_secret |
|
||||
server_tls_use_sni | server_edns_client_string |
|
||||
server_edns_client_string_opcode | server_nsid |
|
||||
server_zonemd_permissive_mode | server_max_reuse_tcp_queries |
|
||||
@ -3695,6 +3699,30 @@ server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG
|
||||
}
|
||||
}
|
||||
;
|
||||
server_answer_cookie: VAR_ANSWER_COOKIE STRING_ARG
|
||||
{
|
||||
OUTYY(("P(server_answer_cookie:%s)\n", $2));
|
||||
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
|
||||
yyerror("expected yes or no.");
|
||||
else cfg_parser->cfg->do_answer_cookie = (strcmp($2, "yes")==0);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_cookie_secret: VAR_COOKIE_SECRET STRING_ARG
|
||||
{
|
||||
uint8_t secret[32];
|
||||
size_t secret_len = sizeof(secret);
|
||||
|
||||
OUTYY(("P(server_cookie_secret:%s)\n", $2));
|
||||
if (sldns_str2wire_hex_buf($2, secret, &secret_len)
|
||||
|| ( secret_len != 16))
|
||||
yyerror("expected 128 bit hex string");
|
||||
else {
|
||||
cfg_parser->cfg->cookie_secret_len = secret_len;
|
||||
memcpy(cfg_parser->cfg->cookie_secret, secret, sizeof(secret));
|
||||
}
|
||||
free($2);
|
||||
}
|
||||
ipsetstart: VAR_IPSET
|
||||
{
|
||||
OUTYY(("\nP(ipset:)\n"));
|
||||
@ -3764,10 +3792,11 @@ validate_acl_action(const char* action)
|
||||
strcmp(action, "refuse_non_local")!=0 &&
|
||||
strcmp(action, "allow_setrd")!=0 &&
|
||||
strcmp(action, "allow")!=0 &&
|
||||
strcmp(action, "allow_snoop")!=0)
|
||||
strcmp(action, "allow_snoop")!=0 &&
|
||||
strcmp(action, "allow_cookie")!=0)
|
||||
{
|
||||
yyerror("expected deny, refuse, deny_non_local, "
|
||||
"refuse_non_local, allow, allow_setrd or "
|
||||
"allow_snoop as access control action");
|
||||
"refuse_non_local, allow, allow_setrd, "
|
||||
"allow_snoop or allow_cookie as access control action");
|
||||
}
|
||||
}
|
||||
|
@ -951,11 +951,67 @@ edns_opt_list_append_keepalive(struct edns_option** list, int msec,
|
||||
data, region);
|
||||
}
|
||||
|
||||
int siphash(const uint8_t *in, const size_t inlen,
|
||||
const uint8_t *k, uint8_t *out, const size_t outlen);
|
||||
|
||||
/** RFC 1982 comparison, uses unsigned integers, and tries to avoid
|
||||
* compiler optimization (eg. by avoiding a-b<0 comparisons),
|
||||
* this routine matches compare_serial(), for SOA serial number checks */
|
||||
static int
|
||||
compare_1982(uint32_t a, uint32_t b)
|
||||
{
|
||||
/* for 32 bit values */
|
||||
const uint32_t cutoff = ((uint32_t) 1 << (32 - 1));
|
||||
|
||||
if (a == b) {
|
||||
return 0;
|
||||
} else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** if we know that b is larger than a, return the difference between them,
|
||||
* that is the distance between them. in RFC1982 arith */
|
||||
static uint32_t
|
||||
subtract_1982(uint32_t a, uint32_t b)
|
||||
{
|
||||
/* for 32 bit values */
|
||||
const uint32_t cutoff = ((uint32_t) 1 << (32 - 1));
|
||||
|
||||
if(a == b)
|
||||
return 0;
|
||||
if(a < b && b - a < cutoff) {
|
||||
return b-a;
|
||||
}
|
||||
if(a > b && a - b > cutoff) {
|
||||
return ((uint32_t)0xffffffff) - (a-b-1);
|
||||
}
|
||||
/* wrong case, b smaller than a */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t *
|
||||
cookie_hash(uint8_t *hash, uint8_t *buf,
|
||||
struct sockaddr_storage *addr, uint8_t *secret)
|
||||
{
|
||||
if (addr->ss_family == AF_INET6) {
|
||||
memcpy(buf+16, &((struct sockaddr_in6 *)addr)->sin6_addr, 16);
|
||||
siphash(buf, 32, secret, hash, 8);
|
||||
} else {
|
||||
memcpy(buf+16, &((struct sockaddr_in *)addr)->sin_addr, 4);
|
||||
siphash(buf, 20, secret, hash, 8);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/** parse EDNS options from EDNS wireformat rdata */
|
||||
static int
|
||||
parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
||||
struct edns_data* edns, struct config_file* cfg, struct comm_point* c,
|
||||
struct regional* region)
|
||||
struct comm_reply* repinfo, uint32_t now, struct regional* region)
|
||||
{
|
||||
/* To respond with a Keepalive option, the client connection must have
|
||||
* received one message with a TCP Keepalive EDNS option, and that
|
||||
@ -979,6 +1035,10 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
||||
while(rdata_len >= 4) {
|
||||
uint16_t opt_code = sldns_read_uint16(rdata_ptr);
|
||||
uint16_t opt_len = sldns_read_uint16(rdata_ptr+2);
|
||||
uint8_t server_cookie[40], hash[8];
|
||||
uint32_t cookie_time, subt_1982;
|
||||
int comp_1982;
|
||||
|
||||
rdata_ptr += 4;
|
||||
rdata_len -= 4;
|
||||
if(opt_len > rdata_len)
|
||||
@ -1041,6 +1101,86 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
||||
edns->padding_block_size = cfg->pad_responses_block_size;
|
||||
break;
|
||||
|
||||
case LDNS_EDNS_COOKIE:
|
||||
if(!cfg || !cfg->do_answer_cookie)
|
||||
break;
|
||||
if(opt_len != 8 && (opt_len < 16 || opt_len > 40)) {
|
||||
verbose(VERB_ALGO, "worker request: "
|
||||
"badly formatted cookie");
|
||||
return LDNS_RCODE_FORMERR;
|
||||
}
|
||||
edns->cookie_present = 1;
|
||||
|
||||
/* Copy client cookie, version and timestamp for
|
||||
* validation and creation purposes.
|
||||
*/
|
||||
memcpy(server_cookie, rdata_ptr, 16);
|
||||
|
||||
/* In the "if, if else" block below, we validate a
|
||||
* RFC9018 cookie. If it doesn't match the recipe, or
|
||||
* if it doesn't validate, or if the cookie is too old
|
||||
* (< 30 min), a new cookie is generated.
|
||||
*/
|
||||
if (opt_len != 24)
|
||||
; /* RFC9018 cookies are 24 bytes long */
|
||||
|
||||
else if (cfg->cookie_secret_len != 16)
|
||||
; /* RFC9018 cookies have 16 byte secrets */
|
||||
|
||||
else if (rdata_ptr[8] != 1)
|
||||
; /* RFC9018 cookies are cookie version 1 */
|
||||
|
||||
else if ((comp_1982 = compare_1982(now,
|
||||
(cookie_time = sldns_read_uint32(rdata_ptr + 12)))) > 0
|
||||
&& (subt_1982 = subtract_1982(cookie_time, now)) > 3600)
|
||||
; /* Cookie is older than 1 hour
|
||||
* (see RFC9018 Section 4.3.)
|
||||
*/
|
||||
|
||||
else if (comp_1982 <= 0
|
||||
&& subtract_1982(now, cookie_time) > 300)
|
||||
; /* Cookie time is more than 5 minutes in the
|
||||
* future. (see RFC9018 Section 4.3.)
|
||||
*/
|
||||
|
||||
else if (memcmp( cookie_hash( hash, server_cookie
|
||||
, &repinfo->addr
|
||||
, cfg->cookie_secret)
|
||||
, rdata_ptr + 16 , 8 ) == 0) {
|
||||
|
||||
/* Cookie is valid! */
|
||||
edns->cookie_valid = 1;
|
||||
if (comp_1982 > 0 && subt_1982 > 1800)
|
||||
; /* But older than 30 minutes,
|
||||
* so create a new one anyway */
|
||||
|
||||
else if (!edns_opt_list_append( /* Reuse cookie */
|
||||
&edns->opt_list_out, LDNS_EDNS_COOKIE, opt_len,
|
||||
rdata_ptr, region)) {
|
||||
log_err("out of memory");
|
||||
return LDNS_RCODE_SERVFAIL;
|
||||
} else
|
||||
/* Cookie to be reused added to
|
||||
* outgoing options. Done!
|
||||
*/
|
||||
break;
|
||||
}
|
||||
/* Add a new server cookie to outgoing cookies */
|
||||
server_cookie[ 8] = 1; /* Version */
|
||||
server_cookie[ 9] = 0; /* Reserved */
|
||||
server_cookie[10] = 0; /* Reserved */
|
||||
server_cookie[11] = 0; /* Reserved */
|
||||
sldns_write_uint32(server_cookie + 12, now);
|
||||
cookie_hash( hash, server_cookie
|
||||
, &repinfo->addr, cfg->cookie_secret);
|
||||
memcpy(server_cookie + 16, hash, 8);
|
||||
if (!edns_opt_list_append( &edns->opt_list_out
|
||||
, LDNS_EDNS_COOKIE
|
||||
, 24, server_cookie, region)) {
|
||||
log_err("out of memory");
|
||||
return LDNS_RCODE_SERVFAIL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1115,6 +1255,8 @@ parse_extract_edns_from_response_msg(struct msg_parse* msg,
|
||||
edns->opt_list_out = NULL;
|
||||
edns->opt_list_inplace_cb_out = NULL;
|
||||
edns->padding_block_size = 0;
|
||||
edns->cookie_present = 0;
|
||||
edns->cookie_valid = 0;
|
||||
|
||||
/* take the options */
|
||||
rdata_len = found->rr_first->size-2;
|
||||
@ -1170,7 +1312,8 @@ skip_pkt_rrs(sldns_buffer* pkt, int num)
|
||||
|
||||
int
|
||||
parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns,
|
||||
struct config_file* cfg, struct comm_point* c, struct regional* region)
|
||||
struct config_file* cfg, struct comm_point* c,
|
||||
struct comm_reply* repinfo, time_t now, struct regional* region)
|
||||
{
|
||||
size_t rdata_len;
|
||||
uint8_t* rdata_ptr;
|
||||
@ -1206,6 +1349,8 @@ parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns,
|
||||
edns->opt_list_out = NULL;
|
||||
edns->opt_list_inplace_cb_out = NULL;
|
||||
edns->padding_block_size = 0;
|
||||
edns->cookie_present = 0;
|
||||
edns->cookie_valid = 0;
|
||||
|
||||
/* take the options */
|
||||
rdata_len = sldns_buffer_read_u16(pkt);
|
||||
@ -1214,7 +1359,7 @@ parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns,
|
||||
rdata_ptr = sldns_buffer_current(pkt);
|
||||
/* ignore rrsigs */
|
||||
return parse_edns_options_from_query(rdata_ptr, rdata_len, edns, cfg,
|
||||
c, region);
|
||||
c, repinfo, now, region);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -72,6 +72,7 @@ struct regional;
|
||||
struct edns_option;
|
||||
struct config_file;
|
||||
struct comm_point;
|
||||
struct comm_reply;
|
||||
|
||||
/** number of buckets in parse rrset hash table. Must be power of 2. */
|
||||
#define PARSE_TABLE_SIZE 32
|
||||
@ -217,8 +218,6 @@ struct rr_parse {
|
||||
* region.
|
||||
*/
|
||||
struct edns_data {
|
||||
/** if EDNS OPT record was present */
|
||||
int edns_present;
|
||||
/** Extended RCODE */
|
||||
uint8_t ext_rcode;
|
||||
/** The EDNS version number */
|
||||
@ -238,7 +237,13 @@ struct edns_data {
|
||||
struct edns_option* opt_list_inplace_cb_out;
|
||||
/** block size to pad */
|
||||
uint16_t padding_block_size;
|
||||
};
|
||||
/** if EDNS OPT record was present */
|
||||
unsigned int edns_present : 1;
|
||||
/** if a cookie was present */
|
||||
unsigned int cookie_present : 1;
|
||||
/** if the cookie validated */
|
||||
unsigned int cookie_valid : 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* EDNS option
|
||||
@ -315,7 +320,8 @@ int skip_pkt_rrs(struct sldns_buffer* pkt, int num);
|
||||
* RCODE formerr if OPT is badly formatted and so on.
|
||||
*/
|
||||
int parse_edns_from_query_pkt(struct sldns_buffer* pkt, struct edns_data* edns,
|
||||
struct config_file* cfg, struct comm_point* c, struct regional* region);
|
||||
struct config_file* cfg, struct comm_point* c,
|
||||
struct comm_reply* repinfo, time_t now, struct regional* region);
|
||||
|
||||
/**
|
||||
* Calculate hash value for rrset in packet.
|
||||
|
165
util/siphash.c
Normal file
165
util/siphash.c
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
SipHash reference C implementation
|
||||
|
||||
Copyright (c) 2012-2016 Jean-Philippe Aumasson
|
||||
<jeanphilippe.aumasson@gmail.com>
|
||||
Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication along
|
||||
with
|
||||
this software. If not, see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* default: SipHash-2-4 */
|
||||
#define cROUNDS 2
|
||||
#define dROUNDS 4
|
||||
|
||||
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
|
||||
|
||||
#define U32TO8_LE(p, v) \
|
||||
(p)[0] = (uint8_t)((v)); \
|
||||
(p)[1] = (uint8_t)((v) >> 8); \
|
||||
(p)[2] = (uint8_t)((v) >> 16); \
|
||||
(p)[3] = (uint8_t)((v) >> 24);
|
||||
|
||||
#define U64TO8_LE(p, v) \
|
||||
U32TO8_LE((p), (uint32_t)((v))); \
|
||||
U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
|
||||
|
||||
#define U8TO64_LE(p) \
|
||||
(((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
|
||||
((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
|
||||
((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
|
||||
((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
|
||||
|
||||
#define SIPROUND \
|
||||
do { \
|
||||
v0 += v1; \
|
||||
v1 = ROTL(v1, 13); \
|
||||
v1 ^= v0; \
|
||||
v0 = ROTL(v0, 32); \
|
||||
v2 += v3; \
|
||||
v3 = ROTL(v3, 16); \
|
||||
v3 ^= v2; \
|
||||
v0 += v3; \
|
||||
v3 = ROTL(v3, 21); \
|
||||
v3 ^= v0; \
|
||||
v2 += v1; \
|
||||
v1 = ROTL(v1, 17); \
|
||||
v1 ^= v2; \
|
||||
v2 = ROTL(v2, 32); \
|
||||
} while (0)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define TRACE \
|
||||
do { \
|
||||
printf("(%3d) v0 %08x %08x\n", (int)inlen, (uint32_t)(v0 >> 32), \
|
||||
(uint32_t)v0); \
|
||||
printf("(%3d) v1 %08x %08x\n", (int)inlen, (uint32_t)(v1 >> 32), \
|
||||
(uint32_t)v1); \
|
||||
printf("(%3d) v2 %08x %08x\n", (int)inlen, (uint32_t)(v2 >> 32), \
|
||||
(uint32_t)v2); \
|
||||
printf("(%3d) v3 %08x %08x\n", (int)inlen, (uint32_t)(v3 >> 32), \
|
||||
(uint32_t)v3); \
|
||||
} while (0)
|
||||
#else
|
||||
#define TRACE
|
||||
#endif
|
||||
|
||||
int siphash(const uint8_t *in, const size_t inlen, const uint8_t *k,
|
||||
uint8_t *out, const size_t outlen) {
|
||||
|
||||
assert((outlen == 8) || (outlen == 16));
|
||||
uint64_t v0 = 0x736f6d6570736575ULL;
|
||||
uint64_t v1 = 0x646f72616e646f6dULL;
|
||||
uint64_t v2 = 0x6c7967656e657261ULL;
|
||||
uint64_t v3 = 0x7465646279746573ULL;
|
||||
uint64_t k0 = U8TO64_LE(k);
|
||||
uint64_t k1 = U8TO64_LE(k + 8);
|
||||
uint64_t m;
|
||||
int i;
|
||||
const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));
|
||||
const int left = inlen & 7;
|
||||
uint64_t b = ((uint64_t)inlen) << 56;
|
||||
v3 ^= k1;
|
||||
v2 ^= k0;
|
||||
v1 ^= k1;
|
||||
v0 ^= k0;
|
||||
|
||||
if (outlen == 16)
|
||||
v1 ^= 0xee;
|
||||
|
||||
for (; in != end; in += 8) {
|
||||
m = U8TO64_LE(in);
|
||||
v3 ^= m;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < cROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
v0 ^= m;
|
||||
}
|
||||
|
||||
switch (left) {
|
||||
case 7:
|
||||
b |= ((uint64_t)in[6]) << 48;
|
||||
case 6:
|
||||
b |= ((uint64_t)in[5]) << 40;
|
||||
case 5:
|
||||
b |= ((uint64_t)in[4]) << 32;
|
||||
case 4:
|
||||
b |= ((uint64_t)in[3]) << 24;
|
||||
case 3:
|
||||
b |= ((uint64_t)in[2]) << 16;
|
||||
case 2:
|
||||
b |= ((uint64_t)in[1]) << 8;
|
||||
case 1:
|
||||
b |= ((uint64_t)in[0]);
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
|
||||
v3 ^= b;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < cROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
v0 ^= b;
|
||||
|
||||
if (outlen == 16)
|
||||
v2 ^= 0xee;
|
||||
else
|
||||
v2 ^= 0xff;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < dROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
b = v0 ^ v1 ^ v2 ^ v3;
|
||||
U64TO8_LE(out, b);
|
||||
|
||||
if (outlen == 8)
|
||||
return 0;
|
||||
|
||||
v1 ^= 0xdd;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < dROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
b = v0 ^ v1 ^ v2 ^ v3;
|
||||
U64TO8_LE(out + 8, b);
|
||||
|
||||
return 0;
|
||||
}
|
@ -2376,6 +2376,8 @@ probe_anchor(struct module_env* env, struct trust_anchor* tp)
|
||||
edns.opt_list_out = NULL;
|
||||
edns.opt_list_inplace_cb_out = NULL;
|
||||
edns.padding_block_size = 0;
|
||||
edns.cookie_present = 0;
|
||||
edns.cookie_valid = 0;
|
||||
if(sldns_buffer_capacity(buf) < 65535)
|
||||
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
|
||||
else edns.udp_size = 65535;
|
||||
|
Loading…
Reference in New Issue
Block a user