- For #762: Introduce rpl testing for DNS Cookies.

This commit is contained in:
George Thessalonikefs 2023-08-05 19:50:57 +02:00
parent b6e2f4dbf8
commit 8580a74b37
4 changed files with 312 additions and 15 deletions

View File

@ -21,7 +21,6 @@
*/
#include "config.h"
struct sockaddr_storage;
#include <errno.h>
#include <stdarg.h>
#include <ctype.h>
@ -140,6 +139,10 @@ static void matchline(char* line, struct entry* e)
e->match_noedns = 1;
} else if(str_keyword(&parse, "ednsdata")) {
e->match_ednsdata_raw = 1;
} else if(str_keyword(&parse, "client_cookie")) {
e->match_client_cookie = 1;
} else if(str_keyword(&parse, "server_cookie")) {
e->match_server_cookie = 1;
} else if(str_keyword(&parse, "UDP")) {
e->match_transport = transport_udp;
} else if(str_keyword(&parse, "TCP")) {
@ -905,37 +908,64 @@ get_do_flag(uint8_t* pkt, size_t len)
return (int)(edns_bits&LDNS_EDNS_MASK_DO_BIT);
}
/** Snips the EDE option out of the OPT record and returns the EDNS EDE
* INFO-CODE if found, else -1 */
/** Snips the specified EDNS option out of the OPT record and puts it in the
* provided buffer. The buffer should be able to hold any opt data ie 65535.
* Returns the length of the option written,
* or 0 if not found, else -1 on error. */
static int
extract_ede(uint8_t* pkt, size_t len)
pkt_snip_edns_option(uint8_t* pkt, size_t len, sldns_edns_option code,
uint8_t* buf)
{
uint8_t *rdata, *opt_position = pkt;
uint16_t rdlen, optlen;
size_t remaining = len;
int ede_code;
if(!pkt_find_edns_opt(&opt_position, &remaining)) return -1;
if(!pkt_find_edns_opt(&opt_position, &remaining)) return 0;
if(remaining < 8) return -1; /* malformed */
rdlen = sldns_read_uint16(opt_position+6);
rdata = opt_position + 8;
while(rdlen > 0) {
if(rdlen < 4) return -1; /* malformed */
optlen = sldns_read_uint16(rdata+2);
if(sldns_read_uint16(rdata) == LDNS_EDNS_EDE) {
if(rdlen < 6) return -1; /* malformed */
ede_code = sldns_read_uint16(rdata+4);
if(sldns_read_uint16(rdata) == code) {
/* save data to buf for caller inspection */
memcpy(buf, rdata+4, optlen);
/* snip option from packet; assumes len is correct */
memmove(rdata, rdata+4+optlen,
(pkt+len)-(rdata+4+optlen));
/* update OPT size */
sldns_write_uint16(opt_position+6,
sldns_read_uint16(opt_position+6)-(4+optlen));
return ede_code;
return optlen;
}
rdlen -= 4 + optlen;
rdata += 4 + optlen;
}
return -1;
return 0;
}
/** Snips the EDE option out of the OPT record and returns the EDNS EDE
* INFO-CODE if found, else -1 */
static int
extract_ede(uint8_t* pkt, size_t len)
{
uint8_t buf[65535];
int buflen = pkt_snip_edns_option(pkt, len, LDNS_EDNS_EDE, buf);
if(buflen < 2 /*ede without text at minimum*/) return -1;
return sldns_read_uint16(buf);
}
/** Snips the EDNS Cookie option out of the OPT record and puts it in the
* provided cookie buffer (should be at least 24 octets).
* Returns the length of the cookie if found, else -1. */
static int
extract_cookie(uint8_t* pkt, size_t len, uint8_t* cookie)
{
uint8_t buf[65535];
int buflen = pkt_snip_edns_option(pkt, len, LDNS_EDNS_COOKIE, buf);
if(buflen != 8 /*client cookie*/ &&
buflen != 8 + 16 /*server cookie*/) return -1;
memcpy(cookie, buf, buflen);
return buflen;
}
/** zero TTLs in packet */
@ -1530,6 +1560,27 @@ find_match(struct entry* entries, uint8_t* query_pkt, size_t len,
continue;
}
}
/* Cookies could also modify the query_pkt; keep them early */
if(p->match_client_cookie || p->match_server_cookie) {
uint8_t cookie[24];
int cookie_len = extract_cookie(query_pkt, len,
cookie);
if(cookie_len == -1) {
verbose(3, "bad EDNS Cookie. "
"Expected but not found\n");
continue;
} else if(p->match_client_cookie &&
cookie_len != 8) {
verbose(3, "bad EDNS Cookie. Expected client "
"cookie of length 8.");
continue;
} else if((p->match_server_cookie) &&
cookie_len != 24) {
verbose(3, "bad EDNS Cookie. Expected server "
"cookie of length 24.");
continue;
}
}
if(p->match_opcode && get_opcode(query_pkt, len) !=
get_opcode(reply, rlen)) {
verbose(3, "bad opcode\n");

View File

@ -64,6 +64,14 @@ struct sldns_file_parse_state;
; 'ede=any' makes the query match any EDNS EDE info-code.
; It also snips the EDE record out of the packet to facilitate
; other matches.
; 'client_cookie' makes the query match any EDNS Cookie option with
; with a length of 8 octets.
; It also snips the EDNS Cookie record out of the packet to
; facilitate other matches.
; 'server_cookie' makes the query match any EDNS Cookie option with
; with a length of 24 octets.
; It also snips the EDNS Cookie record out of the packet to
; facilitate other matches.
MATCH [opcode] [qtype] [qname] [serial=<value>] [all] [ttl]
MATCH [UDP|TCP] DO
MATCH ...
@ -104,11 +112,11 @@ struct sldns_file_parse_state;
; be parsed, ADJUST rules for the answer packet
; are ignored. Only copy_id is done.
HEX_ANSWER_END
HEX_EDNS_BEGIN ; follow with hex data.
HEX_EDNSDATA_BEGIN ; follow with hex data.
; Raw EDNS data to match against. It must be an
; exact match (all options are matched) and will be
; evaluated only when 'MATCH ednsdata' given.
HEX_EDNS_END
HEX_EDNSDATA_END
ENTRY_END
@ -214,6 +222,10 @@ struct entry {
uint8_t match_noedns;
/** match edns data field given in hex */
uint8_t match_ednsdata_raw;
/** match an EDNS cookie of length 8 */
uint8_t match_client_cookie;
/** match an EDNS cookie of length 24 */
uint8_t match_server_cookie;
/** match query serial with this value. */
uint32_t ixfr_soa_serial;
/** match on UDP/TCP */
@ -235,7 +247,7 @@ struct entry {
/** increment the ECS scope copied from the sourcemask by one */
uint8_t increment_ecs_scope;
/** in seconds */
unsigned int sleeptime;
unsigned int sleeptime;
/** some number that names this entry, line number in file or so */
int lineno;

View File

@ -559,7 +559,6 @@ edns_cookie_invalid_version(void)
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);
}

235
testdata/edns_downstream_cookies.rpl vendored Normal file
View File

@ -0,0 +1,235 @@
; config options
server:
answer-cookie: yes
cookie-secret: "000102030405060708090a0b0c0d0e0f"
access-control: 127.0.0.1 allow_cookie
access-control: 1.2.3.4 allow
local-data: "test. TXT test"
CONFIG_END
SCENARIO_BEGIN Test downstream EDNS Cookies
; Note: When a valid hash was required, it was generated by running this test
; with an invalid one and checking the output for the valid one.
; Actual hash generation is tested with unit tests.
; Query without a client cookie ...
STEP 0 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
test. IN TXT
ENTRY_END
; ... get TC and refused
STEP 1 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
REPLY QR RD RA TC REFUSED
SECTION QUESTION
test. IN TXT
ENTRY_END
; Query without a client cookie on TCP ...
STEP 10 QUERY
ENTRY_BEGIN
REPLY RD
MATCH TCP
SECTION QUESTION
test. IN TXT
ENTRY_END
; ... get an answer
STEP 11 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
REPLY QR RD RA AA NOERROR
SECTION QUESTION
test. IN TXT
SECTION ANSWER
test. IN TXT "test"
ENTRY_END
; Query with only a client cookie ...
STEP 20 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
test. IN TXT
SECTION ADDITIONAL
HEX_EDNSDATA_BEGIN
00 0a ; Opcode 10
00 08 ; Length 8
31 32 33 34 35 36 37 38 ; Random bits
HEX_EDNSDATA_END
ENTRY_END
; ... get BADCOOKIE and a new cookie
STEP 21 CHECK_ANSWER
ENTRY_BEGIN
MATCH all server_cookie
REPLY QR RD RA DO YXRRSET ; BADCOOKIE is an extended rcode
SECTION QUESTION
test. IN TXT
ENTRY_END
; Query with an invalid cookie ...
STEP 30 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
test. IN TXT
SECTION ADDITIONAL
HEX_EDNSDATA_BEGIN
00 0a ; Opcode 10
00 18 ; Length 24
31 32 33 34 35 36 37 38 ; Random bits
02 00 00 00 ; wrong version
00 00 00 00 ; Timestamp
31 32 33 34 35 36 37 38 ; wrong hash
HEX_EDNSDATA_END
ENTRY_END
; ... get BADCOOKIE and a new cookie
STEP 31 CHECK_ANSWER
ENTRY_BEGIN
MATCH all server_cookie
REPLY QR RD RA DO YXRRSET ; BADCOOKIE is an extended rcode
SECTION QUESTION
test. IN TXT
ENTRY_END
; Query with an invalid cookie from a non-cookie protected address ...
STEP 40 QUERY ADDRESS 1.2.3.4
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
test. IN TXT
SECTION ADDITIONAL
HEX_EDNSDATA_BEGIN
00 0a ; Opcode 10
00 18 ; Length 24
31 32 33 34 35 36 37 38 ; Random bits
02 00 00 00 ; wrong version
00 00 00 00 ; Timestamp
31 32 33 34 35 36 37 38 ; wrong hash
HEX_EDNSDATA_END
ENTRY_END
; ... get answer and a cookie
STEP 41 CHECK_ANSWER
ENTRY_BEGIN
MATCH all server_cookie
REPLY QR RD RA AA DO NOERROR
SECTION QUESTION
test. IN TXT
SECTION ANSWER
test. IN TXT "test"
ENTRY_END
; Query with a valid cookie ...
STEP 50 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
test. IN TXT
SECTION ADDITIONAL
HEX_EDNSDATA_BEGIN
00 0a ; Opcode 10
00 18 ; Length 24
31 32 33 34 35 36 37 38 ; Random bits
01 00 00 00 ; wrong version
00 00 00 00 ; Timestamp
38 52 7b a8 c6 a4 ea 96 ; Hash
HEX_EDNSDATA_END
ENTRY_END
; ... get answer and the cookie
STEP 51 CHECK_ANSWER
ENTRY_BEGIN
MATCH all server_cookie
REPLY QR RD RA AA DO NOERROR
SECTION QUESTION
test. IN TXT
SECTION ANSWER
test. IN TXT "test"
ENTRY_END
; Query with a valid >30 minutes old cookie ...
STEP 59 TIME_PASSES ELAPSE 1801
STEP 60 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
test. IN TXT
SECTION ADDITIONAL
HEX_EDNSDATA_BEGIN
00 0a ; Opcode 10
00 18 ; Length 24
31 32 33 34 35 36 37 38 ; Random bits
01 00 00 00 ; Version/Reserved
00 00 00 00 ; Timestamp
38 52 7b a8 c6 a4 ea 96 ; Hash
HEX_EDNSDATA_END
ENTRY_END
; ... Get answer and a refreshed cookie
; (we don't check the re-freshness here; it has its own unit test)
STEP 61 CHECK_ANSWER
ENTRY_BEGIN
MATCH all server_cookie
REPLY QR RD RA AA DO NOERROR
SECTION QUESTION
test. IN TXT
SECTION ANSWER
test. IN TXT "test"
ENTRY_END
; Query with a hash-valid >60 minutes old cookie ...
STEP 69 TIME_PASSES ELAPSE 3601
STEP 70 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
test. IN TXT
SECTION ADDITIONAL
HEX_EDNSDATA_BEGIN
00 0a ; Opcode 10
00 18 ; Length 24
31 32 33 34 35 36 37 38 ; Random bits
01 00 00 00 ; Version/Reserved
00 00 07 09 ; Timestamp (1801)
77 81 38 e3 8f aa 72 86 ; Hash
HEX_EDNSDATA_END
ENTRY_END
; ... get BADCOOKIE and a new cookie
STEP 71 CHECK_ANSWER
ENTRY_BEGIN
MATCH all server_cookie
REPLY QR RD RA DO YXRRSET ; BADCOOKIE is an extended rcode
SECTION QUESTION
test. IN TXT
ENTRY_END
; Query with a valid future (<5 minutes) cookie ...
STEP 80 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
test. IN TXT
SECTION ADDITIONAL
HEX_EDNSDATA_BEGIN
00 0a ; Opcode 10
00 18 ; Length 24
31 32 33 34 35 36 37 38 ; Random bits
01 00 00 00 ; Version/Reserved
00 00 16 45 ; Timestamp (1801 + 3601 + 299)
4a f5 0f df f0 e8 c7 09 ; Hash
HEX_EDNSDATA_END
ENTRY_END
; ... get an answer
STEP 81 CHECK_ANSWER
ENTRY_BEGIN
MATCH all server_cookie
REPLY QR RD RA AA DO NOERROR
SECTION QUESTION
test. IN TXT
SECTION ANSWER
test. IN TXT "test"
ENTRY_END
SCENARIO_END