diff --git a/CHANGES b/CHANGES index 9bf6f54a32..2d9cd6dbf0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +5989. [func] Implement support for DDNS update forwarding using DoT + to TLS-enabled primary servers. [GL #3512] + 5988. [bug] Some out of memory conditions in opensslrsa_link.c could lead to memory leaks. [GL #3551] diff --git a/bin/tests/system/upforwd/knowngood.after2 b/bin/tests/system/upforwd/knowngood.after2 index eab7a2cf06..04a001f813 100644 --- a/bin/tests/system/upforwd/knowngood.after2 +++ b/bin/tests/system/upforwd/knowngood.after2 @@ -4,8 +4,8 @@ example. 3600 IN NS ns3.example. ns1.example. 3600 IN A 10.53.0.1 ns2.example. 3600 IN A 10.53.0.2 ns3.example. 3600 IN A 10.53.0.3 -unsigned.example. 600 IN TXT "Foo" -unsigned.example. 600 IN A 10.10.10.1 updated.example. 600 IN TXT "Foo" updated.example. 600 IN A 10.10.10.1 +updated-dot.example. 600 IN TXT "Foo" +updated-dot.example. 600 IN A 10.10.10.1 example. 3600 IN SOA n1.example. hostmaster.ns1.example. 3 3600 1200 604800 7200 diff --git a/bin/tests/system/upforwd/knowngood.after3 b/bin/tests/system/upforwd/knowngood.after3 new file mode 100644 index 0000000000..18407f09e6 --- /dev/null +++ b/bin/tests/system/upforwd/knowngood.after3 @@ -0,0 +1,13 @@ +example. 3600 IN SOA n1.example. hostmaster.ns1.example. 4 3600 1200 604800 7200 +example. 3600 IN NS ns2.example. +example. 3600 IN NS ns3.example. +ns1.example. 3600 IN A 10.53.0.1 +ns2.example. 3600 IN A 10.53.0.2 +ns3.example. 3600 IN A 10.53.0.3 +unsigned.example. 600 IN TXT "Foo" +unsigned.example. 600 IN A 10.10.10.1 +updated.example. 600 IN TXT "Foo" +updated.example. 600 IN A 10.10.10.1 +updated-dot.example. 600 IN TXT "Foo" +updated-dot.example. 600 IN A 10.10.10.1 +example. 3600 IN SOA n1.example. hostmaster.ns1.example. 4 3600 1200 604800 7200 diff --git a/bin/tests/system/upforwd/ns1/named.conf.in b/bin/tests/system/upforwd/ns1/named.conf.in index 83ba6040c4..dad7b2ffbf 100644 --- a/bin/tests/system/upforwd/ns1/named.conf.in +++ b/bin/tests/system/upforwd/ns1/named.conf.in @@ -21,8 +21,10 @@ options { notify-source 10.53.0.1; transfer-source 10.53.0.1; port @PORT@; + tls-port @TLSPORT@; pid-file "named.pid"; listen-on { 10.53.0.1; }; + listen-on tls ephemeral { 10.53.0.1; }; listen-on-v6 { none; }; recursion yes; notify yes; diff --git a/bin/tests/system/upforwd/ns3/named.conf.in b/bin/tests/system/upforwd/ns3/named.conf.in index 75eed2ed93..abb6770c09 100644 --- a/bin/tests/system/upforwd/ns3/named.conf.in +++ b/bin/tests/system/upforwd/ns3/named.conf.in @@ -16,8 +16,10 @@ options { notify-source 10.53.0.3; transfer-source 10.53.0.3; port @PORT@; + tls-port @TLSPORT@; pid-file "named.pid"; listen-on { 10.53.0.3; }; + listen-on tls ephemeral { 10.53.0.3; }; listen-on-v6 { none; }; recursion no; notify yes; @@ -37,7 +39,7 @@ zone "example" { type secondary; file "example.bk"; allow-update-forwarding { any; }; - primaries { 10.53.0.1; }; + primaries { 10.53.0.1 port @TLSPORT@ tls ephemeral; }; }; zone "example2" { diff --git a/bin/tests/system/upforwd/tests.sh b/bin/tests/system/upforwd/tests.sh index 6d53af1c61..33227c363d 100644 --- a/bin/tests/system/upforwd/tests.sh +++ b/bin/tests/system/upforwd/tests.sh @@ -78,7 +78,7 @@ digcomp knowngood.before dig.out.ns2 || ret=1 digcomp knowngood.before dig.out.ns3 || ret=1 if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi -echo_i "updating zone (signed) ($n)" +echo_i "checking update forwarding of a zone (signed) (Do53 -> DoT) ($n)" ret=0 $NSUPDATE -y "${DEFAULT_HMAC}:update.example:c3Ryb25nIGVub3VnaCBmb3IgYSBtYW4gYnV0IG1hZGUgZm9yIGEgd29tYW4K" -- - < DoT) ($n)" ret=0 -grep "forwarding update for zone 'example/IN'" ns3/named.run > /dev/null || ret=1 +$NSUPDATE -y "${DEFAULT_HMAC}:update.example:c3Ryb25nIGVub3VnaCBmb3IgYSBtYW4gYnV0IG1hZGUgZm9yIGEgd29tYW4K" -S -O -- - < dig.out.ns1 || ret=1 +if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi +n=`expr $n + 1` + +echo_i "fetching secondary 1 copy of zone after update ($n)" +ret=0 +$DIG $DIGOPTS example.\ + @10.53.0.2 axfr > dig.out.ns2 || ret=1 +if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi + +echo_i "fetching secondary 2 copy of zone after update ($n)" +ret=0 +$DIG $DIGOPTS example.\ + @10.53.0.3 axfr > dig.out.ns3 || ret=1 +if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi +n=`expr $n + 1` + +echo_i "comparing post-update copies to known good data ($n)" +ret=0 +digcomp knowngood.after2 dig.out.ns1 || ret=1 +digcomp knowngood.after2 dig.out.ns2 || ret=1 +digcomp knowngood.after2 dig.out.ns3 || ret=1 +if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi + +echo_i "checking 'forwarding update for zone' is logged twice ($n)" +ret=0 +cnt=$(grep -F "forwarding update for zone 'example/IN'" ns3/named.run | wc -l || ret=1) +test "${cnt}" -eq 2 || ret=1 if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi n=`expr $n + 1` @@ -171,9 +213,9 @@ if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi echo_i "comparing post-update copies to known good data ($n)" ret=0 -digcomp knowngood.after2 dig.out.ns1 || ret=1 -digcomp knowngood.after2 dig.out.ns2 || ret=1 -digcomp knowngood.after2 dig.out.ns3 || ret=1 +digcomp knowngood.after3 dig.out.ns1 || ret=1 +digcomp knowngood.after3 dig.out.ns2 || ret=1 +digcomp knowngood.after3 dig.out.ns3 || ret=1 if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi if $FEATURETEST --enable-dnstap @@ -222,7 +264,7 @@ fi if test -f keyname then - echo_i "checking update forwarding to with sig0 ($n)" + echo_i "checking update forwarding to with sig0 (Do53 -> Do53) ($n)" ret=0 keyname=`cat keyname` $NSUPDATE -k $keyname.private -- - < Do53) ($n)" + ret=0 + keyname=`cat keyname` + $NSUPDATE -k $keyname.private -S -O -- - < dig.out.ns1.test$n + grep "status: NOERROR" dig.out.ns1.test$n > /dev/null || ret=1 + if [ $ret != 0 ] ; then echo_i "failed"; fi + status=`expr $status + $ret` + n=`expr $n + 1` + + if $FEATURETEST --enable-dnstap + then + echo_i "checking DNSTAP logging of UPDATE forwarded update replies ($n)" + ret=0 + capture_dnstap + uq_equals_ur || ret=1 + if [ $ret != 0 ] ; then echo_i "failed"; fi + status=`expr $status + $ret` + n=`expr $n + 1` + fi fi echo_i "exit status: $status" diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 5849fd13e8..5776a4aa82 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -40,6 +40,9 @@ New Features - The ``nsupdate`` tool now supports DNS-over-TLS (DoT). :gl:`#1781` +- :iscman:``named`` now supports forwarding Dynamic DNS updates through + DNS-over-TLS (DoT), configured with a TLS-enabled primary server. :gl:`#3512` + - :iscman:`named` now logs the supported cryptographic algorithms during startup and in the output of :option:`named -V`. :gl:`#3541` diff --git a/lib/dns/zone.c b/lib/dns/zone.c index fba7cb4485..9a7ab8f89b 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -717,6 +717,7 @@ struct dns_forward { dns_request_t *request; uint32_t which; isc_sockaddr_t addr; + dns_transport_t *transport; dns_updatecallback_t callback; void *callback_arg; unsigned int options; @@ -18286,6 +18287,9 @@ forward_destroy(dns_forward_t *forward) { if (forward->msgbuf != NULL) { isc_buffer_free(&forward->msgbuf); } + if (forward->transport != NULL) { + dns_transport_detach(&forward->transport); + } if (forward->zone != NULL) { LOCK(&forward->zone->lock); if (ISC_LINK_LINKED(forward, link)) { @@ -18302,20 +18306,22 @@ sendtoprimary(dns_forward_t *forward) { isc_result_t result; isc_sockaddr_t src; isc_dscp_t dscp = -1; + dns_zone_t *zone = forward->zone; + bool tls_transport_invalid = false; - LOCK_ZONE(forward->zone); + LOCK_ZONE(zone); - if (DNS_ZONE_FLAG(forward->zone, DNS_ZONEFLG_EXITING)) { - UNLOCK_ZONE(forward->zone); + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) { + UNLOCK_ZONE(zone); return (ISC_R_CANCELED); } - if (forward->which >= forward->zone->primariescnt) { - UNLOCK_ZONE(forward->zone); + if (forward->which >= zone->primariescnt) { + UNLOCK_ZONE(zone); return (ISC_R_NOMORE); } - forward->addr = forward->zone->primaries[forward->which]; + forward->addr = zone->primaries[forward->which]; /* * Always use TCP regardless of whether the original update * used TCP. @@ -18324,30 +18330,59 @@ sendtoprimary(dns_forward_t *forward) { */ switch (isc_sockaddr_pf(&forward->addr)) { case PF_INET: - src = forward->zone->xfrsource4; - dscp = forward->zone->xfrsource4dscp; + src = zone->xfrsource4; + dscp = zone->xfrsource4dscp; break; case PF_INET6: - src = forward->zone->xfrsource6; - dscp = forward->zone->xfrsource6dscp; + src = zone->xfrsource6; + dscp = zone->xfrsource6dscp; break; default: result = ISC_R_NOTIMPLEMENTED; goto unlock; } + + if (forward->transport != NULL) { + dns_transport_detach(&forward->transport); + } + + if (zone->primarytlsnames != NULL && + zone->primarytlsnames[forward->which] != NULL) + { + dns_view_t *view = dns_zone_getview(zone); + dns_name_t *tlsname = zone->primarytlsnames[zone->curprimary]; + + result = dns_view_gettransport(view, DNS_TRANSPORT_TLS, tlsname, + &forward->transport); + + if (result != ISC_R_SUCCESS) { + /* Log the error message when unlocked. */ + tls_transport_invalid = true; + goto unlock; + } + } + result = dns_request_createraw( forward->zone->view->requestmgr, forward->msgbuf, &src, - &forward->addr, NULL, NULL, dscp, forward->options, - 15 /* XXX */, 0, 0, forward->zone->task, forward_callback, - forward, &forward->request); + &forward->addr, forward->transport, zone->zmgr->tlsctx_cache, + dscp, forward->options, 15 /* XXX */, 0, 0, forward->zone->task, + forward_callback, forward, &forward->request); if (result == ISC_R_SUCCESS) { if (!ISC_LINK_LINKED(forward, link)) { - ISC_LIST_APPEND(forward->zone->forwards, forward, link); + ISC_LIST_APPEND(zone->forwards, forward, link); } } unlock: - UNLOCK_ZONE(forward->zone); + UNLOCK_ZONE(zone); + + if (tls_transport_invalid) { + dns_zone_log(zone, ISC_LOG_ERROR, + "could not get TLS configuration " + "for dynamic update: %s", + isc_result_totext(result)); + } + return (result); } @@ -18487,17 +18522,12 @@ dns_zone_forwardupdate(dns_zone_t *zone, dns_message_t *msg, REQUIRE(callback != NULL); forward = isc_mem_get(zone->mctx, sizeof(*forward)); - - forward->request = NULL; - forward->zone = NULL; - forward->msgbuf = NULL; - forward->which = 0; - forward->mctx = 0; - forward->callback = callback; - forward->callback_arg = callback_arg; + *forward = (dns_forward_t){ .callback = callback, + .callback_arg = callback_arg, + .options = DNS_REQUESTOPT_TCP }; ISC_LINK_INIT(forward, link); forward->magic = FORWARD_MAGIC; - forward->options = DNS_REQUESTOPT_TCP; + /* * If we have a SIG(0) signed message we need to preserve the * query id as that is included in the SIG(0) computation.