2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 06:25:31 +00:00

Fix a bug in RPZ that could cause unwanted recursion (#39229)

Conflicts:
	doc/arm/notes.xml
This commit is contained in:
Mukund Sivaraman
2015-05-07 08:26:27 +05:30
parent 012142bbe0
commit b947e1a521
22 changed files with 991 additions and 27 deletions

View File

@@ -55,7 +55,7 @@
*
* Each leaf indicates that an IP address is listed in the IP address or the
* name server IP address policy sub-zone (or both) of the corresponding
* response response zone. The policy data such as a CNAME or an A record
* response policy zone. The policy data such as a CNAME or an A record
* is kept in the policy zone. After an IP address has been found in a radix
* tree, the node in the policy zone's database is found by converting
* the IP address to a domain name in a canonical form.
@@ -133,11 +133,6 @@ struct dns_rpz_cidr_node {
dns_rpz_addr_zbits_t sum;
};
/*
* The data in a RBT node has two pairs of bits for policy zones.
* One pair is for the corresponding name of the node such as example.com
* and the other pair is for a wildcard child such as *.example.com.
*/
/*
* A pair of arrays of bits flagging the existence of
* QNAME and NSDNAME policy triggers.
@@ -148,6 +143,11 @@ struct dns_rpz_nm_zbits {
dns_rpz_zbits_t ns;
};
/*
* The data in a RBT node has two pairs of bits for policy zones.
* One pair is for the corresponding name of the node such as example.com
* and the other pair is for a wildcard child such as *.example.com.
*/
typedef struct dns_rpz_nm_data dns_rpz_nm_data_t;
struct dns_rpz_nm_data {
dns_rpz_nm_zbits_t set;
@@ -259,11 +259,15 @@ dns_rpz_policy2str(dns_rpz_policy_t policy) {
return (str);
}
/*
* Return the bit number of the highest set bit in 'zbit'.
* (for example, 0x01 returns 0, 0xFF returns 7, etc.)
*/
static int
zbit_to_num(dns_rpz_zbits_t zbit) {
dns_rpz_num_t rpz_num;
INSIST(zbit != 0);
REQUIRE(zbit != 0);
rpz_num = 0;
#if DNS_RPZ_MAX_ZONES > 32
if ((zbit & 0xffffffff00000000L) != 0) {
@@ -376,30 +380,172 @@ set_sum_pair(dns_rpz_cidr_node_t *cnode) {
static void
fix_qname_skip_recurse(dns_rpz_zones_t *rpzs) {
dns_rpz_zbits_t zbits;
dns_rpz_zbits_t mask;
/* qname_wait_recurse and qname_skip_recurse are used to
* implement the "qname-wait-recurse" config option.
*
* By default, "qname-wait-recurse" is yes, so no
* processing happens without recursion. In this case,
* qname_wait_recurse is true, and qname_skip_recurse
* (a bit field indicating which policy zones can be
* processed without recursion) is set to all 0's by
* fix_qname_skip_recurse().
*
* When "qname-wait-recurse" is no, qname_skip_recurse may be
* set to a non-zero value by fix_qname_skip_recurse(). The mask
* has to have bits set for for the policy zones for which
* processing may continue without recursion, and bits cleared
* for the rest.
*
* (1) The ARM says:
*
* The "qname-wait-recurse no" option overrides that default
* behavior when recursion cannot change a non-error
* response. The option does not affect QNAME or client-IP
* triggers in policy zones listed after other zones
* containing IP, NSIP and NSDNAME triggers, because those may
* depend on the A, AAAA, and NS records that would be found
* during recursive resolution.
*
* Let's consider the following:
*
* zbits_req = (rpzs->have.ipv4 | rpzs->have.ipv6 |
* rpzs->have.nsdname |
* rpzs->have.nsipv4 | rpzs->have.nsipv6);
*
* zbits_req now contains bits set for zones which require
* recursion.
*
* But going by the description in the ARM, if the first policy
* zone requires recursion, then all zones after that (higher
* order bits) have to wait as well. If the Nth zone requires
* recursion, then (N+1)th zone onwards all need to wait.
*
* So mapping this, examples:
*
* zbits_req = 0b000 mask = 0xffffffff (no zones have to wait for
* recursion)
* zbits_req = 0b001 mask = 0x00000000 (all zones have to wait)
* zbits_req = 0b010 mask = 0x00000001 (the first zone doesn't have to
* wait, second zone onwards need
* to wait)
* zbits_req = 0b011 mask = 0x00000000 (all zones have to wait)
* zbits_req = 0b100 mask = 0x00000011 (the 1st and 2nd zones don't
* have to wait, third zone
* onwards need to wait)
*
* More generally, we have to count the number of trailing 0
* bits in zbits_req and only these can be processed without
* recursion. All the rest need to wait.
*
* (2) The ARM says that "qname-wait-recurse no" option
* overrides the default behavior when recursion cannot change a
* non-error response. So, in the order of listing of policy
* zones, within the first policy zone where recursion may be
* required, we should first allow CLIENT-IP and QNAME policy
* records to be attempted without recursion.
*/
/*
* Get a mask covering all policy zones that are not subordinate to
* other policy zones containing triggers that require that the
* qname be resolved before they can be checked.
*/
if (rpzs->p.qname_wait_recurse) {
zbits = 0;
} else {
zbits = (rpzs->have.ipv4 || rpzs->have.ipv6 ||
rpzs->have.nsdname ||
rpzs->have.nsipv4 || rpzs->have.nsipv6);
if (zbits == 0) {
zbits = DNS_RPZ_ALL_ZBITS;
} else {
zbits = DNS_RPZ_ZMASK(zbit_to_num(zbits));
}
}
rpzs->have.qname_skip_recurse = zbits;
rpzs->have.client_ip = rpzs->have.client_ipv4 | rpzs->have.client_ipv6;
rpzs->have.ip = rpzs->have.ipv4 | rpzs->have.ipv6;
rpzs->have.nsip = rpzs->have.nsipv4 | rpzs->have.nsipv6;
if (rpzs->p.qname_wait_recurse) {
mask = 0;
} else {
dns_rpz_zbits_t zbits_req;
dns_rpz_zbits_t zbits_notreq;
dns_rpz_zbits_t mask2;
dns_rpz_zbits_t req_mask;
/*
* Get the masks of zones with policies that
* do/don't require recursion
*/
zbits_req = (rpzs->have.ipv4 | rpzs->have.ipv6 |
rpzs->have.nsdname |
rpzs->have.nsipv4 | rpzs->have.nsipv6);
zbits_notreq = (rpzs->have.client_ip | rpzs->have.qname);
if (zbits_req == 0) {
mask = DNS_RPZ_ALL_ZBITS;
goto set;
}
/*
* req_mask is a mask covering used bits in
* zbits_req. (For instance, 0b1 => 0b1, 0b101 => 0b111,
* 0b11010101 => 0b11111111).
*/
req_mask = zbits_req;
req_mask |= req_mask >> 1;
req_mask |= req_mask >> 2;
req_mask |= req_mask >> 4;
req_mask |= req_mask >> 8;
req_mask |= req_mask >> 16;
#if DNS_RPZ_MAX_ZONES > 32
req_mask |= req_mask >> 32;
#endif
/*
* There's no point in skipping recursion for a later
* zone if it is required in a previous zone.
*/
if ((zbits_notreq & req_mask) == 0) {
mask = 0;
goto set;
}
/*
* This bit arithmetic creates a mask of zones in which
* it is okay to skip recursion. After the first zone
* that has to wait for recursion, all the others have
* to wait as well, so we want to create a mask in which
* all the trailing zeroes in zbits_req are are 1, and
* more significant bits are 0. (For instance,
* 0x0700 => 0x00ff, 0x0007 => 0x0000)
*/
mask = ~(zbits_req | -zbits_req);
/*
* As mentioned in (2) above, the zone corresponding to
* the least significant zero could have its CLIENT-IP
* and QNAME policies checked before recursion, if it
* has any of those policies. So if it does, we
* can set its 0 to 1.
*
* Locate the least significant 0 bit in the mask (for
* instance, 0xff => 0x100)...
*/
mask2 = (mask << 1) & ~mask;
/*
* Also set the bit for zone 0, because if it's in
* zbits_notreq then it's definitely okay to attempt to
* skip recursion for zone 0...
*/
mask2 |= 1;
/* Clear any bits *not* in zbits_notreq... */
mask2 &= zbits_notreq;
/* And merge the result into the skip-recursion mask */
mask |= mask2;
}
set:
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB,
DNS_RPZ_DEBUG_QUIET,
"computed RPZ qname_skip_recurse mask=0x%llx",
(isc_uint64_t) mask);
rpzs->have.qname_skip_recurse = mask;
}
static void