[master] DNS Response Policy Service API
4713. [func] Added support for the DNS Response Policy Service (DNSRPS) API, which allows named to use an external response policy daemon when built with "configure --enable-dnsrps". Thanks to Vernon Schryver and Farsight Security. [RT #43376]
This commit is contained in:
212
lib/dns/rpz.c
212
lib/dns/rpz.c
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <dns/db.h>
|
||||
#include <dns/dbiterator.h>
|
||||
#include <dns/dnsrps.h>
|
||||
#include <dns/events.h>
|
||||
#include <dns/fixedname.h>
|
||||
#include <dns/log.h>
|
||||
@@ -397,11 +398,10 @@ fix_qname_skip_recurse(dns_rpz_zones_t *rpzs) {
|
||||
* 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
|
||||
* When "qname-wait-recurse" is yes, 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
|
||||
@@ -681,6 +681,12 @@ badname(int level, const dns_name_t *name, const char *str1, const char *str2) {
|
||||
* Convert an IP address from radix tree binary (host byte order) to
|
||||
* to its canonical response policy domain name without the origin of the
|
||||
* policy zone.
|
||||
*
|
||||
* Generate a name for an IPv6 address that fits RFC 5952, except that our
|
||||
* reversed format requires that when the length of the consecutive 16-bit
|
||||
* 0 fields are equal (e.g., 1.0.0.1.0.0.db8.2001 corresponding to
|
||||
* 2001:db8:0:0:1:0:0:1), we shorted the last instead of the first
|
||||
* (e.g., 1.0.0.1.zz.db8.2001 corresponding to 2001:db8::1:0:0:1).
|
||||
*/
|
||||
static isc_result_t
|
||||
ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
|
||||
@@ -693,7 +699,7 @@ ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
|
||||
char str[1+8+1+INET6_ADDRSTRLEN+1];
|
||||
isc_buffer_t buffer;
|
||||
isc_result_t result;
|
||||
isc_boolean_t zeros;
|
||||
int best_first, best_len, cur_first, cur_len;
|
||||
int i, n, len;
|
||||
|
||||
if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
|
||||
@@ -703,43 +709,53 @@ ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
|
||||
(tgt_ip->w[3]>>8) & 0xff,
|
||||
(tgt_ip->w[3]>>16) & 0xff,
|
||||
(tgt_ip->w[3]>>24) & 0xff);
|
||||
if (len < 0 || len > (int)sizeof(str))
|
||||
if (len < 0 || len > (int)sizeof(str)) {
|
||||
return (ISC_R_FAILURE);
|
||||
}
|
||||
} else {
|
||||
len = snprintf(str, sizeof(str), "%d", tgt_prefix);
|
||||
if (len == -1) {
|
||||
return (ISC_R_FAILURE);
|
||||
}
|
||||
|
||||
for (i = 0; i < DNS_RPZ_CIDR_WORDS; i++) {
|
||||
w[i*2+1] = ((tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] >> 16)
|
||||
& 0xffff);
|
||||
w[i*2] = tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] & 0xffff;
|
||||
}
|
||||
zeros = ISC_FALSE;
|
||||
len = snprintf(str, sizeof(str), "%d", tgt_prefix);
|
||||
if (len == -1)
|
||||
return (ISC_R_FAILURE);
|
||||
i = 0;
|
||||
while (i < DNS_RPZ_CIDR_WORDS * 2) {
|
||||
if (w[i] != 0 || zeros ||
|
||||
i >= DNS_RPZ_CIDR_WORDS * 2 - 1 ||
|
||||
w[i+1] != 0) {
|
||||
INSIST((size_t)len <= sizeof(str));
|
||||
n = snprintf(&str[len], sizeof(str) - len,
|
||||
".%x", w[i++]);
|
||||
if (n < 0)
|
||||
return (ISC_R_FAILURE);
|
||||
len += n;
|
||||
/*
|
||||
* Find the start and length of the first longest sequence
|
||||
* of zeros in the address.
|
||||
*/
|
||||
best_first = -1;
|
||||
best_len = 0;
|
||||
cur_first = -1;
|
||||
cur_len = 0;
|
||||
for (n = 0; n <=7; ++n) {
|
||||
if (w[n] != 0) {
|
||||
cur_len = 0;
|
||||
cur_first = -1;
|
||||
} else {
|
||||
zeros = ISC_TRUE;
|
||||
INSIST((size_t)len <= sizeof(str));
|
||||
n = snprintf(&str[len], sizeof(str) - len,
|
||||
".zz");
|
||||
if (n < 0)
|
||||
return (ISC_R_FAILURE);
|
||||
len += n;
|
||||
i += 2;
|
||||
while (i < DNS_RPZ_CIDR_WORDS * 2 && w[i] == 0)
|
||||
++i;
|
||||
++cur_len;
|
||||
if (cur_first < 0) {
|
||||
cur_first = n;
|
||||
} else if (cur_len >= best_len) {
|
||||
best_first = cur_first;
|
||||
best_len = cur_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (n = 0; n <= 7; ++n) {
|
||||
INSIST(len < (int)sizeof(str));
|
||||
if (n == best_first) {
|
||||
len += snprintf(str + len, sizeof(str) - len,
|
||||
".zz");
|
||||
n += best_len - 1;
|
||||
} else {
|
||||
len += snprintf(str + len, sizeof(str) - len,
|
||||
".%x", w[n]);
|
||||
}
|
||||
if (len >= (int)sizeof(str))
|
||||
return (ISC_R_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,26 +766,31 @@ ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the type a of a name in a response policy zone.
|
||||
* Determine the type of a name in a response policy zone.
|
||||
*/
|
||||
static dns_rpz_type_t
|
||||
type_from_name(dns_rpz_zone_t *rpz, const dns_name_t *name) {
|
||||
|
||||
if (dns_name_issubdomain(name, &rpz->ip))
|
||||
type_from_name(const dns_rpz_zones_t *rpzs,
|
||||
dns_rpz_zone_t *rpz, const dns_name_t *name)
|
||||
{
|
||||
if (dns_name_issubdomain(name, &rpz->ip)) {
|
||||
return (DNS_RPZ_TYPE_IP);
|
||||
}
|
||||
|
||||
if (dns_name_issubdomain(name, &rpz->client_ip))
|
||||
if (dns_name_issubdomain(name, &rpz->client_ip)) {
|
||||
return (DNS_RPZ_TYPE_CLIENT_IP);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_RPZ_NSIP
|
||||
if (dns_name_issubdomain(name, &rpz->nsip))
|
||||
if ((rpzs->p.nsip_on & DNS_RPZ_ZBIT(rpz->num)) != 0 &&
|
||||
dns_name_issubdomain(name, &rpz->nsip))
|
||||
{
|
||||
return (DNS_RPZ_TYPE_NSIP);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_RPZ_NSDNAME
|
||||
if (dns_name_issubdomain(name, &rpz->nsdname))
|
||||
if ((rpzs->p.nsdname_on & DNS_RPZ_ZBIT(rpz->num)) != 0 &&
|
||||
dns_name_issubdomain(name, &rpz->nsdname))
|
||||
{
|
||||
return (DNS_RPZ_TYPE_NSDNAME);
|
||||
#endif
|
||||
}
|
||||
|
||||
return (DNS_RPZ_TYPE_QNAME);
|
||||
}
|
||||
@@ -787,7 +808,7 @@ name2ipkey(int log_level,
|
||||
dns_rpz_addr_zbits_t *new_set)
|
||||
{
|
||||
dns_rpz_zone_t *rpz;
|
||||
char ip_str[DNS_NAME_FORMATSIZE];
|
||||
char ip_str[DNS_NAME_FORMATSIZE], ip2_str[DNS_NAME_FORMATSIZE];
|
||||
dns_offsets_t ip_name_offsets;
|
||||
dns_fixedname_t ip_name2f;
|
||||
dns_name_t ip_name, *ip_name2;
|
||||
@@ -830,7 +851,6 @@ name2ipkey(int log_level,
|
||||
"; invalid leading prefix length", "");
|
||||
return (ISC_R_FAILURE);
|
||||
}
|
||||
*cp2 = '\0';
|
||||
if (prefix_num < 1U || prefix_num > 128U) {
|
||||
badname(log_level, src_name,
|
||||
"; invalid prefix length of ", prefix_str);
|
||||
@@ -926,21 +946,27 @@ name2ipkey(int log_level,
|
||||
}
|
||||
|
||||
/*
|
||||
* XXXMUKS: Should the following check be enabled in a
|
||||
* production build? It can be expensive for large IP zones
|
||||
* from 3rd parties.
|
||||
* Complain about bad names but be generous and accept them.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Convert the address back to a canonical domain name
|
||||
* to ensure that the original name is in canonical form.
|
||||
*/
|
||||
dns_fixedname_init(&ip_name2f);
|
||||
ip_name2 = dns_fixedname_name(&ip_name2f);
|
||||
result = ip2name(tgt_ip, (dns_rpz_prefix_t)prefix_num, NULL, ip_name2);
|
||||
if (result != ISC_R_SUCCESS || !dns_name_equal(&ip_name, ip_name2)) {
|
||||
badname(log_level, src_name, "; not canonical", "");
|
||||
return (ISC_R_FAILURE);
|
||||
if (log_level < DNS_RPZ_DEBUG_QUIET &&
|
||||
isc_log_wouldlog(dns_lctx, log_level)) {
|
||||
/*
|
||||
* Convert the address back to a canonical domain name
|
||||
* to ensure that the original name is in canonical form.
|
||||
*/
|
||||
dns_fixedname_init(&ip_name2f);
|
||||
ip_name2 = dns_fixedname_name(&ip_name2f);
|
||||
result = ip2name(tgt_ip, (dns_rpz_prefix_t)prefix_num,
|
||||
NULL, ip_name2);
|
||||
if (result != ISC_R_SUCCESS ||
|
||||
!dns_name_equal(&ip_name, ip_name2)) {
|
||||
dns_name_format(ip_name2, ip2_str, sizeof(ip2_str));
|
||||
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
|
||||
DNS_LOGMODULE_RBTDB, log_level,
|
||||
"rpz IP address \"%s\""
|
||||
" is not the canonical \"%s\"",
|
||||
ip_str, ip2_str);
|
||||
}
|
||||
}
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
@@ -1396,10 +1422,11 @@ rpz_node_deleter(void *nm_data, void *mctx) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Get ready for a new set of policy zones.
|
||||
* Get ready for a new set of policy zones for a view.
|
||||
*/
|
||||
isc_result_t
|
||||
dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx,
|
||||
dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, char *rps_cstr,
|
||||
size_t rps_cstr_size, isc_mem_t *mctx,
|
||||
isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
|
||||
{
|
||||
dns_rpz_zones_t *zones;
|
||||
@@ -1424,7 +1451,20 @@ dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx,
|
||||
if (result != ISC_R_SUCCESS)
|
||||
goto cleanup_refcount;
|
||||
|
||||
result = dns_rbt_create(mctx, rpz_node_deleter, mctx, &zones->rbt);
|
||||
zones->rps_cstr = rps_cstr;
|
||||
zones->rps_cstr_size = rps_cstr_size;
|
||||
#ifdef USE_DNSRPS
|
||||
if (rps_cstr != NULL) {
|
||||
result = dns_dnsrps_view_init(zones, rps_cstr);
|
||||
}
|
||||
#else
|
||||
INSIST(!zones->p.dnsrps_enabled);
|
||||
#endif
|
||||
if (result == ISC_R_SUCCESS && !zones->p.dnsrps_enabled) {
|
||||
result = dns_rbt_create(mctx, rpz_node_deleter,
|
||||
mctx, &zones->rbt);
|
||||
}
|
||||
|
||||
if (result != ISC_R_SUCCESS)
|
||||
goto cleanup_rbt;
|
||||
|
||||
@@ -2074,26 +2114,38 @@ dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp) {
|
||||
|
||||
*rpzsp = NULL;
|
||||
isc_refcount_decrement(&rpzs->refs, &refs);
|
||||
if (refs != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Forget the last of view's rpz machinery after the last reference.
|
||||
*/
|
||||
if (refs == 0) {
|
||||
for (rpz_num = 0; rpz_num < DNS_RPZ_MAX_ZONES; ++rpz_num) {
|
||||
rpz = rpzs->zones[rpz_num];
|
||||
rpzs->zones[rpz_num] = NULL;
|
||||
if (rpz != NULL)
|
||||
rpz_detach(&rpz, rpzs);
|
||||
for (rpz_num = 0; rpz_num < DNS_RPZ_MAX_ZONES; ++rpz_num) {
|
||||
rpz = rpzs->zones[rpz_num];
|
||||
rpzs->zones[rpz_num] = NULL;
|
||||
if (rpz != NULL) {
|
||||
rpz_detach(&rpz, rpzs);
|
||||
}
|
||||
|
||||
cidr_free(rpzs);
|
||||
dns_rbt_destroy(&rpzs->rbt);
|
||||
DESTROYLOCK(&rpzs->maint_lock);
|
||||
isc_rwlock_destroy(&rpzs->search_lock);
|
||||
isc_refcount_destroy(&rpzs->refs);
|
||||
isc_task_destroy(&rpzs->updater);
|
||||
isc_mem_putanddetach(&rpzs->mctx, rpzs, sizeof(*rpzs));
|
||||
}
|
||||
|
||||
if (rpzs->rps_cstr_size != 0) {
|
||||
#ifdef USE_DNSRPS
|
||||
librpz->client_detach(&rpzs->rps_client);
|
||||
#endif
|
||||
isc_mem_put(rpzs->mctx, rpzs->rps_cstr,
|
||||
rpzs->rps_cstr_size);
|
||||
}
|
||||
|
||||
cidr_free(rpzs);
|
||||
if (rpzs->rbt != NULL) {
|
||||
dns_rbt_destroy(&rpzs->rbt);
|
||||
}
|
||||
DESTROYLOCK(&rpzs->maint_lock);
|
||||
isc_rwlock_destroy(&rpzs->search_lock);
|
||||
isc_refcount_destroy(&rpzs->refs);
|
||||
isc_task_destroy(&rpzs->updater);
|
||||
isc_mem_putanddetach(&rpzs->mctx, rpzs, sizeof(*rpzs));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2140,7 +2192,7 @@ dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
|
||||
REQUIRE(rpz != NULL);
|
||||
RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
|
||||
|
||||
rpz_type = type_from_name(rpz, src_name);
|
||||
rpz_type = type_from_name(rpzs, rpz, src_name);
|
||||
|
||||
|
||||
switch (rpz_type) {
|
||||
@@ -2350,7 +2402,7 @@ dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
|
||||
|
||||
RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
|
||||
|
||||
rpz_type = type_from_name(rpz, src_name);
|
||||
rpz_type = type_from_name(rpzs, rpz, src_name);
|
||||
|
||||
switch (rpz_type) {
|
||||
case DNS_RPZ_TYPE_QNAME:
|
||||
|
||||
Reference in New Issue
Block a user