static void
rpz_st_clear(ns_client_t *client);
+static isc_boolean_t
+rpz_ck_dnssec(ns_client_t *client, isc_result_t qresult,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
+
/*%
* Increment query statistics counters.
*/
}
static inline ns_dbversion_t *
-query_findversion(ns_client_t *client, dns_db_t *db)
-{
+query_findversion(ns_client_t *client, dns_db_t *db) {
ns_dbversion_t *dbversion;
/*%
dns_dbversion_t *rpz_version = NULL;
isc_result_t result;
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_getdb");
+
result = query_getzonedb(client, p_name, dns_rdatatype_any,
DNS_GETDB_IGNOREACL, zonep, dbp, &rpz_version);
if (result == ISC_R_SUCCESS) {
}
static inline void
-rpz_match_clear(dns_rpz_st_t *st)
-{
+rpz_match_clear(dns_rpz_st_t *st) {
rpz_clean(&st->m.zone, &st->m.db, &st->m.node, &st->m.rdataset);
st->m.version = NULL;
}
static inline isc_result_t
-rpz_ready(ns_client_t *client, dns_rdataset_t **rdatasetp)
-{
+rpz_ready(ns_client_t *client, dns_rdataset_t **rdatasetp) {
REQUIRE(rdatasetp != NULL);
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_ready");
+
if (*rdatasetp == NULL) {
*rdatasetp = query_newrdataset(client);
if (*rdatasetp == NULL) {
rpz_st_clear(ns_client_t *client) {
dns_rpz_st_t *st = client->query.rpz_st;
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_st_clear");
+
if (st->m.rdataset != NULL)
query_putrdataset(client, &st->m.rdataset);
rpz_match_clear(st);
rpz_get_zbits(ns_client_t *client,
dns_rdatatype_t ip_type, dns_rpz_type_t rpz_type)
{
- dns_rpz_zones_t *rpzs;
dns_rpz_st_t *st;
dns_rpz_zbits_t zbits;
- rpzs = client->view->rpzs;
+ REQUIRE(client != NULL);
+ REQUIRE(client->query.rpz_st != NULL);
+
+ st = client->query.rpz_st;
switch (rpz_type) {
case DNS_RPZ_TYPE_CLIENT_IP:
- zbits = rpzs->have.client_ip;
+ zbits = st->have.client_ip;
break;
case DNS_RPZ_TYPE_QNAME:
- zbits = rpzs->have.qname;
+ zbits = st->have.qname;
break;
case DNS_RPZ_TYPE_IP:
if (ip_type == dns_rdatatype_a) {
- zbits = rpzs->have.ipv4;
+ zbits = st->have.ipv4;
} else if (ip_type == dns_rdatatype_aaaa) {
- zbits = rpzs->have.ipv6;
+ zbits = st->have.ipv6;
} else {
- zbits = rpzs->have.ip;
+ zbits = st->have.ip;
}
break;
case DNS_RPZ_TYPE_NSDNAME:
- zbits = rpzs->have.nsdname;
+ zbits = st->have.nsdname;
break;
case DNS_RPZ_TYPE_NSIP:
if (ip_type == dns_rdatatype_a) {
- zbits = rpzs->have.nsipv4;
+ zbits = st->have.nsipv4;
} else if (ip_type == dns_rdatatype_aaaa) {
- zbits = rpzs->have.nsipv6;
+ zbits = st->have.nsipv6;
} else {
- zbits = rpzs->have.nsip;
+ zbits = st->have.nsip;
}
break;
default:
break;
}
- st = client->query.rpz_st;
-
/*
* Choose
* the earliest configured policy zone (rpz->num)
* If the client wants recursion, allow only compatible policies.
*/
if (!RECURSIONOK(client))
- zbits &= rpzs->p.no_rd_ok;
+ zbits &= st->popt.no_rd_ok;
return (zbits);
}
dns_clientinfomethods_t cm;
dns_clientinfo_t ci;
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rrset_find");
+
dns_clientinfomethods_init(&cm, ns_client_sourceip);
dns_clientinfo_init(&ci, client, NULL);
/*
* Compute a policy owner name, p_name, in a policy zone given the needed
- * policy type and the trigger name.
+ * policy type and the trigger name.
*/
static isc_result_t
rpz_get_p_name(ns_client_t *client, dns_name_t *p_name,
unsigned int first, labels;
isc_result_t result;
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_get_p_name");
+
/*
* The policy owner name consists of a suffix depending on the type
* and policy zone and a prefix that is the longest possible string
REQUIRE(nodep != NULL);
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_find_p");
+
dns_clientinfomethods_init(&cm, ns_client_sourceip);
dns_clientinfo_init(&ci, client, NULL);
dns_rpz_policy_t policy;
isc_result_t result;
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_ip");
+
dns_fixedname_init(&ip_namef);
ip_name = dns_fixedname_name(&ip_namef);
break;
zbits &= (DNS_RPZ_ZMASK(rpz_num) >> 1);
+ /*
+ * In case num_zones has changed since zbits was
+ * originally calculated
+ */
+ if (rpz_num >= rpzs->p.num_zones)
+ break;
+
/*
* Do not try applying policy zones that cannot replace a
* previously found policy zone.
* data can out of date during races with and among
* policy zone updates.
*/
+ CTRACE(ISC_LOG_ERROR,
+ "rpz_rewrite_ip: mismatched summary data; "
+ "continuing");
continue;
case DNS_R_SERVFAIL:
rpz_clean(&p_zone, &p_db, &p_node, p_rdatasetp);
* ensures that we found the longest match.
*/
if (rpz->policy != DNS_RPZ_POLICY_DISABLED) {
+ CTRACE(ISC_LOG_DEBUG(3),
+ "rpz_rewrite_ip: rpz_save_p");
rpz_save_p(st, rpz, rpz_type,
policy, p_name, prefix, result,
&p_zone, &p_db, &p_node,
struct in6_addr in6a;
isc_result_t result;
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_ip_rrset");
+
zbits = rpz_get_zbits(client, ip_type, rpz_type);
if (zbits == 0)
return (ISC_R_SUCCESS);
dns_rdataset_t *p_rdataset;
isc_result_t result;
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_ip_rrsets");
+
st = client->query.rpz_st;
ip_version = NULL;
ip_db = NULL;
dns_rdatatype_t qtype, dns_rpz_type_t rpz_type,
dns_rpz_zbits_t allowed_zbits, dns_rdataset_t **rdatasetp)
{
+ dns_rpz_zones_t *rpzs;
dns_rpz_zone_t *rpz;
dns_rpz_st_t *st;
dns_fixedname_t p_namef;
dns_rpz_policy_t policy;
isc_result_t result;
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_name");
+
zbits = rpz_get_zbits(client, qtype, rpz_type);
zbits &= allowed_zbits;
if (zbits == 0)
return (ISC_R_SUCCESS);
+ rpzs = client->view->rpzs;
+
/*
* If there is only one eligible policy zone, just check it.
* If more than one, then use the summary database to find
* the bit mask of policy zones with policies for this trigger name.
- * x&-x is the least significant bit set in x
+ * x&(~x+1) is the least significant bit set in x
*/
if (zbits != (zbits & (~zbits + 1))) {
- zbits = dns_rpz_find_name(client->view->rpzs,
- rpz_type, zbits, trig_name);
+ zbits = dns_rpz_find_name(rpzs, rpz_type, zbits, trig_name);
if (zbits == 0)
return (ISC_R_SUCCESS);
}
* We check the most eligible zone first and so usually check only
* one policy zone.
*/
- for (rpz_num = 0;
- zbits != 0;
- ++rpz_num, zbits >>= 1) {
- if ((zbits & 1) == 0) {
- INSIST(rpz_num <= client->view->rpzs->p.num_zones);
+ for (rpz_num = 0; zbits != 0; ++rpz_num, zbits >>= 1) {
+ if ((zbits & 1) == 0)
continue;
- }
+
+ /*
+ * In case num_zones has changed since the 'have'
+ * originally calculated
+ */
+ if (rpz_num >= rpzs->p.num_zones)
+ break;
/*
* Do not check policy zones that cannot replace a previously
* found policy.
*/
- rpz = client->view->rpzs->zones[rpz_num];
+ rpz = rpzs->zones[rpz_num];
if (st->m.policy != DNS_RPZ_POLICY_MISS) {
if (st->m.rpz->num < rpz->num)
break;
* data can out of date during races with and among
* policy zone updates.
*/
+ CTRACE(ISC_LOG_ERROR,
+ "rpz_rewrite_name: mismatched summary data; "
+ "continuing");
continue;
case DNS_R_SERVFAIL:
rpz_clean(&p_zone, &p_db, &p_node, rdatasetp);
}
#endif
if (rpz->policy != DNS_RPZ_POLICY_DISABLED) {
+ CTRACE(ISC_LOG_DEBUG(3),
+ "rpz_rewrite_name: rpz_save_p");
rpz_save_p(st, rpz, rpz_type,
policy, p_name, 0, result,
&p_zone, &p_db, &p_node,
{
dns_rpz_st_t *st;
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_ns_skip");
+
st = client->query.rpz_st;
if (str != NULL)
* Look for response policy zone QNAME, NSIP, and NSDNAME rewriting.
*/
static isc_result_t
-rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
- isc_boolean_t resuming)
+rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
+ isc_result_t qresult, isc_boolean_t resuming,
+ dns_rdataset_t *ordataset, dns_rdataset_t *osigset)
{
+ dns_rpz_zones_t *rpzs;
dns_rpz_st_t *st;
dns_rdataset_t *rdataset;
dns_fixedname_t nsnamef;
int qresult_type;
dns_rpz_zbits_t zbits;
isc_result_t result = ISC_R_SUCCESS;
+ dns_rpz_have_t have;
+ dns_rpz_popt_t popt;
+ int rpz_ver;
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite");
+
+ rpzs = client->view->rpzs;
st = client->query.rpz_st;
+
+ if (rpzs == NULL ||
+ (st != NULL && (st->state & DNS_RPZ_REWRITTEN) != 0))
+ return (DNS_R_DISALLOWED);
+
+ LOCK(&rpzs->maint_lock);
+ if (rpzs->p.num_zones == 0 ||
+ (!RECURSIONOK(client) && rpzs->p.no_rd_ok == 0) ||
+ !rpz_ck_dnssec(client, result, ordataset, osigset))
+ {
+ UNLOCK(&rpzs->maint_lock);
+ return (DNS_R_DISALLOWED);
+ }
+ have = rpzs->have;
+ popt = rpzs->p;
+ rpz_ver = rpzs->rpz_ver;
+ UNLOCK(&rpzs->maint_lock);
+
if (st == NULL) {
st = isc_mem_get(client->mctx, sizeof(*st));
if (st == NULL)
st->p_name = dns_fixedname_name(&st->_p_namef);
st->r_name = dns_fixedname_name(&st->_r_namef);
st->fname = dns_fixedname_name(&st->_fnamef);
+ st->have = have;
+ st->popt = popt;
+ st->rpz_ver = rpz_ver;
client->query.rpz_st = st;
}
* Get bits for the policy zones that do not need
* to wait for the results of recursion.
*/
- allowed = client->view->rpzs->have.qname_skip_recurse;
+ allowed = st->have.qname_skip_recurse;
if (allowed == 0)
return (ISC_R_SUCCESS);
} else {
dns_fixedname_init(&nsnamef);
dns_name_clone(client->query.qname, dns_fixedname_name(&nsnamef));
- while (st->r.label > client->view->rpzs->p.min_ns_labels) {
+ while (st->r.label > st->popt.min_ns_labels) {
/*
* Get NS rrset for each domain in the current qname.
*/
NULL, nsname);
}
if (st->r.ns_rdataset == NULL ||
- !dns_rdataset_isassociated(st->r.ns_rdataset)) {
+ !dns_rdataset_isassociated(st->r.ns_rdataset))
+ {
dns_db_t *db = NULL;
result = rpz_rrset_find(client, nsname,
dns_rdatatype_ns,
dns_rdatatype_t type;
isc_result_t result;
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_ck_dnssec");
+
if (client->view->rpzs->p.break_dnssec || !WANTDNSSEC(client))
return (ISC_TRUE);
unsigned int labels;
isc_result_t result;
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_add_cname");
+
labels = dns_name_countlabels(cname);
if (labels > 2 && dns_name_iswildcard(cname)) {
dns_fixedname_init(&prefix);
rpz_st = client->query.rpz_st;
if (rpz_st != NULL &&
- (rpz_st->state & DNS_RPZ_RECURSING) != 0) {
+ (rpz_st->state & DNS_RPZ_RECURSING) != 0)
+ {
+ CTRACE(ISC_LOG_DEBUG(3), "resume from RPZ recursion");
+
+ /*
+ * Has response policy changed out from under us?
+ */
+ if (rpz_st->rpz_ver != client->view->rpzs->rpz_ver) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: RPZ settings out of date");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
is_zone = rpz_st->q.is_zone;
authoritative = rpz_st->q.authoritative;
zone = rpz_st->q.zone;
rpz_st->r.r_rdataset = event->rdataset;
query_putrdataset(client, &event->sigrdataset);
} else if (REDIRECT(client)) {
-
/*
* Restore saved state.
*/
+ CTRACE(ISC_LOG_DEBUG(3),
+ "resume from redirect recursion");
qtype = client->query.redirect.qtype;
rdataset = client->query.redirect.rdataset;
client->query.redirect.rdataset = NULL;
if (event->db != NULL)
dns_db_detach(&event->db);
} else {
+ CTRACE(ISC_LOG_DEBUG(3),
+ "resume from normal recursion");
authoritative = ISC_FALSE;
qtype = event->qtype;
!(result == DNS_R_DELEGATION && !is_zone && RECURSIONOK(client)) &&
(client->query.rpz_st == NULL ||
(client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0)&&
- (client->query.attributes & NS_QUERYATTR_RRL_CHECKED) == 0) {
+ (client->query.attributes & NS_QUERYATTR_RRL_CHECKED) == 0)
+ {
dns_rdataset_t nc_rdataset;
isc_boolean_t wouldlog;
char log_buf[DNS_RRL_LOG_BUF_LEN];
}
}
- if (client->view->rpzs != NULL &&
- client->view->rpzs->p.num_zones != 0 &&
- (RECURSIONOK(client) || client->view->rpzs->p.no_rd_ok != 0) &&
- rpz_ck_dnssec(client, result, rdataset, sigrdataset) &&
- !RECURSING(client) &&
- (client->query.rpz_st == NULL ||
- (client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0) &&
+ if (!RECURSING(client) &&
!dns_name_equal(client->query.qname, dns_rootname))
{
isc_result_t rresult;
- rresult = rpz_rewrite(client, qtype, result, resuming);
+ rresult = rpz_rewrite(client, qtype, result, resuming,
+ rdataset, sigrdataset);
rpz_st = client->query.rpz_st;
switch (rresult) {
case ISC_R_SUCCESS:
break;
+ case DNS_R_DISALLOWED:
+ goto norpz;
case DNS_R_DELEGATION:
/*
* recursing for NS names or addresses,
RECURSE_ERROR(rresult);
goto cleanup;
}
+
if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS)
rpz_st->state |= DNS_RPZ_REWRITTEN;
if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS &&
rpz_st->m.policy != DNS_RPZ_POLICY_PASSTHRU &&
(rpz_st->m.policy != DNS_RPZ_POLICY_TCP_ONLY ||
(client->attributes & NS_CLIENTATTR_TCP) == 0) &&
- rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) {
- /* We got a hit and are going to answer with our
+ rpz_st->m.policy != DNS_RPZ_POLICY_ERROR)
+ {
+ /*
+ * We got a hit and are going to answer with our
* fiction. Ensure that we answer with the name
* we looked up even if we were stopped short
* in recursion or for a deferral.
* We will add this rdataset.
*/
rdataset->ttl = ISC_MIN(rdataset->ttl,
- rpz_st->m.ttl);
+ rpz_st->m.ttl);
}
break;
case DNS_RPZ_POLICY_WILDCNAME:
}
}
+ norpz:
switch (result) {
case ISC_R_SUCCESS:
/*
t=`expr $t + 1`
echo "I:testing $NAME recurses (${t})"
run_query $TESTNAME $LINE && {
- echo "I:test $t failed"
+ echo "I:test ${t} failed"
status=1
}
}
echo "I:testing that l1.l0 exists without RPZ (${t})"
$DIG $DIGOPTS l1.l0 ns @10.53.0.2 -p 5300 > dig.out.${t}
grep "status: NOERROR" dig.out.${t} > /dev/null 2>&1 || {
- echo "I:test $t failed"
+ echo "I:test ${t} failed"
status=1
}
echo "I:testing that l2.l1.l0 returns SERVFAIL without RPZ (${t})"
$DIG $DIGOPTS l2.l1.l0 ns @10.53.0.2 -p 5300 > dig.out.${t}
grep "status: SERVFAIL" dig.out.${t} > /dev/null 2>&1 || {
- echo "I:test $t failed"
+ echo "I:test ${t} failed"
status=1
}
expect_recurse 5a 5
expect_recurse 5a 6
-echo "I:exit status: $status"
+# Group 6
+echo "I:check recursive behavior consistency during policy update races"
+run_server 6a
+sleep 1
+t=`expr $t + 1`
+echo "I:running dig to cache CNAME record (${t})"
+$DIG $DIGOPTS @10.53.0.2 -p 5300 www.test.example.org CNAME > dig.out.${t}
+sleep 1
+echo "I:suspending authority server"
+kill -TSTP `cat ns1/named.pid`
+echo "I:adding an NSDNAME policy"
+cp ns2/db.6a.00.policy.local ns2/saved.policy.local
+cp ns2/db.6b.00.policy.local ns2/db.6a.00.policy.local
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 6a.00.policy.local 2>&1 | sed 's/^/I:ns2 /'
+sleep 1
+t=`expr $t + 1`
+echo "I:running dig to follow CNAME (blocks, so runs in the background) (${t})"
+$DIG $DIGOPTS @10.53.0.2 -p 5300 www.test.example.org A > dig.out.${t} &
+sleep 1
+echo "I:removing the NSDNAME policy"
+cp ns2/db.6c.00.policy.local ns2/db.6a.00.policy.local
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 6a.00.policy.local 2>&1 | sed 's/^/I:ns2 /'
+sleep 1
+echo "I:resuming authority server"
+kill -CONT `cat ns1/named.pid`
+for n in 1 2 3 4 5 6 7 8 9; do
+ sleep 1
+ [ -s dig.out.${t} ] || continue
+ grep "status: NOERROR" dig.out.${t} > /dev/null 2>&1 || {
+ echo "I:test ${t} failed"
+ status=1
+ }
+done
+
+echo "I:check recursive behavior consistency during policy removal races"
+cp ns2/saved.policy.local ns2/db.6a.00.policy.local
+run_server 6a
+sleep 1
+t=`expr $t + 1`
+echo "I:running dig to cache CNAME record (${t})"
+$DIG $DIGOPTS @10.53.0.2 -p 5300 www.test.example.org CNAME > dig.out.${t}
+sleep 1
+echo "I:suspending authority server"
+kill -TSTP `cat ns1/named.pid`
+echo "I:adding an NSDNAME policy"
+cp ns2/db.6b.00.policy.local ns2/db.6a.00.policy.local
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 6a.00.policy.local 2>&1 | sed 's/^/I:ns2 /'
+sleep 1
+t=`expr $t + 1`
+echo "I:running dig to follow CNAME (blocks, so runs in the background) (${t})"
+$DIG $DIGOPTS @10.53.0.2 -p 5300 www.test.example.org A > dig.out.${t} &
+sleep 1
+echo "I:removing the policy zone"
+cp ns2/named.default.conf ns2/db.6a.00.policy.local
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 6a.00.policy.local 2>&1 | sed 's/^/I:ns2 /'
+sleep 1
+echo "I:resuming authority server"
+kill -CONT `cat ns1/named.pid`
+for n in 1 2 3 4 5 6 7 8 9; do
+ sleep 1
+ [ -s dig.out.${t} ] || continue
+ grep "status: NOERROR" dig.out.${t} > /dev/null 2>&1 || {
+ echo "I:test ${t} failed"
+ status=1
+ }
+done
+
exit $status