[v9_9] change 4558 was incomplete

(cherry picked from commit cd668ea57f)
This commit is contained in:
Evan Hunt 2017-01-30 14:11:30 -08:00
parent 61a5559acd
commit a6bae6d52c
5 changed files with 212 additions and 40 deletions

View file

@ -0,0 +1,95 @@
#!/usr/bin/env perl
#
# Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
use strict;
use warnings;
use IO::File;
use Getopt::Long;
use Net::DNS::Nameserver;
my $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!";
print $pidf "$$\n" or die "cannot write pid file: $!";
$pidf->close or die "cannot close pid file: $!";
sub rmpid { unlink "ans.pid"; exit 1; };
$SIG{INT} = \&rmpid;
$SIG{TERM} = \&rmpid;
my $localaddr = "10.53.0.3";
my $localport = 5300;
my $verbose = 0;
my $ttl = 60;
my $zone = "example.broken";
my $nsname = "ns3.$zone";
my $synth = "synth-then-dname.$zone";
my $synth2 = "synth2-then-dname.$zone";
sub reply_handler {
my ($qname, $qclass, $qtype, $peerhost, $query, $conn) = @_;
my ($rcode, @ans, @auth, @add);
print ("request: $qname/$qtype\n");
STDOUT->flush();
if ($qname eq "example.broken") {
if ($qtype eq "SOA") {
my $rr = new Net::DNS::RR("$qname $ttl $qclass SOA . . 0 0 0 0 0");
push @ans, $rr;
} elsif ($qtype eq "NS") {
my $rr = new Net::DNS::RR("$qname $ttl $qclass NS $nsname");
push @ans, $rr;
$rr = new Net::DNS::RR("$nsname $ttl $qclass A $localaddr");
push @add, $rr;
}
$rcode = "NOERROR";
} elsif ($qname eq "cname-to-$synth2") {
my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.$synth2");
push @ans, $rr;
$rr = new Net::DNS::RR("name.$synth2 $ttl $qclass CNAME name");
push @ans, $rr;
$rr = new Net::DNS::RR("$synth2 $ttl $qclass DNAME .");
push @ans, $rr;
$rcode = "NOERROR";
} elsif ($qname eq "$synth" || $qname eq "$synth2") {
if ($qtype eq "DNAME") {
my $rr = new Net::DNS::RR("$qname $ttl $qclass DNAME .");
push @ans, $rr;
}
$rcode = "NOERROR";
} elsif ($qname eq "name.$synth") {
my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.");
push @ans, $rr;
$rr = new Net::DNS::RR("$synth $ttl $qclass DNAME .");
push @ans, $rr;
$rcode = "NOERROR";
} elsif ($qname eq "name.$synth2") {
my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.");
push @ans, $rr;
$rr = new Net::DNS::RR("$synth2 $ttl $qclass DNAME .");
push @ans, $rr;
$rcode = "NOERROR";
} else {
$rcode = "REFUSED";
}
return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
}
GetOptions(
'port=i' => \$localport,
'verbose!' => \$verbose,
);
my $ns = Net::DNS::Nameserver->new(
LocalAddr => $localaddr,
LocalPort => $localport,
ReplyHandler => \&reply_handler,
Verbose => $verbose,
);
$ns->main_loop;

View file

