Commit ffeaac1d authored by Mark Andrews's avatar Mark Andrews

3961. [bug] Forwarding of SIG(0) signed UPDATE messages failed with

                        BADSIG.  [RT #37216]
parent c83b91fb
3961. [bug] Forwarding of SIG(0) signed UPDATE messages failed with
BADSIG. [RT #37216]
3960. [bug] 'dig +sigchase' could loop forever. [RT #37220]
3959. [bug] Updates could be lost if they arrived immediately
......
......@@ -1376,7 +1376,7 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
client->formerrcache.addr = client->peeraddr;
client->formerrcache.time = client->requesttime;
client->formerrcache.id = message->id;
} else if (rcode == dns_rcode_servfail &&
} else if (rcode == dns_rcode_servfail && client->query.qname != NULL &&
client->view != NULL && client->view->fail_ttl != 0 &&
((client->attributes & NS_CLIENTATTR_NOSETFC) == 0))
{
......
......@@ -42,3 +42,9 @@ zone "example" {
file "example.db";
allow-update { key update.example.; 10.53.0.3; };
};
zone "example2" {
type master;
file "example2.db";
allow-update { key sig0.example2.; };
};
......@@ -37,3 +37,9 @@ zone "example" {
file "example.bk";
masters { 10.53.0.1; };
};
zone "example2" {
type slave;
file "example2.bk";
masters { 10.53.0.1; };
};
......@@ -39,6 +39,13 @@ zone "example" {
masters { 10.53.0.1; };
};
zone "example2" {
type slave;
file "example2.bk";
allow-update-forwarding { any; };
masters { 10.53.0.1; };
};
zone "nomaster" {
type slave;
file "nomaster1.db";
......
......@@ -15,8 +15,16 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: setup.sh,v 1.10 2011/09/02 23:46:32 tbox Exp $
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
cp -f ns1/example1.db ns1/example.db
rm -f ns1/example.db.jnl ns2/example.bk ns2/example.bk.jnl
rm -f ns1/example2.db.jnl ns2/example2.bk ns2/example2.bk.jnl
cp -f ns3/nomaster.db ns3/nomaster1.db
rm -f Ksig0.example2.*
test -r $RANDFILE || $GENRANDOM 400 $RANDFILE
keyname=`$KEYGEN -q -r $RANDFILE -n HOST -a RSASHA1 -b 1024 -T KEY sig0.example2`
cat ns1/example1.db $keyname.key > ns1/example2.db
echo $keyname > keyname
......@@ -25,9 +25,11 @@ SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
status=0
n=1
sleep 5
echo "I:waiting for servers to be ready for testing"
echo "I:waiting for servers to be ready for testing ($n)"
for i in 1 2 3 4 5 6 7 8 9 10
do
ret=0
......@@ -41,32 +43,36 @@ do
sleep 1
done
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
echo "I:fetching master copy of zone before update"
echo "I:fetching master copy of zone before update ($n)"
ret=0
$DIG +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd example.\
@10.53.0.1 axfr -p 5300 > dig.out.ns1 || ret=1
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
echo "I:fetching slave 1 copy of zone before update"
echo "I:fetching slave 1 copy of zone before update ($n)"
$DIG +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd example.\
@10.53.0.2 axfr -p 5300 > dig.out.ns2 || ret=1
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
echo "I:fetching slave 2 copy of zone before update"
echo "I:fetching slave 2 copy of zone before update ($n)"
ret=0
$DIG +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd example.\
@10.53.0.3 axfr -p 5300 > dig.out.ns3 || ret=1
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
echo "I:comparing pre-update copies to known good data"
echo "I:comparing pre-update copies to known good data ($n)"
ret=0
$PERL ../digcomp.pl knowngood.before dig.out.ns1 || ret=1
$PERL ../digcomp.pl knowngood.before dig.out.ns2 || ret=1
$PERL ../digcomp.pl knowngood.before dig.out.ns3 || ret=1
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
echo "I:updating zone (signed)"
echo "I:updating zone (signed) ($n)"
ret=0
$NSUPDATE -y update.example:c3Ryb25nIGVub3VnaCBmb3IgYSBtYW4gYnV0IG1hZGUgZm9yIGEgd29tYW4K -- - <<EOF || ret=1
server 10.53.0.3 5300
......@@ -75,41 +81,45 @@ update add updated.example. 600 TXT Foo
send
EOF
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
echo "I:sleeping 15 seconds for server to incorporate changes"
sleep 15
echo "I:fetching master copy of zone after update"
echo "I:fetching master copy of zone after update ($n)"
ret=0
$DIG +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd example.\
@10.53.0.1 axfr -p 5300 > dig.out.ns1 || ret=1
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
echo "I:fetching slave 1 copy of zone after update"
echo "I:fetching slave 1 copy of zone after update ($n)"
ret=0
$DIG +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd example.\
@10.53.0.2 axfr -p 5300 > dig.out.ns2 || ret=1
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
echo "I:fetching slave 2 copy of zone after update"
echo "I:fetching slave 2 copy of zone after update ($n)"
ret=0
$DIG +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd example.\
@10.53.0.3 axfr -p 5300 > 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"
echo "I:comparing post-update copies to known good data ($n)"
ret=0
$PERL ../digcomp.pl knowngood.after1 dig.out.ns1 || ret=1
$PERL ../digcomp.pl knowngood.after1 dig.out.ns2 || ret=1
$PERL ../digcomp.pl knowngood.after1 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"
echo "I:checking 'forwarding update for zone' is logged ($n)"
ret=0
grep "forwarding update for zone 'example/IN'" ns3/named.run > /dev/null || ret=1
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
echo "I:updating zone (unsigned)"
echo "I:updating zone (unsigned) ($n)"
ret=0
$NSUPDATE -- - <<EOF || ret=1
server 10.53.0.3 5300
......@@ -118,36 +128,39 @@ update add unsigned.example. 600 TXT Foo
send
EOF
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
echo "I:sleeping 15 seconds for server to incorporate changes"
sleep 15
echo "I:fetching master copy of zone after update"
echo "I:fetching master copy of zone after update ($n)"
ret=0
$DIG +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd example.\
@10.53.0.1 axfr -p 5300 > dig.out.ns1 || ret=1
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
echo "I:fetching slave 1 copy of zone after update"
echo "I:fetching slave 1 copy of zone after update ($n)"
ret=0
$DIG +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd example.\
@10.53.0.2 axfr -p 5300 > dig.out.ns2 || ret=1
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
echo "I:fetching slave 2 copy of zone after update"
echo "I:fetching slave 2 copy of zone after update ($n)"
ret=0
$DIG +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd example.\
@10.53.0.3 axfr -p 5300 > dig.out.ns3 || ret=1
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
echo "I:comparing post-update copies to known good data"
echo "I:comparing post-update copies to known good data ($n)"
ret=0
$PERL ../digcomp.pl knowngood.after2 dig.out.ns1 || ret=1
$PERL ../digcomp.pl knowngood.after2 dig.out.ns2 || ret=1
$PERL ../digcomp.pl knowngood.after2 dig.out.ns3 || ret=1
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
echo "I:checking update forwarding to dead master"
echo "I:checking update forwarding to dead master ($n)"
count=0
ret=0
while [ $count -lt 5 -a $ret -eq 0 ]
......@@ -167,6 +180,21 @@ EOF
count=`expr $count + 1`
done
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
echo "I:checking update forwarding to with sig0 ($n)"
keyname=`cat keyname`
$NSUPDATE -k $keyname.private -- - <<EOF
server 10.53.0.3 5300
zone example2
update add unsigned.example2. 600 A 10.10.10.1
update add unsigned.example2. 600 TXT Foo
send
EOF
$DIG unsigned.example2 A @10.53.0.1 -p 5300 > dig.out.ns1.test$n
grep "status: NOERROR" dig.out.ns1.test$n > /dev/null || ret=1
if [ $ret != 0 ] ; then echo "I:failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
echo "I:exit status: $status"
exit $status
......@@ -3122,6 +3122,17 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
isc_task_t *task, isc_taskaction_t action, void *arg,
dns_messageid_t *idp, dns_dispentry_t **resp,
isc_socketmgr_t *sockmgr)
{
return (dns_dispatch_addresponse3(disp, 0, dest, task, action, arg,
idp, resp, sockmgr));
}
isc_result_t
dns_dispatch_addresponse3(dns_dispatch_t *disp, unsigned int options,
isc_sockaddr_t *dest, isc_task_t *task,
isc_taskaction_t action, void *arg,
dns_messageid_t *idp, dns_dispentry_t **resp,
isc_socketmgr_t *sockmgr)
{
dns_dispentry_t *res;
unsigned int bucket;
......@@ -3210,10 +3221,14 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
}
/*
* Try somewhat hard to find an unique ID.
* Try somewhat hard to find an unique ID unless FIXEDID is set
* in which case we use the id passed in via *idp.
*/
LOCK(&qid->lock);
id = (dns_messageid_t)isc_rng_random(DISP_RNGCTX(disp));
if ((options & DNS_DISPATCHOPT_FIXEDID) != 0)
id = *idp;
else
id = (dns_messageid_t)isc_rng_random(DISP_RNGCTX(disp));
ok = ISC_FALSE;
i = 0;
do {
......@@ -3222,6 +3237,8 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
ok = ISC_TRUE;
break;
}
if ((disp->attributes & DNS_DISPATCHATTR_FIXEDID) != 0)
break;
id += qid->qid_increment;
id &= 0x0000ffff;
} while (i++ < 64);
......@@ -3317,7 +3334,7 @@ dns_dispatch_addresponse(dns_dispatch_t *disp, isc_sockaddr_t *dest,
REQUIRE(VALID_DISPATCH(disp));
REQUIRE((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0);
return (dns_dispatch_addresponse2(disp, dest, task, action, arg,
return (dns_dispatch_addresponse3(disp, 0, dest, task, action, arg,
idp, resp, NULL));
}
......
......@@ -143,10 +143,14 @@ struct dns_dispatchset {
#define DNS_DISPATCHATTR_NOLISTEN 0x00000020U
#define DNS_DISPATCHATTR_MAKEQUERY 0x00000040U
#define DNS_DISPATCHATTR_CONNECTED 0x00000080U
/*#define DNS_DISPATCHATTR_RANDOMPORT 0x00000100U*/
#define DNS_DISPATCHATTR_FIXEDID 0x00000100U
#define DNS_DISPATCHATTR_EXCLUSIVE 0x00000200U
/*@}*/
/*
*/
#define DNS_DISPATCHOPT_FIXEDID 0x00000001U
isc_result_t
dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy,
dns_dispatchmgr_t **mgrp);
......@@ -384,6 +388,13 @@ dns_dispatch_gettcp(dns_dispatchmgr_t *mgr, isc_sockaddr_t *destaddr,
*/
isc_result_t
dns_dispatch_addresponse3(dns_dispatch_t *disp, unsigned int options,
isc_sockaddr_t *dest, isc_task_t *task,
isc_taskaction_t action, void *arg,
isc_uint16_t *idp, dns_dispentry_t **resp,
isc_socketmgr_t *sockmgr);
isc_result_t
dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
isc_task_t *task, isc_taskaction_t action, void *arg,
......
......@@ -48,6 +48,7 @@
#define DNS_REQUESTOPT_TCP 0x00000001U
#define DNS_REQUESTOPT_CASE 0x00000002U
#define DNS_REQUESTOPT_FIXEDID 0x00000004U
typedef struct dns_requestevent {
ISC_EVENT_COMMON(struct dns_requestevent);
......
......@@ -523,7 +523,8 @@ isblackholed(dns_dispatchmgr_t *dispatchmgr, isc_sockaddr_t *destaddr) {
}
static isc_result_t
create_tcp_dispatch(dns_requestmgr_t *requestmgr, isc_sockaddr_t *srcaddr,
create_tcp_dispatch(isc_boolean_t newtcp, dns_requestmgr_t *requestmgr,
isc_sockaddr_t *srcaddr,
isc_sockaddr_t *destaddr, isc_dscp_t dscp,
isc_boolean_t *connected, dns_dispatch_t **dispatchp)
{
......@@ -533,16 +534,18 @@ create_tcp_dispatch(dns_requestmgr_t *requestmgr, isc_sockaddr_t *srcaddr,
unsigned int attrs;
isc_sockaddr_t bind_any;
result = dns_dispatch_gettcp(requestmgr->dispatchmgr, destaddr,
srcaddr, dispatchp);
if (result == ISC_R_SUCCESS) {
char peer[ISC_SOCKADDR_FORMATSIZE];
if (!newtcp) {
result = dns_dispatch_gettcp(requestmgr->dispatchmgr, destaddr,
srcaddr, dispatchp);
if (result == ISC_R_SUCCESS) {
char peer[ISC_SOCKADDR_FORMATSIZE];
*connected = ISC_TRUE;
isc_sockaddr_format(destaddr, peer, sizeof(peer));
req_log(ISC_LOG_DEBUG(1), "attached to existing TCP "
"connection to %s", peer);
return (result);
*connected = ISC_TRUE;
isc_sockaddr_format(destaddr, peer, sizeof(peer));
req_log(ISC_LOG_DEBUG(1), "attached to existing TCP "
"connection to %s", peer);
return (result);
}
}
result = isc_socket_create(requestmgr->socketmgr,
......@@ -637,7 +640,8 @@ find_udp_dispatch(dns_requestmgr_t *requestmgr, isc_sockaddr_t *srcaddr,
}
static isc_result_t
get_dispatch(isc_boolean_t tcp, dns_requestmgr_t *requestmgr,
get_dispatch(isc_boolean_t tcp, isc_boolean_t newtcp,
dns_requestmgr_t *requestmgr,
isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
isc_dscp_t dscp, isc_boolean_t *connected,
dns_dispatch_t **dispatchp)
......@@ -645,7 +649,7 @@ get_dispatch(isc_boolean_t tcp, dns_requestmgr_t *requestmgr,
isc_result_t result;
if (tcp)
result = create_tcp_dispatch(requestmgr, srcaddr,
result = create_tcp_dispatch(newtcp, requestmgr, srcaddr,
destaddr, dscp, connected,
dispatchp);
else
......@@ -733,8 +737,10 @@ dns_request_createraw4(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
isc_mem_t *mctx;
dns_messageid_t id;
isc_boolean_t tcp = ISC_FALSE;
isc_boolean_t newtcp = ISC_FALSE;
isc_region_t r;
isc_boolean_t connected = ISC_FALSE;
unsigned int dispopt = 0;
REQUIRE(VALID_REQUESTMGR(requestmgr));
REQUIRE(msgbuf != NULL);
......@@ -796,17 +802,30 @@ dns_request_createraw4(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
if ((options & DNS_REQUESTOPT_TCP) != 0 || r.length > 512)
tcp = ISC_TRUE;
result = get_dispatch(tcp, requestmgr, srcaddr, destaddr, dscp,
again:
result = get_dispatch(tcp, newtcp, requestmgr, srcaddr, destaddr, dscp,
&connected, &request->dispatch);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = dns_dispatch_addresponse2(request->dispatch, destaddr, task,
req_response, request, &id,
&request->dispentry,
if ((options & DNS_REQUESTOPT_FIXEDID) != 0) {
id = (r.base[0] << 8) | r.base[1];
dispopt |= DNS_DISPATCHOPT_FIXEDID;
}
result = dns_dispatch_addresponse3(request->dispatch, dispopt,
destaddr, task, req_response,
request, &id, &request->dispentry,
requestmgr->socketmgr);
if (result != ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS) {
if ((options & DNS_REQUESTOPT_FIXEDID) != 0 && !newtcp) {
newtcp = ISC_TRUE;
connected = ISC_FALSE;
dns_dispatch_detach(&request->dispatch);
goto again;
}
goto cleanup;
}
socket = req_getsocket(request);
INSIST(socket != NULL);
......@@ -1010,8 +1029,8 @@ dns_request_createvia4(dns_requestmgr_t *requestmgr, dns_message_t *message,
use_tcp:
tcp = ISC_TF((options & DNS_REQUESTOPT_TCP) != 0);
result = get_dispatch(tcp, requestmgr, srcaddr, destaddr, dscp,
&connected, &request->dispatch);
result = get_dispatch(tcp, ISC_FALSE, requestmgr, srcaddr, destaddr,
dscp, &connected, &request->dispatch);
if (result != ISC_R_SUCCESS)
goto cleanup;
......
......@@ -609,6 +609,7 @@ struct dns_forward {
isc_sockaddr_t addr;
dns_updatecallback_t callback;
void *callback_arg;
unsigned int options;
ISC_LINK(dns_forward_t) link;
};
......@@ -14997,8 +14998,8 @@ sendtomaster(dns_forward_t *forward) {
result = dns_request_createraw4(forward->zone->view->requestmgr,
forward->msgbuf,
&src, &forward->addr, dscp,
DNS_REQUESTOPT_TCP, 15 /* XXX */,
0, 0, forward->zone->task,
forward->options, 15 /* XXX */,
0, 0, forward->zone->task,
forward_callback, forward,
&forward->request);
if (result == ISC_R_SUCCESS) {
......@@ -15144,6 +15145,13 @@ dns_zone_forwardupdate(dns_zone_t *zone, dns_message_t *msg,
forward->callback_arg = callback_arg;
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.
*/
if (msg->sig0 != NULL)
forward->options |= DNS_REQUESTOPT_FIXEDID;
mr = dns_message_getrawmessage(msg);
if (mr == NULL) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment