From 6d561d38864d2cc21a4e9528527bceac389b173e Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 15 Sep 2022 16:04:43 +1000 Subject: [PATCH 1/4] Add support for 'dohpath' to SVCB (and HTTPS) dohpath is specfied in draft-ietf-add-svcb-dns and has a value of 7. It must be a relative path (start with a /), be encoded as UTF8 and contain the variable dns ({?dns}). --- bin/tests/system/checkzone/zones/good-svcb.db | 1 + lib/dns/rdata/in_1/svcb_64.c | 44 ++++++++++++++++++- tests/dns/rdata_test.c | 40 ++++++++++------- 3 files changed, 67 insertions(+), 18 deletions(-) diff --git a/bin/tests/system/checkzone/zones/good-svcb.db b/bin/tests/system/checkzone/zones/good-svcb.db index 7b7b31d6f0..df868f3d4e 100644 --- a/bin/tests/system/checkzone/zones/good-svcb.db +++ b/bin/tests/system/checkzone/zones/good-svcb.db @@ -24,3 +24,4 @@ svcb6 SVCB 6 . mandatory=port,alpn port=60 alpn=h3 svcb7 SVCB 7 . mandatory=port,alpn port=60 alpn=h1,h3 svcb8 SVCB 8 . mandatory=port,alpn port=60 alpn="h1\\,h2,h3" svcb9 SVCB 0 44._svbc.example.net. +svcb10 SVCB 7 . alpn="h2,h3" dohpath=/{?dns} diff --git a/lib/dns/rdata/in_1/svcb_64.c b/lib/dns/rdata/in_1/svcb_64.c index 7d119d6e48..e9f43ad1c4 100644 --- a/lib/dns/rdata/in_1/svcb_64.c +++ b/lib/dns/rdata/in_1/svcb_64.c @@ -34,13 +34,14 @@ enum encoding { sbpr_base64, sbpr_empty, sbpr_alpn, - sbpr_keylist + sbpr_keylist, + sbpr_dohpath }; static const struct { const char *name; /* Restricted to lowercase LDH by registry. */ unsigned int value; enum encoding encoding; - bool initial; + bool initial; /* Part of the first defined set of encodings. */ } sbpr[] = { { "mandatory", 0, sbpr_keylist, true }, { "alpn", 1, sbpr_alpn, true }, @@ -49,6 +50,7 @@ static const struct { { "ipv4hint", 4, sbpr_ipv4s, true }, { "ech", 5, sbpr_base64, true }, { "ipv6hint", 6, sbpr_ipv6s, true }, + { "dohpath", 7, sbpr_dohpath, false }, }; static isc_result_t @@ -147,6 +149,30 @@ svcb_validate(uint16_t key, isc_region_t *region) { case sbpr_text: case sbpr_base64: break; + case sbpr_dohpath: + /* + * Minimum valid dohpath is "/{?dns}" as + * it MUST be relative (leading "/") and + * MUST contain "{?dns}". + */ + if (region->length < 7) { + return (DNS_R_FORMERR); + } + /* MUST be relative */ + if (region->base[0] != '/') { + return (DNS_R_FORMERR); + } + /* MUST be UTF8 */ + if (!isc_utf8_valid(region->base, + region->length)) { + return (DNS_R_FORMERR); + } + /* MUST contain "{?dns}" */ + if (strnstr((char *)region->base, "{?dns}", + region->length) == NULL) { + return (DNS_R_FORMERR); + } + break; case sbpr_empty: if (region->length != 0) { return (DNS_R_FORMERR); @@ -252,6 +278,7 @@ svc_fromtext(isc_textregion_t *region, isc_buffer_t *target) { switch (sbpr[i].encoding) { case sbpr_text: + case sbpr_dohpath: RETERR(multitxt_fromtext(region, target)); break; case sbpr_alpn: @@ -328,6 +355,19 @@ svc_fromtext(isc_textregion_t *region, isc_buffer_t *target) { len = isc_buffer_usedlength(target) - isc_buffer_usedlength(&sb) - 2; RETERR(uint16_tobuffer(len, &sb)); /* length */ + switch (sbpr[i].encoding) { + case sbpr_dohpath: + /* + * Apply constraints not applied by multitxt_fromtext. + */ + keyregion.base = isc_buffer_used(&sb); + keyregion.length = isc_buffer_usedlength(target) - + isc_buffer_usedlength(&sb); + RETERR(svcb_validate(sbpr[i].value, &keyregion)); + break; + default: + break; + } return (ISC_R_SUCCESS); } diff --git a/tests/dns/rdata_test.c b/tests/dns/rdata_test.c index 0b6c6d73f7..acd964e4ef 100644 --- a/tests/dns/rdata_test.c +++ b/tests/dns/rdata_test.c @@ -2508,7 +2508,7 @@ ISC_RUN_TEST_IMPL(wks) { ISC_RUN_TEST_IMPL(https_svcb) { /* * Known keys: mandatory, apln, no-default-alpn, port, - * ipv4hint, port, ipv6hint. + * ipv4hint, port, ipv6hint, dohpath. */ text_ok_t text_ok[] = { /* unknown key invalid */ @@ -2527,9 +2527,9 @@ ISC_RUN_TEST_IMPL(https_svcb) { TEXT_INVALID("2 svc.example.net. key=\"2222\""), /* zero pad invalid */ TEXT_INVALID("2 svc.example.net. key07=\"2222\""), - TEXT_VALID_LOOP(1, "2 svc.example.net. key7=\"2222\""), - TEXT_VALID_LOOPCHG(1, "2 svc.example.net. key7=2222", - "2 svc.example.net. key7=\"2222\""), + TEXT_VALID_LOOP(1, "2 svc.example.net. key8=\"2222\""), + TEXT_VALID_LOOPCHG(1, "2 svc.example.net. key8=2222", + "2 svc.example.net. key8=\"2222\""), TEXT_VALID_LOOPCHG(1, "2 svc.example.net. alpn=h2", "2 svc.example.net. alpn=\"h2\""), TEXT_VALID_LOOPCHG(1, "2 svc.example.net. alpn=h3", @@ -2557,12 +2557,12 @@ ISC_RUN_TEST_IMPL(https_svcb) { TEXT_VALID_LOOP(1, "2 svc.example.net. ech=abcdefghijkl"), /* bad base64 */ TEXT_INVALID("2 svc.example.net. ech=abcdefghijklm"), - TEXT_VALID_LOOP(1, "2 svc.example.net. key7=\"2222\""), + TEXT_VALID_LOOP(1, "2 svc.example.net. key8=\"2222\""), /* Out of key order on input (alpn == key1). */ TEXT_VALID_LOOPCHG(2, - "2 svc.example.net. key7=\"2222\" alpn=h2", + "2 svc.example.net. key8=\"2222\" alpn=h2", "2 svc.example.net. alpn=\"h2\" " - "key7=\"2222\""), + "key8=\"2222\""), TEXT_VALID_LOOP(1, "2 svc.example.net. key65535=\"2222\""), TEXT_INVALID("2 svc.example.net. key65536=\"2222\""), TEXT_VALID_LOOP(1, "2 svc.example.net. key10"), @@ -2593,18 +2593,18 @@ ISC_RUN_TEST_IMPL(https_svcb) { TEXT_INVALID("2 svc.example.net. " "mandatory=alpn,,port alpn=h2 port=433"), /* mandatory w/ unknown key values */ - TEXT_VALID_LOOP(2, "2 svc.example.net. mandatory=key7 key7"), - TEXT_VALID_LOOP(3, "2 svc.example.net. mandatory=key7,key8 " - "key7 key8"), + TEXT_VALID_LOOP(2, "2 svc.example.net. mandatory=key8 key8"), + TEXT_VALID_LOOP(3, "2 svc.example.net. mandatory=key8,key9 " + "key8 key9"), TEXT_VALID_LOOPCHG( - 3, "2 svc.example.net. mandatory=key8,key7 key7 key8", - "2 svc.example.net. mandatory=key7,key8 key7 key8"), + 3, "2 svc.example.net. mandatory=key9,key8 key8 key9", + "2 svc.example.net. mandatory=key8,key9 key8 key9"), TEXT_INVALID("2 svc.example.net. " - "mandatory=key7,key7"), - TEXT_INVALID("2 svc.example.net. mandatory=,key7"), - TEXT_INVALID("2 svc.example.net. mandatory=key7,"), + "mandatory=key8,key8"), + TEXT_INVALID("2 svc.example.net. mandatory=,key8"), + TEXT_INVALID("2 svc.example.net. mandatory=key8,"), TEXT_INVALID("2 svc.example.net. " - "mandatory=key7,,key7"), + "mandatory=key8,,key8"), /* Invalid test vectors */ TEXT_INVALID("1 foo.example.com. ( key123=abc key123=def )"), TEXT_INVALID("1 foo.example.com. mandatory"), @@ -2617,6 +2617,14 @@ ISC_RUN_TEST_IMPL(https_svcb) { TEXT_INVALID("1 foo.example.com. mandatory=mandatory"), TEXT_INVALID("1 foo.example.com. ( mandatory=key123,key123 " "key123=abc)"), + /* dohpath tests */ + TEXT_VALID_LOOPCHG(1, "1 example.net. dohpath=/{?dns}", + "1 example.net. key7=\"/{?dns}\""), + TEXT_VALID_LOOPCHG(1, "1 example.net. dohpath=/some/path{?dns}", + "1 example.net. key7=\"/some/path{?dns}\""), + TEXT_INVALID("1 example.com. dohpath=no-slash"), + TEXT_INVALID("1 example.com. dohpath=/{?notdns}"), + TEXT_INVALID("1 example.com. dohpath=/notvariable"), TEXT_SENTINEL() }; From 335b397e15185930e3b3224eee440e4cccc2f372 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 15 Sep 2022 16:10:29 +1000 Subject: [PATCH 2/4] Add CHANGES note for [GL #3544] --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index fdaaeb2cf7..dd4e7086d9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +5991. [protocol] Add support for parsing and validating "dohpath" to + SVCB. [GL #3544] + 5990. [test] fuzz/dns_message_checksig now creates the key directory it uses when testing in /tmp at run time. [GL #3569] From 2f3441b40a0b98087c97201eb5ef5e4a3c5af0fa Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 15 Sep 2022 16:12:49 +1000 Subject: [PATCH 3/4] Add release note for [GL #3544] --- doc/notes/notes-current.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 5776a4aa82..0152d9f208 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -74,6 +74,9 @@ Feature Changes libuv should be available on all supported platforms either as a native package or as a backport. :gl:`#3567` +- Add support for parsing and validating ``dohpath`` to SVBC records. + :gl:`#3544` + Bug Fixes ~~~~~~~~~ From 5f07fe8cbb9d3696e94bb9c64625e23721c95c4e Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 15 Sep 2022 17:03:50 +1000 Subject: [PATCH 4/4] Use strnstr implementation from FreeBSD if not provided by OS --- configure.ac | 2 +- lib/isc/include/isc/string.h | 5 +++++ lib/isc/string.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 037b10a0b6..04d400f8de 100644 --- a/configure.ac +++ b/configure.ac @@ -991,7 +991,7 @@ AS_IF([test "$enable_tcp_fastopen" = "yes"], # # Check for some other useful functions that are not ever-present. # -AC_CHECK_FUNCS([strlcpy strlcat]) +AC_CHECK_FUNCS([strlcpy strlcat strnstr]) # # Check for readline support diff --git a/lib/isc/include/isc/string.h b/lib/isc/include/isc/string.h index 5469d69a0e..fbf6129cbe 100644 --- a/lib/isc/include/isc/string.h +++ b/lib/isc/include/isc/string.h @@ -31,6 +31,11 @@ size_t strlcat(char *dst, const char *src, size_t size); #endif /* if !defined(HAVE_STRLCAT) */ +#if !defined(HAVE_STRNSTR) +char * +strnstr(const char *s, const char *find, size_t slen); +#endif /* if !defined(HAVE_STRNSTR) */ + int isc_string_strerror_r(int errnum, char *buf, size_t buflen); diff --git a/lib/isc/string.c b/lib/isc/string.c index f1ee7748cd..09cf5d636c 100644 --- a/lib/isc/string.c +++ b/lib/isc/string.c @@ -12,9 +12,15 @@ */ /* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2001 Mike Barcroft * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -109,6 +115,28 @@ strlcat(char *dst, const char *src, size_t size) { } #endif /* !defined(HAVE_STRLCAT) */ +#if !defined(HAVE_STRNSTR) +char * +strnstr(const char *s, const char *find, size_t slen) { + char c, sc; + size_t len; + + if ((c = *find++) != '\0') { + len = strlen(find); + do { + do { + if (slen-- < 1 || (sc = *s++) == '\0') + return (NULL); + } while (sc != c); + if (len > slen) + return (NULL); + } while (strncmp(s, find, len) != 0); + s--; + } + return ((char *)s); +} +#endif + int isc_string_strerror_r(int errnum, char *buf, size_t buflen) { return (strerror_r(errnum, buf, buflen));