mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-01-16 19:52:55 -05:00
- For #762: relocate EDNS cookie code to util/edns and introduce unit
tests.
This commit is contained in:
parent
6e47c1e05b
commit
702f485587
4 changed files with 345 additions and 78 deletions
|
|
@ -530,6 +530,208 @@ infra_test(void)
|
|||
config_delete(cfg);
|
||||
}
|
||||
|
||||
#include "util/edns.h"
|
||||
/* Complete version-invalid client cookie; needs a new one.
|
||||
* Based on edns_cookie_rfc9018_a2 */
|
||||
static void
|
||||
edns_cookie_invalid_version(void)
|
||||
{
|
||||
uint32_t timestamp = 1559734385;
|
||||
uint8_t client_cookie[] = {
|
||||
0x24, 0x64, 0xc4, 0xab, 0xcf, 0x10, 0xc9, 0x57,
|
||||
0x99, 0x00, 0x00, 0x00,
|
||||
0x5c, 0xf7, 0x9f, 0x11,
|
||||
0x1f, 0x81, 0x30, 0xc3, 0xee, 0xe2, 0x94, 0x80 };
|
||||
uint8_t server_cookie[] = {
|
||||
0x24, 0x64, 0xc4, 0xab, 0xcf, 0x10, 0xc9, 0x57,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x5c, 0xf7, 0xa8, 0x71,
|
||||
0xd4, 0xa5, 0x64, 0xa1, 0x44, 0x2a, 0xca, 0x77 };
|
||||
uint8_t server_secret[] = {
|
||||
0xe5, 0xe9, 0x73, 0xe5, 0xa6, 0xb2, 0xa4, 0x3f,
|
||||
0x48, 0xe7, 0xdc, 0x84, 0x9e, 0x37, 0xbf, 0xcf };
|
||||
uint8_t buf[32];
|
||||
/* copy client cookie|version|reserved|timestamp */
|
||||
memcpy(buf, client_cookie, 8 + 4 + 4);
|
||||
/* copy ip 198.51.100.100 */
|
||||
memcpy(buf + 16, "\306\063\144\144", 4);
|
||||
unit_assert(edns_cookie_server_validate(client_cookie,
|
||||
sizeof(client_cookie), server_secret, sizeof(server_secret), 1,
|
||||
buf, timestamp) == 0);
|
||||
edns_cookie_server_write(buf, server_secret, 1, timestamp);
|
||||
log_hex("server:", buf, 32);
|
||||
unit_assert(memcmp(server_cookie, buf, 24) == 0);
|
||||
}
|
||||
|
||||
/* Complete hash-invalid client cookie; needs a new one. */
|
||||
static void
|
||||
edns_cookie_invalid_hash(void)
|
||||
{
|
||||
uint32_t timestamp = 0;
|
||||
uint8_t client_cookie[] = {
|
||||
0xfc, 0x93, 0xfc, 0x62, 0x80, 0x7d, 0xdb, 0x86,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x32, 0xF2, 0x43, 0xB9, 0xBC, 0xFE, 0xC4, 0x06 };
|
||||
uint8_t server_cookie[] = {
|
||||
0xfc, 0x93, 0xfc, 0x62, 0x80, 0x7d, 0xdb, 0x86,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0xBA, 0x0D, 0x82, 0x90, 0x8F, 0xAA, 0xEB, 0xBD };
|
||||
uint8_t server_secret[] = {
|
||||
0xe5, 0xe9, 0x73, 0xe5, 0xa6, 0xb2, 0xa4, 0x3f,
|
||||
0x48, 0xe7, 0xdc, 0x84, 0x9e, 0x37, 0xbf, 0xcf };
|
||||
uint8_t buf[32];
|
||||
/* copy client cookie|version|reserved|timestamp */
|
||||
memcpy(buf, client_cookie, 8 + 4 + 4);
|
||||
/* copy ip 203.0.113.203 */
|
||||
memcpy(buf + 16, "\313\000\161\313", 4);
|
||||
unit_assert(edns_cookie_server_validate(client_cookie,
|
||||
sizeof(client_cookie), server_secret, sizeof(server_secret), 1,
|
||||
buf, timestamp) == 0);
|
||||
edns_cookie_server_write(buf, server_secret, 1, timestamp);
|
||||
unit_assert(memcmp(server_cookie, buf, 24) == 0);
|
||||
}
|
||||
|
||||
/* Complete hash-valid client cookie; more than 30 minutes old; needs a
|
||||
* refreshed server cookie.
|
||||
* A slightly better variation of edns_cookie_rfc9018_a3 for Unbound to check
|
||||
* that RESERVED bits do not influence cookie validation. */
|
||||
static void
|
||||
edns_cookie_rfc9018_a3_better(void)
|
||||
{
|
||||
uint32_t timestamp = 1800 + 1;
|
||||
uint8_t client_cookie[] = {
|
||||
0xfc, 0x93, 0xfc, 0x62, 0x80, 0x7d, 0xdb, 0x86,
|
||||
0x01, 0xab, 0xcd, 0xef,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x32, 0xF2, 0x43, 0xB9, 0xBC, 0xFE, 0xC4, 0x06 };
|
||||
uint8_t server_cookie[] = {
|
||||
0xfc, 0x93, 0xfc, 0x62, 0x80, 0x7d, 0xdb, 0x86,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x07, 0x09,
|
||||
0x62, 0xD5, 0x93, 0x09, 0x14, 0x5C, 0x23, 0x9D };
|
||||
uint8_t server_secret[] = {
|
||||
0xe5, 0xe9, 0x73, 0xe5, 0xa6, 0xb2, 0xa4, 0x3f,
|
||||
0x48, 0xe7, 0xdc, 0x84, 0x9e, 0x37, 0xbf, 0xcf };
|
||||
uint8_t buf[32];
|
||||
/* copy client cookie|version|reserved|timestamp */
|
||||
memcpy(buf, client_cookie, 8 + 4 + 4);
|
||||
/* copy ip 203.0.113.203 */
|
||||
memcpy(buf + 16, "\313\000\161\313", 4);
|
||||
unit_assert(edns_cookie_server_validate(client_cookie,
|
||||
sizeof(client_cookie), server_secret, sizeof(server_secret), 1,
|
||||
buf, timestamp) == -1);
|
||||
edns_cookie_server_write(buf, server_secret, 1, timestamp);
|
||||
unit_assert(memcmp(server_cookie, buf, 24) == 0);
|
||||
}
|
||||
|
||||
/* Complete hash-valid client cookie; more than 60 minutes old; needs a
|
||||
* refreshed server cookie. */
|
||||
static void
|
||||
edns_cookie_rfc9018_a3(void)
|
||||
{
|
||||
uint32_t timestamp = 1559734700;
|
||||
uint8_t client_cookie[] = {
|
||||
0xfc, 0x93, 0xfc, 0x62, 0x80, 0x7d, 0xdb, 0x86,
|
||||
0x01, 0xab, 0xcd, 0xef,
|
||||
0x5c, 0xf7, 0x8f, 0x71,
|
||||
0xa3, 0x14, 0x22, 0x7b, 0x66, 0x79, 0xeb, 0xf5 };
|
||||
uint8_t server_cookie[] = {
|
||||
0xfc, 0x93, 0xfc, 0x62, 0x80, 0x7d, 0xdb, 0x86,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x5c, 0xf7, 0xa9, 0xac,
|
||||
0xf7, 0x3a, 0x78, 0x10, 0xac, 0xa2, 0x38, 0x1e };
|
||||
uint8_t server_secret[] = {
|
||||
0xe5, 0xe9, 0x73, 0xe5, 0xa6, 0xb2, 0xa4, 0x3f,
|
||||
0x48, 0xe7, 0xdc, 0x84, 0x9e, 0x37, 0xbf, 0xcf };
|
||||
uint8_t buf[32];
|
||||
/* copy client cookie|version|reserved|timestamp */
|
||||
memcpy(buf, client_cookie, 8 + 4 + 4);
|
||||
/* copy ip 203.0.113.203 */
|
||||
memcpy(buf + 16, "\313\000\161\313", 4);
|
||||
unit_assert(edns_cookie_server_validate(client_cookie,
|
||||
sizeof(client_cookie), server_secret, sizeof(server_secret), 1,
|
||||
buf, timestamp) == 0);
|
||||
edns_cookie_server_write(buf, server_secret, 1, timestamp);
|
||||
unit_assert(memcmp(server_cookie, buf, 24) == 0);
|
||||
}
|
||||
|
||||
/* Complete hash-valid client cookie; more than 30 minutes old; needs a
|
||||
* refreshed server cookie. */
|
||||
static void
|
||||
edns_cookie_rfc9018_a2(void)
|
||||
{
|
||||
uint32_t timestamp = 1559734385;
|
||||
uint8_t client_cookie[] = {
|
||||
0x24, 0x64, 0xc4, 0xab, 0xcf, 0x10, 0xc9, 0x57,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x5c, 0xf7, 0x9f, 0x11,
|
||||
0x1f, 0x81, 0x30, 0xc3, 0xee, 0xe2, 0x94, 0x80 };
|
||||
uint8_t server_cookie[] = {
|
||||
0x24, 0x64, 0xc4, 0xab, 0xcf, 0x10, 0xc9, 0x57,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x5c, 0xf7, 0xa8, 0x71,
|
||||
0xd4, 0xa5, 0x64, 0xa1, 0x44, 0x2a, 0xca, 0x77 };
|
||||
uint8_t server_secret[] = {
|
||||
0xe5, 0xe9, 0x73, 0xe5, 0xa6, 0xb2, 0xa4, 0x3f,
|
||||
0x48, 0xe7, 0xdc, 0x84, 0x9e, 0x37, 0xbf, 0xcf };
|
||||
uint8_t buf[32];
|
||||
/* copy client cookie|version|reserved|timestamp */
|
||||
memcpy(buf, client_cookie, 8 + 4 + 4);
|
||||
/* copy ip 198.51.100.100 */
|
||||
memcpy(buf + 16, "\306\063\144\144", 4);
|
||||
unit_assert(edns_cookie_server_validate(client_cookie,
|
||||
sizeof(client_cookie), server_secret, sizeof(server_secret), 1,
|
||||
buf, timestamp) == -1);
|
||||
edns_cookie_server_write(buf, server_secret, 1, timestamp);
|
||||
unit_assert(memcmp(server_cookie, buf, 24) == 0);
|
||||
}
|
||||
|
||||
/* Only client cookie; needs a complete server cookie. */
|
||||
static void
|
||||
edns_cookie_rfc9018_a1(void)
|
||||
{
|
||||
uint32_t timestamp = 1559731985;
|
||||
uint8_t client_cookie[] = {
|
||||
0x24, 0x64, 0xc4, 0xab, 0xcf, 0x10, 0xc9, 0x57 };
|
||||
uint8_t server_cookie[] = {
|
||||
0x24, 0x64, 0xc4, 0xab, 0xcf, 0x10, 0xc9, 0x57,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x5c, 0xf7, 0x9f, 0x11,
|
||||
0x1f, 0x81, 0x30, 0xc3, 0xee, 0xe2, 0x94, 0x80 };
|
||||
uint8_t server_secret[] = {
|
||||
0xe5, 0xe9, 0x73, 0xe5, 0xa6, 0xb2, 0xa4, 0x3f,
|
||||
0x48, 0xe7, 0xdc, 0x84, 0x9e, 0x37, 0xbf, 0xcf };
|
||||
uint8_t buf[32];
|
||||
/* copy client cookie|version|reserved|timestamp */
|
||||
memcpy(buf, server_cookie, 8 + 4 + 4);
|
||||
/* copy ip 198.51.100.100 */
|
||||
memcpy(buf + 16, "\306\063\144\144", 4);
|
||||
unit_assert(edns_cookie_server_validate(client_cookie,
|
||||
sizeof(client_cookie),
|
||||
/* these will not be used; it will return invalid
|
||||
* because of the size. */
|
||||
NULL, 0, 1, NULL, 0) == 0);
|
||||
edns_cookie_server_write(buf, server_secret, 1, timestamp);
|
||||
unit_assert(memcmp(server_cookie, buf, 24) == 0);
|
||||
}
|
||||
|
||||
/** test interoperable EDNS cookies (RFC9018) */
|
||||
static void
|
||||
edns_cookie_test(void)
|
||||
{
|
||||
unit_show_feature("interoperable edns cookies");
|
||||
/* Check RFC9018 appendix test vectors */
|
||||
edns_cookie_rfc9018_a1();
|
||||
edns_cookie_rfc9018_a2();
|
||||
edns_cookie_rfc9018_a3();
|
||||
/* More tests */
|
||||
edns_cookie_rfc9018_a3_better();
|
||||
edns_cookie_invalid_hash();
|
||||
edns_cookie_invalid_version();
|
||||
}
|
||||
|
||||
#include "util/random.h"
|
||||
/** test randomness */
|
||||
static void
|
||||
|
|
@ -906,6 +1108,7 @@ main(int argc, char* argv[])
|
|||
slabhash_test();
|
||||
infra_test();
|
||||
ldns_test();
|
||||
edns_cookie_test();
|
||||
zonemd_test();
|
||||
tcpreuse_test();
|
||||
msgparse_test();
|
||||
|
|
|
|||
|
|
@ -43,10 +43,10 @@
|
|||
#include "util/data/dname.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/siphash.h"
|
||||
#include "util/storage/lookup3.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/rfc_1982.h"
|
||||
#include "util/edns.h"
|
||||
#include "sldns/rrdef.h"
|
||||
#include "sldns/sbuffer.h"
|
||||
#include "sldns/parseutil.h"
|
||||
|
|
@ -942,21 +942,6 @@ parse_packet(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region)
|
|||
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,
|
||||
|
|
@ -985,9 +970,9 @@ 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;
|
||||
uint8_t server_cookie[40];
|
||||
int cookie_is_valid;
|
||||
int cookie_is_v4 = 1;
|
||||
|
||||
rdata_ptr += 4;
|
||||
rdata_len -= 4;
|
||||
|
|
@ -1052,11 +1037,11 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
|||
break;
|
||||
|
||||
case LDNS_EDNS_COOKIE:
|
||||
if(!cfg || !cfg->do_answer_cookie)
|
||||
if(!cfg || !cfg->do_answer_cookie || !repinfo)
|
||||
break;
|
||||
if(opt_len != 8 && (opt_len < 16 || opt_len > 40)) {
|
||||
verbose(VERB_ALGO, "worker request: "
|
||||
"badly formatted cookie");
|
||||
"badly formatted cookie");
|
||||
return LDNS_RCODE_FORMERR;
|
||||
}
|
||||
edns->cookie_present = 1;
|
||||
|
|
@ -1066,67 +1051,41 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
|||
*/
|
||||
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.
|
||||
/* Copy client ip for validation and creation
|
||||
* purposes. It will be overwritten if (re)creation
|
||||
* is needed.
|
||||
*/
|
||||
if (opt_len != 24)
|
||||
; /* RFC9018 cookies are 24 bytes long */
|
||||
if(repinfo->remote_addr.ss_family == AF_INET) {
|
||||
memcpy(server_cookie + 16,
|
||||
&((struct sockaddr_in*)&repinfo->remote_addr)->sin_addr, 4);
|
||||
} else {
|
||||
cookie_is_v4 = 0;
|
||||
memcpy(server_cookie + 16,
|
||||
&((struct sockaddr_in6*)&repinfo->remote_addr)->sin6_addr, 16);
|
||||
}
|
||||
|
||||
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)) {
|
||||
cookie_is_valid = edns_cookie_server_validate(
|
||||
rdata_ptr, opt_len, cfg->cookie_secret,
|
||||
cfg->cookie_secret_len, cookie_is_v4,
|
||||
server_cookie, now);
|
||||
if(cookie_is_valid != 0) edns->cookie_valid = 1;
|
||||
if(cookie_is_valid == 1) {
|
||||
/* Reuse cookie */
|
||||
if(!edns_opt_list_append(
|
||||
&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;
|
||||
}
|
||||
/* 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)) {
|
||||
edns_cookie_server_write(server_cookie,
|
||||
cfg->cookie_secret, cookie_is_v4, now);
|
||||
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;
|
||||
}
|
||||
|
|
@ -1309,7 +1268,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, repinfo, now, region);
|
||||
c, repinfo, now, region);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
56
util/edns.c
56
util/edns.c
|
|
@ -45,8 +45,11 @@
|
|||
#include "util/netevent.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/rfc_1982.h"
|
||||
#include "util/siphash.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "sldns/sbuffer.h"
|
||||
|
||||
struct edns_strings* edns_strings_create(void)
|
||||
{
|
||||
|
|
@ -128,3 +131,56 @@ edns_string_addr_lookup(rbtree_type* tree, struct sockaddr_storage* addr,
|
|||
return (struct edns_string_addr*)addr_tree_lookup(tree, addr, addrlen);
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
edns_cookie_server_hash(const uint8_t* in, const uint8_t* secret, int v4,
|
||||
uint8_t* hash)
|
||||
{
|
||||
v4?siphash(in, 20, secret, hash, 8):siphash(in, 32, secret, hash, 8);
|
||||
return hash;
|
||||
}
|
||||
|
||||
void
|
||||
edns_cookie_server_write(uint8_t* buf, const uint8_t* secret, int v4,
|
||||
uint32_t timestamp)
|
||||
{
|
||||
uint8_t hash[8];
|
||||
buf[ 8] = 1; /* Version */
|
||||
buf[ 9] = 0; /* Reserved */
|
||||
buf[10] = 0; /* Reserved */
|
||||
buf[11] = 0; /* Reserved */
|
||||
sldns_write_uint32(buf + 12, timestamp);
|
||||
(void)edns_cookie_server_hash(buf, secret, v4, hash);
|
||||
memcpy(buf + 16, hash, 8);
|
||||
}
|
||||
|
||||
int
|
||||
edns_cookie_server_validate(const uint8_t* cookie, size_t cookie_len,
|
||||
const uint8_t* secret, size_t secret_len, int v4,
|
||||
const uint8_t* hash_input, uint32_t now)
|
||||
{
|
||||
uint8_t hash[8];
|
||||
uint32_t timestamp, subt_1982;
|
||||
int comp_1982;
|
||||
if(cookie_len != 24 || /* RFC9018 cookies are 24 bytes long */
|
||||
secret_len != 16 || /* RFC9018 cookies have 16 byte secrets */
|
||||
cookie[8] != 1) /* RFC9018 cookies are cookie version 1 */
|
||||
return 0;
|
||||
timestamp = sldns_read_uint32(cookie + 12);
|
||||
if((comp_1982 = compare_1982(now, timestamp)) > 0
|
||||
&& (subt_1982 = subtract_1982(timestamp, now)) > 3600)
|
||||
/* Cookie is older than 1 hour (see RFC9018 Section 4.3.) */
|
||||
return 0;
|
||||
if(comp_1982 <= 0 && subtract_1982(now, timestamp) > 300)
|
||||
/* Cookie time is more than 5 minutes in the future.
|
||||
* (see RFC9018 Section 4.3.) */
|
||||
return 0;
|
||||
if(memcmp(edns_cookie_server_hash(hash_input, secret, v4, hash),
|
||||
cookie + 16, 8) != 0)
|
||||
/* Hashes do not match */
|
||||
return 0;
|
||||
if(comp_1982 > 0 && subt_1982 > 1800)
|
||||
/* Valid cookie but older than 30 minutes, so create a new one
|
||||
* anyway */
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
49
util/edns.h
49
util/edns.h
|
|
@ -106,4 +106,53 @@ struct edns_string_addr*
|
|||
edns_string_addr_lookup(rbtree_type* tree, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen);
|
||||
|
||||
/**
|
||||
* Compute the interoperable EDNS cookie (RFC9018) hash.
|
||||
* @param in: buffer input for the hash generation. It needs to be:
|
||||
* Client Cookie | Version | Reserved | Timestamp | Client-IP
|
||||
* @param secret: the server secret; implicit length of 16 octets.
|
||||
* @param v4: if the client IP is v4 or v6.
|
||||
* @param hash: buffer to write the hash to.
|
||||
* return a pointer to the hash.
|
||||
*/
|
||||
uint8_t* edns_cookie_server_hash(const uint8_t* in, const uint8_t* secret,
|
||||
int v4, uint8_t* hash);
|
||||
|
||||
/**
|
||||
* Write an interoperable EDNS server cookie (RFC9018).
|
||||
* @param buf: buffer to write to. It should have a size of at least 32 octets
|
||||
* as it doubles as the output buffer and the hash input buffer.
|
||||
* The first 8 octets are expected to be the Client Cookie and will be
|
||||
* left untouched.
|
||||
* The next 8 octets will be written with Version | Reserved | Timestamp.
|
||||
* The next 4 or 16 octets are expected to be the IPv4 or the IPv6 address
|
||||
* based on the v4 flag.
|
||||
* Thus the first 20 or 32 octets, based on the v4 flag, will be used as
|
||||
* the hash input.
|
||||
* The server hash (8 octets) will be written after the first 16 octets;
|
||||
* overwriting the address information.
|
||||
* The caller expects a complete, 24 octet long cookie in the buffer.
|
||||
* @param secret: the server secret; implicit length of 16 octets.
|
||||
* @param v4: if the client IP is v4 or v6.
|
||||
* @param timestamp: the timestamp to use.
|
||||
*/
|
||||
void edns_cookie_server_write(uint8_t* buf, const uint8_t* secret, int v4,
|
||||
uint32_t timestamp);
|
||||
|
||||
/**
|
||||
* Validate an interoperable EDNS cookie (RFC9018).
|
||||
* @param cookie: pointer to the cookie data.
|
||||
* @param cookie_len: the length of the cookie data.
|
||||
* @param secret: pointer to the server secret.
|
||||
* @param secret_len: the length of the secret.
|
||||
* @param v4: if the client IP is v4 or v6.
|
||||
* @param hash_input: pointer to the hash input for validation. It needs to be:
|
||||
* Client Cookie | Version | Reserved | Timestamp | Client-IP
|
||||
* @param now: the current time.
|
||||
* return 1 if valid, -1 if valid but a new one SHOULD be generated, else 0.
|
||||
*/
|
||||
int edns_cookie_server_validate(const uint8_t* cookie, size_t cookie_len,
|
||||
const uint8_t* secret, size_t secret_len, int v4,
|
||||
const uint8_t* hash_input, uint32_t now);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue