From 24bf39dd3d3c0a4ca9accee1f0f653b5ac394b5c Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Fri, 25 Oct 2024 20:14:39 +0200 Subject: [PATCH] draft-fregly-dnsop-slh-dsa-mtl-dnssec support --- configure.ac | 18 ++++++++++++++++ edns.c | 8 +++++++ edns.h | 3 +++ namedb.c | 22 +++++++++++++++++++ namedb.h | 12 +++++++++++ packet.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ zonec.c | 7 +++++- 7 files changed, 129 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 3ebcbbf51..6a1550c06 100644 --- a/configure.ac +++ b/configure.ac @@ -988,6 +988,24 @@ case "$enable_log_role" in ;; esac +AC_ARG_ENABLE(draft-slh-dsa-mtl, AS_HELP_STRING([--enable-draft-slh-dsa-mtl[=optcode]],[Enables responding with SLH-DSA-MTL signatures as described in draft-fregly-dnsop-slh-dsa-mtl-dnssec. Opcode defaults to 65050])) +mtl_mode_full_code="" +case "$enable_draft_slh_dsa_mtl" in + ""|no) + ;; + yes) + AC_DEFINE_UNQUOTED([MTL_MODE_FULL_CODE], [65050], [Define this to enable answering with the full SLH-DSA-MTL signatures when queried with this EDNS opcode]) + ;; + *) + AC_DEFINE_UNQUOTED([MTL_MODE_FULL_CODE], [$enable_draft_slh_dsa_mtl], [Define this to enable answering with the full SLH-DSA-MTL signatures when queried with this EDNS opcode]) + ;; +esac +mtl_dnssec_alg=50 +AC_ARG_WITH([slh-dsa-mtl-dnssec-alg], + AS_HELP_STRING([--with-slh-dsa-mtl-dnssec-alg=alg],[The algorithm used for SLH-DSA-MTL keys and signatures. Only relevant with draft-slh-dsa-mtl enabled. Defaults to 50.]), + [mtl_dnssec_alg=$withval]) +AC_SUBST(mtl_dnssec_alg) +AC_DEFINE_UNQUOTED(MTL_DNSSEC_ALG, [$mtl_dnssec_alg], [The algorithm used for SLH-DSA-MTL keys and signatures. Default 50.]) AC_ARG_ENABLE(memclean, AS_HELP_STRING([--enable-memclean],[Cleanup memory (at exit) for eg. valgrind, memcheck])) if test "$enable_memclean" = "yes"; then AC_DEFINE_UNQUOTED([MEMCLEAN], [1], [Define this to cleanup memory at exit (eg. for valgrind, etc.)]) diff --git a/edns.c b/edns.c index 0e1caca29..82b85e6ab 100644 --- a/edns.c +++ b/edns.c @@ -70,6 +70,9 @@ edns_init_record(edns_record_type *edns) edns->opt_reserved_space = 0; edns->dnssec_ok = 0; edns->nsid = 0; +#ifdef MTL_MODE_FULL_CODE + edns->mtl_mode_full = 0; +#endif edns->cookie_status = COOKIE_NOT_PRESENT; edns->cookie_len = 0; edns->ede = -1; /* -1 means no Extended DNS Error */ @@ -116,6 +119,11 @@ edns_handle_option(uint16_t optcode, uint16_t optlen, buffer_type* packet, buffer_skip(packet, optlen); } break; +#ifdef MTL_MODE_FULL_CODE + case MTL_MODE_FULL_CODE: + edns->mtl_mode_full = 1; + break; +#endif default: buffer_skip(packet, optlen); break; diff --git a/edns.h b/edns.h index 45d7c6361..07fde2b69 100644 --- a/edns.h +++ b/edns.h @@ -65,6 +65,9 @@ struct edns_record int ede; /* RFC 8914 - Extended DNS Errors */ char* ede_text; /* RFC 8914 - Extended DNS Errors text*/ uint16_t ede_text_len; +#ifdef MTL_MODE_FULL_CODE + int mtl_mode_full; +#endif }; typedef struct edns_record edns_record_type; diff --git a/namedb.c b/namedb.c index 772e038b1..2f3ee6e2e 100644 --- a/namedb.c +++ b/namedb.c @@ -656,6 +656,28 @@ rr_rrsig_type_covered(rr_type* rr) return ntohs(* (uint16_t *) rdata_atom_data(rr->rdatas[0])); } +#ifdef MTL_MODE_FULL_CODE +uint8_t +rr_rrsig_algorithm(rr_type* rr) +{ + assert(rr->type == TYPE_RRSIG); + assert(rr->rdata_count > 1); + assert(rdata_atom_size(rr->rdatas[1]) == sizeof(uint8_t)); + + return *(uint8_t*)rdata_atom_data(rr->rdatas[1]); +} + +uint16_t +rr_rrsig_keytag(rr_type* rr) +{ + assert(rr->type == TYPE_RRSIG); + assert(rr->rdata_count > 6); + assert(rdata_atom_size(rr->rdatas[6]) == sizeof(uint16_t)); + + return *(uint16_t*)rdata_atom_data(rr->rdatas[6]); +} +#endif + zone_type * namedb_find_zone(namedb_type* db, const dname_type* dname) { diff --git a/namedb.h b/namedb.h index 37b4a0383..670def298 100644 --- a/namedb.h +++ b/namedb.h @@ -333,6 +333,18 @@ static inline const char* domain_to_string_buf(domain_type* domain, char *buf) */ uint16_t rr_rrsig_type_covered(rr_type* rr); +#ifdef MTL_MODE_FULL_CODE +/* + * The algorithm field of the specified RRSIG RR + */ +uint8_t rr_rrsig_algorithm(rr_type* rr); + +/* + * The keytag field of the specified RRSIG RR + */ +uint16_t rr_rrsig_keytag(rr_type* rr); +#endif + struct namedb { region_type* region; diff --git a/packet.c b/packet.c index 701453d8f..52bd67e18 100644 --- a/packet.c +++ b/packet.c @@ -74,6 +74,66 @@ packet_encode_rr(query_type *q, domain_type *owner, rr_type *rr, uint32_t ttl) buffer_skip(q->packet, sizeof(rdlength)); for (j = 0; j < rr->rdata_count; ++j) { +#ifdef MTL_MODE_FULL_CODE + rrset_type *apex_rrsigs; + + if(rr->type == TYPE_RRSIG + && j == 8 /* The signature data */ + && q->edns.mtl_mode_full + && rr_rrsig_type_covered(rr) != TYPE_SOA + && rr_rrsig_algorithm(rr) == MTL_DNSSEC_ALG + && (apex_rrsigs = domain_find_rrset( + q->zone->apex, q->zone, TYPE_RRSIG))) { + size_t k; + + buffer_write_u8(q->packet, 1); /* Full signature */ + /* First the condensed signature */ + buffer_write(q->packet, + rdata_atom_data(rr->rdatas[j])+1, + rdata_atom_size(rr->rdatas[j])-1); + + for(k = 0; k < apex_rrsigs->rr_count; k++) { + rr_type* rrsig = &apex_rrsigs->rrs[k]; + uint16_t sibling_count; + + if(rrsig->rdata_count < 9 + || rr_rrsig_type_covered(rrsig) != TYPE_SOA + || rr_rrsig_algorithm(rrsig) != MTL_DNSSEC_ALG + || rr_rrsig_keytag(rrsig)!=rr_rrsig_keytag(rr)) + continue; + /* MTL-Type = 1 + * Randomizer = 16 + * Flags = 2 + * Series Identifier = 8 + * Leaf Index = 4 + * Target Rung Left Index = 4 + * Target Rung Right Index = 4 + * 1 + 16 + 2 + 8 + 4 + 4 + 4 = 39 + * Next 2 (uint16_t) is Sibling Count + */ + if(rdata_atom_size(rrsig->rdatas[8]) < 41) + continue; + sibling_count = ntohs(*(uint16_t *)( + rdata_atom_data(rrsig->rdatas[8]) + 39)); + /* Each sibling is 16 bytes, skipping them + * skips the complete Authentication Path + * and takes us to the remainder that needs + * to be appended to the RRSIG signature data. + */ + if(rdata_atom_size(rrsig->rdatas[8]) < 41 + + 16 * sibling_count) + continue; + buffer_write(q->packet, + rdata_atom_data(rrsig->rdatas[8]) + + 41 + 16 * sibling_count, + rdata_atom_size(rrsig->rdatas[8]) - + 41 - 16 * sibling_count); + break; + } + if(k == apex_rrsigs->rr_count) { + } + } else +#endif switch (rdata_atom_wireformat_type(rr->type, j)) { case RDATA_WF_COMPRESSED_DNAME: encode_dname(q, rdata_atom_domain(rr->rdatas[j])); diff --git a/zonec.c b/zonec.c index f91378388..a7a2f6fa0 100644 --- a/zonec.c +++ b/zonec.c @@ -493,7 +493,12 @@ apex_rrset_checks(namedb_type* db, rrset_type* rrset, domain_type* domain) zone->ns_rrset = rrset; } else if (rrset_rrtype(rrset) == TYPE_RRSIG) { for (i = 0; i < rrset->rr_count; ++i) { - if(rr_rrsig_type_covered(&rrset->rrs[i])==TYPE_DNSKEY){ + if(rr_rrsig_type_covered(&rrset->rrs[i])==TYPE_DNSKEY +#ifdef MTL_MODE_FULL_CODE + || (rr_rrsig_type_covered(&rrset->rrs[i])==TYPE_SOA && + rr_rrsig_algorithm(&rrset->rrs[i])==MTL_DNSSEC_ALG) +#endif + ){ zone->is_secure = 1; break; }