2604. [func] Add support for DNS rebinding attack prevention through

			new options, deny-answer-addresses and
			deny-answer-aliases.  Based on contributed code from
			JD Nurmi, Google. [RT #18192]
parent fc7ecc62
2604. [func] Add support for DNS rebinding attack prevention through
new options, deny-answer-addresses and
deny-answer-aliases. Based on contributed code from
JD Nurmi, Google. [RT #18192]
2603. [port] win32: handle .exe extension of named-checkzone and
named-comilezone argv[0] names under windows.
[RT #19767]
......
......@@ -54,6 +54,8 @@ BIND 9.7.0
internal information about query failures, especially about
server failures.
Add support for DNS rebinding attack prevention.
BIND 9.6.0
BIND 9.6.0 includes a number of changes from BIND 9.5 and earlier
......
/*
* Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: bind.keys.h,v 1.3 2009/03/05 23:47:35 tbox Exp $ */
#define TRUSTED_KEYS "\
trusted-keys {\n\
# NOTE: This key expires September 2009 \n\
# Go to https://www.isc.org/solutions/dlv to download a replacement\n\
# NOTE: This key expires September 2009 \n\
# Go to https://www.isc.org/solutions/dlv to download a replacement\n\
dlv.isc.org. 257 3 5 \"BEAAAAPHMu/5onzrEE7z1egmhg/WPO0+juoZrW3euWEn4MxDCE1+lLy2 brhQv5rN32RKtMzX6Mj70jdzeND4XknW58dnJNPCxn8+jAGl2FZLK8t+ 1uq4W+nnA3qO2+DL+k6BD4mewMLbIYFwe0PG73Te9fZ2kJb56dhgMde5 ymX4BI/oQ+cAK50/xvJv00Frf8kw6ucMTwFlgPe+jnGxPPEmHAte/URk Y62ZfkLoBAADLHQ9IrS2tryAe7mbBZVcOwIeU/Rw/mRx/vwwMCTgNboM QKtUdvNXDrYJDSHZws3xiRXF1Rf+al9UmZfSav/4NWLKjHzpT59k/VSt TDN0YUuWrBNh\";\n\
};\n\
"
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: server.c,v 1.530 2009/03/04 23:48:01 tbox Exp $ */
/* $Id: server.c,v 1.531 2009/05/29 22:22:35 jinmei Exp $ */
/*! \file */
......@@ -278,8 +278,8 @@ end_reserved_dispatches(ns_server_t *server, isc_boolean_t all);
*/
static isc_result_t
configure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config,
const char *aclname, cfg_aclconfctx_t *actx,
isc_mem_t *mctx, dns_acl_t **aclp)
const char *aclname, const char *acltuplename,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, dns_acl_t **aclp)
{
isc_result_t result;
const cfg_obj_t *maps[3];
......@@ -305,13 +305,21 @@ configure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config,
*/
return (ISC_R_SUCCESS);
if (acltuplename != NULL) {
/*
* If the ACL is given in an optional tuple, retrieve it.
* The parser should have ensured that a valid object be
* returned.
*/
aclobj = cfg_tuple_get(aclobj, acltuplename);
}
result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
actx, mctx, 0, aclp);
return (result);
}
/*%
* Configure a sortlist at '*aclp'. Essentially the same as
* configure_view_acl() except it calls cfg_acl_fromconfig with a
......@@ -355,6 +363,80 @@ configure_view_sortlist(const cfg_obj_t *vconfig, const cfg_obj_t *config,
return (result);
}
static isc_result_t
configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config,
const char *confname, const char *conftuplename,
isc_mem_t *mctx, dns_rbt_t **rbtp)
{
isc_result_t result;
const cfg_obj_t *maps[3];
const cfg_obj_t *obj = NULL;
const cfg_listelt_t *element;
int i = 0;
dns_fixedname_t fixed;
dns_name_t *name;
isc_buffer_t b;
const char *str;
const cfg_obj_t *nameobj;
if (*rbtp != NULL)
dns_rbt_destroy(rbtp);
if (vconfig != NULL)
maps[i++] = cfg_tuple_get(vconfig, "options");
if (config != NULL) {
const cfg_obj_t *options = NULL;
(void)cfg_map_get(config, "options", &options);
if (options != NULL)
maps[i++] = options;
}
maps[i] = NULL;
(void)ns_config_get(maps, confname, &obj);
if (obj == NULL)
/*
* No value available. *rbtp == NULL.
*/
return (ISC_R_SUCCESS);
if (conftuplename != NULL) {
obj = cfg_tuple_get(obj, conftuplename);
if (cfg_obj_isvoid(obj))
return (ISC_R_SUCCESS);
}
result = dns_rbt_create(mctx, NULL, NULL, rbtp);
if (result != ISC_R_SUCCESS)
return (result);
dns_fixedname_init(&fixed);
name = dns_fixedname_name(&fixed);
for (element = cfg_list_first(obj);
element != NULL;
element = cfg_list_next(element)) {
nameobj = cfg_listelt_value(element);
str = cfg_obj_asstring(nameobj);
isc_buffer_init(&b, str, strlen(str));
isc_buffer_add(&b, strlen(str));
CHECK(dns_name_fromtext(name, &b, dns_rootname,
ISC_FALSE, NULL));
/*
* We don't need the node data, but need to set dummy data to
* avoid a partial match with an empty node. For example, if
* we have foo.example.com and bar.example.com, we'd get a match
* for baz.example.com, which is not the expected result.
* We simply use (void *)1 as the dummy data.
*/
CHECK(dns_rbt_addname(*rbtp, name, (void *)1));
}
return (result);
cleanup:
dns_rbt_destroy(rbtp);
return (result);
}
static isc_result_t
configure_view_dnsseckey(const cfg_obj_t *vconfig, const cfg_obj_t *key,
dns_keytable_t *keytable, isc_mem_t *mctx)
......@@ -1722,10 +1804,10 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
/*
* Configure the "match-clients" and "match-destinations" ACL.
*/
CHECK(configure_view_acl(vconfig, config, "match-clients", actx,
CHECK(configure_view_acl(vconfig, config, "match-clients", NULL, actx,
ns_g_mctx, &view->matchclients));
CHECK(configure_view_acl(vconfig, config, "match-destinations", actx,
ns_g_mctx, &view->matchdestinations));
CHECK(configure_view_acl(vconfig, config, "match-destinations", NULL,
actx, ns_g_mctx, &view->matchdestinations));
/*
* Configure the "match-recursive-only" option.
......@@ -1797,20 +1879,20 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
* "allow-recursion", and "allow-recursion-on" acls if
* configured in named.conf.
*/
CHECK(configure_view_acl(vconfig, config, "allow-query-cache",
CHECK(configure_view_acl(vconfig, config, "allow-query-cache", NULL,
actx, ns_g_mctx, &view->queryacl));
CHECK(configure_view_acl(vconfig, config, "allow-query-cache-on",
CHECK(configure_view_acl(vconfig, config, "allow-query-cache-on", NULL,
actx, ns_g_mctx, &view->queryonacl));
if (view->queryonacl == NULL)
CHECK(configure_view_acl(NULL, ns_g_config,
"allow-query-cache-on", actx,
"allow-query-cache-on", NULL, actx,
ns_g_mctx, &view->queryonacl));
if (strcmp(view->name, "_bind") != 0) {
CHECK(configure_view_acl(vconfig, config, "allow-recursion",
actx, ns_g_mctx,
NULL, actx, ns_g_mctx,
&view->recursionacl));
CHECK(configure_view_acl(vconfig, config, "allow-recursion-on",
actx, ns_g_mctx,
NULL, actx, ns_g_mctx,
&view->recursiononacl));
}
......@@ -1823,7 +1905,7 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
if (view->queryacl == NULL && view->recursionacl != NULL)
dns_acl_attach(view->recursionacl, &view->queryacl);
if (view->queryacl == NULL && view->recursion)
CHECK(configure_view_acl(vconfig, config, "allow-query",
CHECK(configure_view_acl(vconfig, config, "allow-query", NULL,
actx, ns_g_mctx, &view->queryacl));
if (view->recursion &&
view->recursionacl == NULL && view->queryacl != NULL)
......@@ -1835,19 +1917,20 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
*/
if (view->recursionacl == NULL && view->recursion)
CHECK(configure_view_acl(NULL, ns_g_config,
"allow-recursion",
"allow-recursion", NULL,
actx, ns_g_mctx,
&view->recursionacl));
if (view->recursiononacl == NULL && view->recursion)
CHECK(configure_view_acl(NULL, ns_g_config,
"allow-recursion-on",
"allow-recursion-on", NULL,
actx, ns_g_mctx,
&view->recursiononacl));
if (view->queryacl == NULL) {
if (view->recursion)
CHECK(configure_view_acl(NULL, ns_g_config,
"allow-query-cache", actx,
ns_g_mctx, &view->queryacl));
"allow-query-cache", NULL,
actx, ns_g_mctx,
&view->queryacl));
else {
if (view->queryacl != NULL)
dns_acl_detach(&view->queryacl);
......@@ -1855,6 +1938,25 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
}
}
/*
* Filter setting on addresses in the answer section.
*/
CHECK(configure_view_acl(vconfig, config, "deny-answer-addresses",
"acl", actx, ns_g_mctx, &view->denyansweracl));
CHECK(configure_view_nametable(vconfig, config, "deny-answer-addresses",
"except-from", ns_g_mctx,
&view->answeracl_exclude));
/*
* Filter setting on names (CNAME/DNAME targets) in the answer section.
*/
CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases",
"name", ns_g_mctx,
&view->denyanswernames));
CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases",
"except-from", ns_g_mctx,
&view->answernames_exclude));
/*
* Configure sortlist, if set
*/
......@@ -1868,19 +1970,19 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
*/
if (view->notifyacl == NULL)
CHECK(configure_view_acl(NULL, ns_g_config,
"allow-notify", actx,
"allow-notify", NULL, actx,
ns_g_mctx, &view->notifyacl));
if (view->transferacl == NULL)
CHECK(configure_view_acl(NULL, ns_g_config,
"allow-transfer", actx,
"allow-transfer", NULL, actx,
ns_g_mctx, &view->transferacl));
if (view->updateacl == NULL)
CHECK(configure_view_acl(NULL, ns_g_config,
"allow-update", actx,
"allow-update", NULL, actx,
ns_g_mctx, &view->updateacl));
if (view->upfwdacl == NULL)
CHECK(configure_view_acl(NULL, ns_g_config,
"allow-update-forwarding", actx,
"allow-update-forwarding", NULL, actx,
ns_g_mctx, &view->upfwdacl));
obj = NULL;
......@@ -3301,7 +3403,7 @@ load_configuration(const char *filename, ns_server_t *server,
else
isc_quota_soft(&server->recursionquota, 0);
CHECK(configure_view_acl(NULL, config, "blackhole", &aclconfctx,
CHECK(configure_view_acl(NULL, config, "blackhole", NULL, &aclconfctx,
ns_g_mctx, &server->blackholeacl));
if (server->blackholeacl != NULL)
dns_dispatchmgr_setblackhole(ns_g_dispatchmgr,
......
......@@ -15,7 +15,7 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: ans.pl,v 1.10 2007/09/24 04:13:25 marka Exp $
# $Id: ans.pl,v 1.11 2009/05/29 22:22:36 jinmei Exp $
#
# Ad hoc name server
......@@ -52,6 +52,7 @@ for (;;) {
my @questions = $packet->question;
my $qname = $questions[0]->qname;
my $qtype = $questions[0]->qtype;
if ($qname eq "cname1.example.com") {
# Data for the "cname + other data / 1" test
......@@ -61,6 +62,32 @@ for (;;) {
# Data for the "cname + other data / 2" test: same RRs in opposite order
$packet->push("answer", new Net::DNS::RR("cname2.example.com 300 A 1.2.3.4"));
$packet->push("answer", new Net::DNS::RR("cname2.example.com 300 CNAME cname2.example.com"));
} elsif ($qname eq "www.example.org" || $qname eq "www.example.net" ||
$qname eq "badcname.example.org" ||
$qname eq "goodcname.example.org" ||
$qname eq "foo.baddname.example.org" ||
$qname eq "foo.gooddname.example.org") {
# Data for address/alias filtering.
if ($qtype eq "A") {
$packet->push("answer",
new Net::DNS::RR($qname .
" 300 A 192.0.2.1"));
} elsif ($qtype eq "AAAA") {
$packet->push("answer",
new Net::DNS::RR($qname .
" 300 AAAA 2001:db8:beef::1"));
}
} elsif ($qname eq "badcname.example.net" ||
$qname eq "goodcname.example.net") {
# Data for CNAME/DNAME filtering. We need to make one-level
# delegation to avoid automatic acceptance for subdomain aliases
$packet->push("authority", new Net::DNS::RR("example.net 300 NS ns.example.net"));
$packet->push("additional", new Net::DNS::RR("ns.example.net 300 A 10.53.0.3"));
} elsif ($qname =~ /sub\.example\.org/) {
# Data for CNAME/DNAME filtering. The final answers are
# expected to be accepted regardless of the filter setting.
$packet->push("authority", new Net::DNS::RR("sub.example.org 300 NS ns.sub.example.org"));
$packet->push("additional", new Net::DNS::RR("ns.sub.example.org 300 A 10.53.0.3"));
} else {
# Data for the "bogus referrals" test
$packet->push("authority", new Net::DNS::RR("below.www.example.com 300 NS ns.below.www.example.com"));
......
......@@ -15,7 +15,7 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: ans.pl,v 1.9 2007/09/24 04:13:25 marka Exp $
# $Id: ans.pl,v 1.10 2009/05/29 22:22:36 jinmei Exp $
#
# Ad hoc name server
......@@ -50,7 +50,42 @@ for (;;) {
$packet->header->qr(1);
$packet->push("answer", new Net::DNS::RR("www.example.com 300 A 1.2.3.4"));
my @questions = $packet->question;
my $qname = $questions[0]->qname;
if ($qname eq "badcname.example.net") {
$packet->push("answer",
new Net::DNS::RR($qname .
" 300 CNAME badcname.example.org"));
} elsif ($qname eq "foo.baddname.example.net") {
$packet->push("answer",
new Net::DNS::RR("baddname.example.net" .
" 300 DNAME baddname.example.org"));
} elsif ($qname eq "foo.gooddname.example.net") {
$packet->push("answer",
new Net::DNS::RR("gooddname.example.net" .
" 300 DNAME gooddname.example.org"));
} elsif ($qname eq "goodcname.example.net") {
$packet->push("answer",
new Net::DNS::RR($qname .
" 300 CNAME goodcname.example.org"));
} elsif ($qname eq "cname.sub.example.org") {
$packet->push("answer",
new Net::DNS::RR($qname .
" 300 CNAME ok.sub.example.org"));
} elsif ($qname eq "ok.sub.example.org") {
$packet->push("answer",
new Net::DNS::RR($qname . " 300 A 192.0.2.1"));
} elsif ($qname eq "www.dname.sub.example.org") {
$packet->push("answer",
new Net::DNS::RR("dname.sub.example.org" .
" 300 DNAME ok.sub.example.org"));
} elsif ($qname eq "www.ok.sub.example.org") {
$packet->push("answer",
new Net::DNS::RR($qname . " 300 A 192.0.2.1"));
} else {
$packet->push("answer", new Net::DNS::RR("www.example.com 300 A 1.2.3.4"));
}
$sock->send($packet->data);
......
......@@ -14,9 +14,10 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: clean.sh,v 1.1 2008/07/17 01:15:34 marka Exp $
# $Id: clean.sh,v 1.2 2009/05/29 22:22:36 jinmei Exp $
#
# Clean up after resolver tests.
#
rm -f */named.memstats
rm -f dig.out
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: named.conf,v 1.13 2007/06/18 23:47:30 tbox Exp $ */
/* $Id: named.conf,v 1.14 2009/05/29 22:22:36 jinmei Exp $ */
controls { /* empty */ };
......@@ -29,6 +29,11 @@ options {
listen-on-v6 { none; };
recursion yes;
acache-enable yes;
deny-answer-addresses { 192.0.2.0/24; 2001:db8:beef::/48; }
except-from { "example.org"; };
deny-answer-aliases { "example.org"; }
except-from { "goodcname.example.net";
"gooddname.example.net"; };
};
zone "." {
......
......@@ -15,7 +15,7 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: tests.sh,v 1.9 2007/06/19 23:47:05 tbox Exp $
# $Id: tests.sh,v 1.10 2009/05/29 22:22:36 jinmei Exp $
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
......@@ -35,5 +35,75 @@ $DIG +tcp cname2.example.com. a @10.53.0.1 -p 5300 >/dev/null || status=1
echo "I:check that server is still running"
$DIG +tcp www.example.com. a @10.53.0.1 -p 5300 >/dev/null || status=1
echo "I:checking answer IPv4 address filtering (deny)"
ret=0
$DIG +tcp www.example.net @10.53.0.1 a -p 5300 > dig.out || ret=1
grep "status: SERVFAIL" dig.out > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:checking answer IPv6 address filtering (deny)"
ret=0
$DIG +tcp www.example.net @10.53.0.1 aaaa -p 5300 > dig.out || ret=1
grep "status: SERVFAIL" dig.out > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:checking answer IPv4 address filtering (accept)"
ret=0
$DIG +tcp www.example.org @10.53.0.1 a -p 5300 > dig.out || ret=1
grep "status: NOERROR" dig.out > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:checking answer IPv6 address filtering (accept)"
ret=0
$DIG +tcp www.example.org @10.53.0.1 aaaa -p 5300 > dig.out || ret=1
grep "status: NOERROR" dig.out > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:checking CNAME target filtering (deny)"
ret=0
$DIG +tcp badcname.example.net @10.53.0.1 a -p 5300 > dig.out || ret=1
grep "status: SERVFAIL" dig.out > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:checking CNAME target filtering (accept)"
ret=0
$DIG +tcp goodcname.example.net @10.53.0.1 a -p 5300 > dig.out || ret=1
grep "status: NOERROR" dig.out > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:checking CNAME target filtering (accept due to subdomain)"
ret=0
$DIG +tcp cname.sub.example.org @10.53.0.1 a -p 5300 > dig.out || ret=1
grep "status: NOERROR" dig.out > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:checking DNAME target filtering (deny)"
ret=0
$DIG +tcp foo.baddname.example.net @10.53.0.1 a -p 5300 > dig.out || ret=1
grep "status: SERVFAIL" dig.out > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:checking DNAME target filtering (accept)"
ret=0
$DIG +tcp foo.gooddname.example.net @10.53.0.1 a -p 5300 > dig.out || ret=1
grep "status: NOERROR" dig.out > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:checking DNAME target filtering (accept due to subdomain)"
ret=0
$DIG +tcp www.dname.sub.example.org @10.53.0.1 a -p 5300 > dig.out || ret=1
grep "status: NOERROR" dig.out > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:exit status: $status"
exit $status
......@@ -18,7 +18,7 @@
- PERFORMANCE OF THIS SOFTWARE.
-->
<!-- File: $Id: Bv9ARM-book.xml,v 1.409 2009/05/14 20:46:04 jreed Exp $ -->
<!-- File: $Id: Bv9ARM-book.xml,v 1.410 2009/05/29 22:22:36 jinmei Exp $ -->
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<title>BIND 9 Administrator Reference Manual</title>
......@@ -2861,6 +2861,19 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
</para>
</entry>
</row>
<row rowsep="0">
<entry colname="1">
<para>
<varname>namelist</varname>
</para>
</entry>
<entry colname="2">
<para>
A list of one or more <varname>domain_name</varname>
elements.
</para>
</entry>
</row>
<row rowsep="0">
<entry colname="1">
<para>
......@@ -4951,6 +4964,8 @@ badresp:1,adberr:0,findfail:0,valfail:0]
<optional> disable-empty-zone <replaceable>zone_name</replaceable> ; </optional>
<optional> zero-no-soa-ttl <replaceable>yes_or_no</replaceable> ; </optional>
<optional> zero-no-soa-ttl-cache <replaceable>yes_or_no</replaceable> ; </optional>
<optional> deny-answer-addresses { <replaceable>address_match_list</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
<optional> deny-answer-aliases { <replaceable>namelist</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
};
</programlisting>
......@@ -8426,6 +8441,142 @@ XXX: end of RFC1918 addresses #defined out -->
</sect3>
<sect3>
<title>Content Filtering</title>
<para>
<acronym>BIND</acronym> 9 provides the ability to filter
out DNS responses from external DNS servers containing
certain types of data in the answer section.
Specifically, it can reject address (A or AAAA) records if
the corresponding IPv4 or IPv6 addresses match the given
<varname>address_match_list</varname> of the
<command>deny-answer-addresses</command> option.
It can also reject CNAME or DNAME records if the "alias"
name (i.e., the CNAME alias or the substituted query name
due to DNAME) matches the
given <varname>namelist</varname> of the
<command>deny-answer-aliases</command> option, where
"match" means the alias name is a subdomain of one of
the <varname>name_list</varname> elements.
If the optional <varname>namelist</varname> is specified
with <command>except-from</command>, records whose query name
matches the list will be accepted regardless of the filter
setting.
Likewise, if the alias name is a subdomain of the
corresponding zone, the <command>deny-answer-aliases</command>
filter will not apply;
for example, even if "example.com" is specified for
<command>deny-answer-aliases</command>,
</para>
<programlisting>www.example.com. CNAME xxx.example.com.</programlisting>
<para>
returned by an "example.com" server will be accepted.
</para>
<para>
In the <varname>address_match_list</varname> of the
<command>deny-answer-addresses</command> option, only
<varname>ip_addr</varname>
and <varname>ip_prefix</varname>
are meaningful;
any <varname>key_id</varname> will be silently ignored.
</para>
<para>
If a response message is rejected due to the filtering,
the entire message is discarded without being cached, and
a SERVFAIL error will be returned to the client.
</para>
<para>
This filtering is intended to prevent "DNS rebinding attacks," in
which an attacker, in response to a query for a domain name the
attacker controls, returns an IP address within your own network or
an alias name within your own domain.
A naive web browser or script could then serve as an
unintended proxy, allowing the attacker
to get access to an internal node of your local network
that couldn't be externally accessed otherwise.
See the paper available at
<ulink>
http://portal.acm.org/citation.cfm?id=1315245.1315298
</ulink>
for more details about the attacks.
</para>
<para>
For example, if you own a domain named "example.net" and
your internal network uses an IPv4 prefix 192.0.2.0/24,
you might specify the following rules:
</para>
<programlisting>deny-answer-addresses { 192.0.2.0/24; } except-from { "example.net"; };
deny-answer-aliases { "example.net"; };
</programlisting>
<para>
If an external attacker lets a web browser in your local
network look up an IPv4 address of "attacker.example.com",
the attacker's DNS server would return a response like this:
</para>
<programlisting>attacker.example.com. A 192.0.2.1</programlisting>
<para>
in the answer section.