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
slabhash_test();
infra_test();
ldns_test();
+ edns_cookie_test();
zonemd_test();
tcpreuse_test();
msgparse_test();
#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"
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,
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;
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;
*/
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 */
-
- 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 */
+ 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 (!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;
}
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