@ -12,8 +12,6 @@
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
; $Id: root.db,v 1.2 2011/03/18 21:14:19 fdupont Exp $
$TTL 300
. IN SOA gson.nominum.com. a.root.servers.nil. (
2000042100 ; serial
@ -27,3 +25,6 @@ a.root-servers.nil. A 10.53.0.1
example. NS ns2.example.
ns2.example. A 10.53.0.2
example.broken. NS ns3.example.broken.
ns3.example.broken. A 10.53.0.3

View file

@ -20,6 +20,7 @@ SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
status=0
n=0
echo "I:checking short dname from authoritative"
ret=0
@ -81,6 +82,26 @@ grep '^a.target.example.' dig.out.ns4.cname > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:exit status: $status"
n=`expr $n + 1`
echo "I:checking dname is returned with synthesized cname before dname ($n)"
ret=0
$DIG @10.53.0.4 -p 5300 name.synth-then-dname.example.broken A > dig.out.test$n
grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1
grep '^name.synth-then-dname\.example\.broken\..*CNAME.*name.$' dig.out.test$n > /dev/null || ret=1
grep '^synth-then-dname\.example\.broken\..*DNAME.*\.$' dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:checking dname is returned with cname to synthesized cname before dname ($n)"
ret=0
$DIG @10.53.0.4 -p 5300 cname-to-synth2-then-dname.example.broken A > dig.out.test$n
grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1
grep '^cname-to-synth2-then-dname\.example\.broken\..*CNAME.*name\.synth2-then-dname\.example\.broken.$' dig.out.test$n > /dev/null || ret=1
grep '^name\.synth2-then-dname\.example\.broken\..*CNAME.*name.$' dig.out.test$n > /dev/null || ret=1
grep '^synth2-then-dname\.example\.broken\..*DNAME.*\.$' dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:exit status: $status"
[ $status -eq 0 ] || exit 1

View file

@ -137,6 +137,14 @@
<section xml:id="relnotes_bugs"><info><title>Bug Fixes</title></info>
<itemizedlist>
<listitem>
<para>
A synthesized CNAME record appearing in a response before the
associated DNAME could be cached, when it should not have been.
This was a regression introduced while addressing CVE-2016-8864.
[RT #44318]
</para>
</listitem>
<listitem>
<para>
Named could deadlock there were multiple changes to

View file

@ -5818,9 +5818,13 @@ cname_target(dns_rdataset_t *rdataset, dns_name_t *tname) {
return (ISC_R_SUCCESS);
}
/*%
* Construct the synthesised CNAME from the existing QNAME and
* the DNAME RR and store it in 'target'.
*/
static inline isc_result_t
dname_target(dns_rdataset_t *rdataset, dns_name_t *qname,
unsigned int nlabels, dns_fixedname_t *fixeddname)
dname_target(dns_rdataset_t *rdataset, const dns_name_t *qname,
unsigned int nlabels, dns_name_t *target)
{
isc_result_t result;
dns_rdata_t rdata = DNS_RDATA_INIT;
@ -5840,14 +5844,33 @@ dname_target(dns_rdataset_t *rdataset, dns_name_t *qname,
dns_fixedname_init(&prefix);
dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL);
dns_fixedname_init(fixeddname);
result = dns_name_concatenate(dns_fixedname_name(&prefix),
&dname.dname,
dns_fixedname_name(fixeddname), NULL);
&dname.dname, target, NULL);
dns_rdata_freestruct(&dname);
return (result);
}
/*%
* Check if it was possible to construct 'qname' from 'lastcname'
* and 'rdataset'.
*/
static inline isc_result_t
fromdname(dns_rdataset_t *rdataset, const dns_name_t *lastcname,
unsigned int nlabels, const dns_name_t *qname)
{
dns_fixedname_t fixed;
isc_result_t result;
dns_name_t *target;
dns_fixedname_init(&fixed);
target = dns_fixedname_name(&fixed);
result = dname_target(rdataset, lastcname, nlabels, target);
if (result != ISC_R_SUCCESS || !dns_name_equal(qname, target))
return (ISC_R_NOTFOUND);
return (ISC_R_SUCCESS);
}
static isc_boolean_t
is_answeraddress_allowed(dns_view_t *view, dns_name_t *name,
dns_rdataset_t *rdataset)
@ -6464,7 +6487,7 @@ answer_response(fetchctx_t *fctx) {
isc_result_t result;
dns_message_t *message;
dns_name_t *name, *dname = NULL, *qname, tname, *ns_name;
dns_name_t *cname = NULL;
dns_name_t *cname = NULL, *lastcname = NULL;
dns_rdataset_t *rdataset, *ns_rdataset;
isc_boolean_t done, external, aa, found, want_chaining;
isc_boolean_t have_answer, found_cname, found_dname, found_type;
@ -6500,14 +6523,15 @@ answer_response(fetchctx_t *fctx) {
view = fctx->res->view;
result = dns_message_firstname(message, DNS_SECTION_ANSWER);
while (!done && result == ISC_R_SUCCESS) {
dns_namereln_t namereln;
int order;
unsigned int nlabels;
dns_namereln_t namereln, lastreln;
int order, lastorder;
unsigned int nlabels, lastnlabels;
name = NULL;
dns_message_currentname(message, DNS_SECTION_ANSWER, &name);
external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
namereln = dns_name_fullcompare(qname, name, &order, &nlabels);
if (namereln == dns_namereln_equal) {
wanted_chaining = ISC_FALSE;
for (rdataset = ISC_LIST_HEAD(name->list);
@ -6613,6 +6637,7 @@ answer_response(fetchctx_t *fctx) {
&fctx->domain)) {
return (DNS_R_SERVFAIL);
}
lastcname = name;
} else if (rdataset->type == dns_rdatatype_rrsig
&& rdataset->covers ==
dns_rdatatype_cname
@ -6717,6 +6742,17 @@ answer_response(fetchctx_t *fctx) {
chaining++;
} else {
dns_rdataset_t *dnameset = NULL;
isc_boolean_t synthcname = ISC_FALSE;
if (lastcname != NULL) {
lastreln = dns_name_fullcompare(lastcname,
name,
&lastorder,
&lastnlabels);
if (lastreln == dns_namereln_subdomain &&
lastnlabels == dns_name_countlabels(name))
synthcname = ISC_TRUE;
}
/*
* Look for a DNAME (or its SIG). Anything else is
@ -6763,16 +6799,9 @@ answer_response(fetchctx_t *fctx) {
/*
* If DNAME + synthetic CNAME then the
* namereln is dns_namereln_subdomain.
*
* If synthetic CNAME + DNAME then the
* namereln is dns_namereln_commonancestor
* and the number of label must match the
* DNAME. This order is not RFC compliant.
*/
if (namereln != dns_namereln_subdomain &&
(namereln != dns_namereln_commonancestor ||
nlabels != dns_name_countlabels(name)))
!synthcname)
{
char qbuf[DNS_NAME_FORMATSIZE];
char obuf[DNS_NAME_FORMATSIZE];
@ -6792,8 +6821,19 @@ answer_response(fetchctx_t *fctx) {
want_chaining = ISC_TRUE;
POST(want_chaining);
aflag = DNS_RDATASETATTR_ANSWER;
result = dname_target(rdataset, qname,
nlabels, &fdname);
dns_fixedname_init(&fdname);
dname = dns_fixedname_name(&fdname);
if (synthcname) {
result = fromdname(rdataset,
lastcname,
lastnlabels,
qname);
} else {
result = dname_target(rdataset,
qname,
nlabels,
dname);
}
if (result == ISC_R_NOSPACE) {
/*
* We can't construct the
@ -6807,8 +6847,8 @@ answer_response(fetchctx_t *fctx) {
else
dnameset = rdataset;
dname = dns_fixedname_name(&fdname);
if (!is_answertarget_allowed(view,
if (!synthcname &&
!is_answertarget_allowed(view,
qname, rdataset->type,
dname, &fctx->domain))
{
@ -6834,8 +6874,7 @@ answer_response(fetchctx_t *fctx) {
* is a synthesised CNAME before the DNAME.
*/
if ((chaining == 0) ||
(chaining == 1U &&
namereln == dns_namereln_commonancestor))
(chaining == 1U && synthcname))
{
/*
* This data is "the" answer to
@ -6846,9 +6885,12 @@ answer_response(fetchctx_t *fctx) {
if (aflag == DNS_RDATASETATTR_ANSWER) {
have_answer = ISC_TRUE;
found_dname = ISC_TRUE;
if (cname != NULL)
if (cname != NULL &&
synthcname)
{
cname->attributes &=
~DNS_NAMEATTR_ANSWER;
}
name->attributes |=
DNS_NAMEATTR_ANSWER;
}
@ -6866,19 +6908,24 @@ answer_response(fetchctx_t *fctx) {
* DNAME chaining.
*/
if (dnameset != NULL) {
/*
* Copy the dname into the qname fixed name.
*
* Although we check for failure of the copy
* operation, in practice it should never fail
* since we already know that the result fits
* in a fixedname.
*/
dns_fixedname_init(&fqname);
qname = dns_fixedname_name(&fqname);
result = dns_name_copy(dname, qname, NULL);
if (result != ISC_R_SUCCESS)
return (result);
if (!synthcname) {
/*
* Copy the dname into the qname fixed
* name.
*
* Although we check for failure of the
* copy operation, in practice it
* should never fail since we already
* know that the result fits in a
* fixedname.
*/
dns_fixedname_init(&fqname);
qname = dns_fixedname_name(&fqname);
result = dns_name_copy(dname, qname,
NULL);
if (result != ISC_R_SUCCESS)
return (result);
}
wanted_chaining = ISC_TRUE;
name->attributes |= DNS_NAMEATTR_CHAINING;
dnameset->attributes |=