diff --git a/CHANGES b/CHANGES index b2be41e234..cdec5f95b9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +3964. [func] nsupdate now performs check-names processing. + [RT #36266] + 3963. [test] Added NXRRSET test cases to the "dlzexternal" system test. [RT #37344] diff --git a/README b/README index e63637b00c..eac77426c7 100644 --- a/README +++ b/README @@ -97,6 +97,8 @@ BIND 9.11.0 - dig can now set yet-to-be-defined EDNS flags on requests (+ednsflags). - serial-query-rate no longer covers NOTIFY messages. These are separately controlled by notify-rate and startup-notify-rate. + - nsupdate now performs check-names processing by default on records + to be added. This can be disabled with "check-names no". This release addresses the security flaw described in CVE-2014-3214 and CVE-2014-3859. diff --git a/bin/nsupdate/nsupdate.c b/bin/nsupdate/nsupdate.c index 183162487f..ef9fcae710 100644 --- a/bin/nsupdate/nsupdate.c +++ b/bin/nsupdate/nsupdate.c @@ -182,6 +182,7 @@ static dns_rdataclass_t zoneclass = dns_rdataclass_none; static dns_message_t *answer = NULL; static isc_uint32_t default_ttl = 0; static isc_boolean_t default_ttl_set = ISC_FALSE; +static isc_boolean_t checknames = ISC_TRUE; typedef struct nsu_requestinfo { dns_message_t *msg; @@ -1827,6 +1828,33 @@ update_addordelete(char *cmdline, isc_boolean_t isdelete) { } } + if (!isdelete && checknames) { + dns_fixedname_t fixed; + dns_name_t *bad; + + if (!dns_rdata_checkowner(name, rdata->rdclass, rdata->type, + ISC_TRUE)) + { + char namebuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "check-names failed: bad owner '%s'\n", + namebuf); + goto failure; + } + + dns_fixedname_init(&fixed); + bad = dns_fixedname_name(&fixed); + if (!dns_rdata_checknames(rdata, name, bad)) { + char namebuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(bad, namebuf, sizeof(namebuf)); + fprintf(stderr, "check-names failed: bad name '%s'\n", + namebuf); + goto failure; + } + } + doneparsing: result = dns_message_gettemprdatalist(updatemsg, &rdatalist); @@ -1878,6 +1906,31 @@ evaluate_update(char *cmdline) { return (update_addordelete(cmdline, isdelete)); } +static isc_uint16_t +evaluate_checknames(char *cmdline) { + char *word; + + ddebug("evaluate_checknames()"); + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read check-names directive\n"); + return (STATUS_SYNTAX); + } + if (strcasecmp(word, "yes") == 0 || + strcasecmp(word, "true") == 0 || + strcasecmp(word, "on") == 0) { + checknames = ISC_TRUE; + } else if (strcasecmp(word, "no") == 0 || + strcasecmp(word, "false") == 0 || + strcasecmp(word, "off") == 0) { + checknames = ISC_FALSE; + } else { + fprintf(stderr, "incorrect check-names directive: %s\n", word); + return (STATUS_SYNTAX); + } + return (STATUS_MORE); +} + static void setzone(dns_name_t *zonename) { isc_result_t result; @@ -2012,6 +2065,9 @@ do_next_command(char *cmdline) { } if (strcasecmp(word, "realm") == 0) return (evaluate_realm(cmdline)); + if (strcasecmp(word, "check-names") == 0 || + strcasecmp(word, "checknames") == 0) + return (evaluate_checknames(cmdline)); if (strcasecmp(word, "gsstsig") == 0) { #ifdef GSSAPI usegsstsig = ISC_TRUE; @@ -2045,6 +2101,7 @@ do_next_command(char *cmdline) { "oldgsstsig (use Microsoft's GSS_TSIG to sign the request)\n" "zone name (set the zone to be updated)\n" "class CLASS (set the zone's DNS class, e.g. IN (default), CH)\n" +"check-names { on | off } (enable / disable check-names)\n" "[prereq] nxdomain name (does this name not exist)\n" "[prereq] yxdomain name (does this name exist)\n" "[prereq] nxrrset .... (does this RRset exist)\n" diff --git a/bin/nsupdate/nsupdate.docbook b/bin/nsupdate/nsupdate.docbook index 7b006a64aa..ad44204a6e 100644 --- a/bin/nsupdate/nsupdate.docbook +++ b/bin/nsupdate/nsupdate.docbook @@ -455,6 +455,22 @@ + + + check-names + yes_or_no + + + + Turn on or off check-names processing on records to + be added. Check-names has no effect on prerequisites + or records to be deleted. By default check-names + processing is on. If check-names processing fails + the record will not be added to the UPDATE message. + + + + prereq nxdomain diff --git a/bin/tests/system/checknames/tests.sh b/bin/tests/system/checknames/tests.sh index 2b4f07c667..e8eca7e116 100644 --- a/bin/tests/system/checknames/tests.sh +++ b/bin/tests/system/checknames/tests.sh @@ -89,6 +89,7 @@ echo "I: check that updates to 'check-names fail;' are rejected ($n)" ret=0 not=1 $NSUPDATE -d < nsupdate.out.test$n 2>&1 || not=0 +check-names off server 10.53.0.1 5300 update add xxx_xxx.fail.update. 600 A 10.10.10.1 send @@ -104,6 +105,7 @@ n=`expr $n + 1` echo "I: check that updates to 'check-names warn;' succeed and are logged ($n)" ret=0 $NSUPDATE -d < nsupdate.out.test$n 2>&1|| ret=1 +check-names off server 10.53.0.1 5300 update add xxx_xxx.warn.update. 600 A 10.10.10.1 send @@ -119,6 +121,7 @@ echo "I: check that updates to 'check-names ignore;' succeed and are not logged ret=0 not=1 $NSUPDATE -d < nsupdate.out.test$n 2>&1 || ret=1 +check-names off server 10.53.0.1 5300 update add xxx_xxx.ignore.update. 600 A 10.10.10.1 send @@ -135,6 +138,7 @@ echo "I: check that updates to 'check-names master ignore;' succeed and are not ret=0 not=1 $NSUPDATE -d < nsupdate.out.test$n 2>&1 || ret=1 +check-names off server 10.53.0.4 5300 update add xxx_xxx.master-ignore.update. 600 A 10.10.10.1 send diff --git a/bin/tests/system/nsupdate/tests.sh b/bin/tests/system/nsupdate/tests.sh index 609088f6e4..abf2b042a8 100755 --- a/bin/tests/system/nsupdate/tests.sh +++ b/bin/tests/system/nsupdate/tests.sh @@ -629,5 +629,32 @@ test ${lines:-0} -eq 64 || ret=1 [ $ret = 0 ] || { echo I:failed; status=1; } fi +n=`expr $n + 1` +echo "I:check check-names processing ($n)" +ret=0 +$NSUPDATE << EOF > nsupdate.out1-$n 2>&1 +update add # 0 in a 1.2.3.4 +EOF +grep "bad owner" nsupdate.out1-$n > /dev/null || ret=1 + +$NSUPDATE << EOF > nsupdate.out2-$n 2>&1 +check-names off +update add # 0 in a 1.2.3.4 +EOF +grep "bad owner" nsupdate.out2-$n > /dev/null && ret=1 + +$NSUPDATE << EOF > nsupdate.out3-$n 2>&1 +update add . 0 in mx 0 # +EOF +grep "bad name" nsupdate.out3-$n > /dev/null || ret=1 + +$NSUPDATE << EOF > nsupdate.out4-$n 2>&1 +check-names off +update add . 0 in mx 0 # +EOF +grep "bad name" nsupdate.out4-$n > /dev/null && ret=1 + +[ $ret = 0 ] || { echo I:failed; status=1; } + echo "I:exit status: $status" exit $status diff --git a/lib/dns/master.c b/lib/dns/master.c index a2eef3f2fd..98ec9280dc 100644 --- a/lib/dns/master.c +++ b/lib/dns/master.c @@ -1833,7 +1833,6 @@ load_text(dns_loadctx_t *lctx) { goto insist_and_cleanup; } - if (type == dns_rdatatype_rrsig || type == dns_rdatatype_sig) covers = dns_rdata_covers(&rdata[rdcount]);