1 Star 0 Fork 4

ethanehuang/unbound

forked from OpenCloudOS Stream/unbound 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
backport-pre-CVE-2024-33655-Downstream-DNS-Cookies-a-la-RFC7873-and-RFC9018.patch 32.49 KB
一键复制 编辑 原始数据 按行查看 历史
ethanehuang 提交于 2024-07-31 14:58 . fix CVE-2024-33655
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
From 75f3fbdd6563dd87c93964e48a3fb7e6c520d74e Mon Sep 17 00:00:00 2001
From: Willem Toorop <willem@nlnetlabs.nl>
Date: Wed, 28 Sep 2022 10:28:19 +0200
Subject: [PATCH] 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.
---
Makefile.in | 8 +-
daemon/acl_list.c | 2 +
daemon/acl_list.h | 4 +-
daemon/worker.c | 44 ++++++++++-
doc/unbound.conf.5.in | 23 +++++-
libunbound/libworker.c | 2 +
services/authzone.c | 4 +
sldns/rrdef.h | 4 +
testcode/fake_event.c | 2 +
util/config_file.c | 23 ++++++
util/config_file.h | 7 ++
util/configlexer.lex | 2 +
util/configparser.y | 35 ++++++++-
util/data/msgparse.c | 151 ++++++++++++++++++++++++++++++++++++-
util/data/msgparse.h | 14 +++-
util/siphash.c | 165 +++++++++++++++++++++++++++++++++++++++++
validator/autotrust.c | 2 +
17 files changed, 473 insertions(+), 19 deletions(-)
create mode 100644 util/siphash.c
diff --git a/Makefile.in b/Makefile.in
index 3189731ad..718f47f5c 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -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/proxy_protocol.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 proxy_protocol.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 \
diff --git a/daemon/acl_list.c b/daemon/acl_list.c
index 8e8e1fc9b..e6d0470a1 100644
--- a/daemon/acl_list.c
+++ b/daemon/acl_list.c
@@ -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;
diff --git a/daemon/acl_list.h b/daemon/acl_list.h
index c717179ba..8aece11bf 100644
--- a/daemon/acl_list.h
+++ b/daemon/acl_list.h
@@ -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
};
/**
diff --git a/daemon/worker.c b/daemon/worker.c
index c2a94be79..1abf20a7b 100644
--- a/daemon/worker.c
+++ b/daemon/worker.c
@@ -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->client_addr,
@@ -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->remote_addr, repinfo->remote_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,
diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in
index 73575d93a..e187452de 100644
--- a/doc/unbound.conf.5.in
+++ b/doc/unbound.conf.5.in
@@ -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
diff --git a/libunbound/libworker.c b/libunbound/libworker.c
index 11bf5f9db..d5fabe6fb 100644
--- a/libunbound/libworker.c
+++ b/libunbound/libworker.c
@@ -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);
diff --git a/services/authzone.c b/services/authzone.c
index 6de1e4319..079a7eaf1 100644
--- a/services/authzone.c
+++ b/services/authzone.c
@@ -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;
diff --git a/sldns/rrdef.h b/sldns/rrdef.h
index 999c22307..d5b0585fd 100644
--- a/sldns/rrdef.h
+++ b/sldns/rrdef.h
@@ -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.
*
diff --git a/testcode/fake_event.c b/testcode/fake_event.c
index 03e1c04f3..c93ceaa4c 100644
--- a/testcode/fake_event.c
+++ b/testcode/fake_event.c
@@ -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;
diff --git a/util/config_file.c b/util/config_file.c
index 158169c42..a92342761 100644
--- a/util/config_file.c
+++ b/util/config_file.c
@@ -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)
{
diff --git a/util/config_file.h b/util/config_file.h
index bbc6d4ac1..3db4676b9 100644
--- a/util/config_file.h
+++ b/util/config_file.h
@@ -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
diff --git a/util/configlexer.lex b/util/configlexer.lex
index fc9aa7266..4fdb2cde0 100644
--- a/util/configlexer.lex
+++ b/util/configlexer.lex
@@ -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 ) }
diff --git a/util/configparser.y b/util/configparser.y
index 8f3672f5d..980201460 100644
--- a/util/configparser.y
+++ b/util/configparser.y
@@ -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");
}
}
diff --git a/util/data/msgparse.c b/util/data/msgparse.c
index 5bb69d6ed..3059d555c 100644
--- a/util/data/msgparse.c
+++ b/util/data/msgparse.c
@@ -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->remote_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->remote_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
diff --git a/util/data/msgparse.h b/util/data/msgparse.h
index 0c458e6e8..aebeb2a88 100644
--- a/util/data/msgparse.h
+++ b/util/data/msgparse.h
@@ -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.
diff --git a/util/siphash.c b/util/siphash.c
new file mode 100644
index 000000000..d69f4b579
--- /dev/null
+++ b/util/siphash.c
@@ -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;
+}
diff --git a/validator/autotrust.c b/validator/autotrust.c
index 3cdf9ceae..3011a0ace 100644
--- a/validator/autotrust.c
+++ b/validator/autotrust.c
@@ -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;
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/ethanehuang/unbound.git
git@gitee.com:ethanehuang/unbound.git
ethanehuang
unbound
unbound
master

搜索帮助