1999-12-16 23:11:07 +00:00
|
|
|
/*
|
2016-06-27 14:56:38 +10:00
|
|
|
* Copyright (C) 1999-2002, 2004-2009, 2011, 2013, 2014, 2016 Internet Systems Consortium, Inc. ("ISC")
|
2000-08-01 01:33:37 +00:00
|
|
|
*
|
2016-06-27 14:56:38 +10:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
1999-12-16 23:11:07 +00:00
|
|
|
*/
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*! \file */
|
2000-06-22 22:00:42 +00:00
|
|
|
|
1999-12-16 23:11:07 +00:00
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <isc/mem.h>
|
2007-09-19 03:03:29 +00:00
|
|
|
#include <isc/once.h>
|
2000-05-08 14:38:29 +00:00
|
|
|
#include <isc/string.h>
|
2000-04-28 01:12:23 +00:00
|
|
|
#include <isc/util.h>
|
1999-12-16 23:11:07 +00:00
|
|
|
|
|
|
|
#include <dns/acl.h>
|
2007-09-12 01:09:08 +00:00
|
|
|
#include <dns/iptable.h>
|
1999-12-16 23:11:07 +00:00
|
|
|
|
2013-02-27 17:19:39 -08:00
|
|
|
|
2007-09-12 01:09:08 +00:00
|
|
|
/*
|
|
|
|
* Create a new ACL, including an IP table and an array with room
|
|
|
|
* for 'n' ACL elements. The elements are uninitialized and the
|
|
|
|
* length is 0.
|
|
|
|
*/
|
2000-01-13 23:38:55 +00:00
|
|
|
isc_result_t
|
2000-05-08 14:38:29 +00:00
|
|
|
dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
|
2000-01-13 23:38:55 +00:00
|
|
|
isc_result_t result;
|
|
|
|
dns_acl_t *acl;
|
2000-02-09 22:59:40 +00:00
|
|
|
|
2000-05-08 14:38:29 +00:00
|
|
|
/*
|
|
|
|
* Work around silly limitation of isc_mem_get().
|
|
|
|
*/
|
2000-02-09 22:59:40 +00:00
|
|
|
if (n == 0)
|
|
|
|
n = 1;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2000-01-13 23:38:55 +00:00
|
|
|
acl = isc_mem_get(mctx, sizeof(*acl));
|
|
|
|
if (acl == NULL)
|
|
|
|
return (ISC_R_NOMEMORY);
|
2013-02-20 21:39:05 -08:00
|
|
|
|
|
|
|
acl->mctx = NULL;
|
|
|
|
isc_mem_attach(mctx, &acl->mctx);
|
|
|
|
|
2000-01-13 23:38:55 +00:00
|
|
|
acl->name = NULL;
|
2007-09-12 01:09:08 +00:00
|
|
|
|
2005-07-12 01:00:20 +00:00
|
|
|
result = isc_refcount_init(&acl->refcount, 1);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
isc_mem_put(mctx, acl, sizeof(*acl));
|
|
|
|
return (result);
|
|
|
|
}
|
2007-09-12 01:09:08 +00:00
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
result = dns_iptable_create(mctx, &acl->iptable);
|
2007-09-12 01:09:08 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
isc_mem_put(mctx, acl, sizeof(*acl));
|
|
|
|
return (result);
|
|
|
|
}
|
2007-12-20 01:48:29 +00:00
|
|
|
|
2000-01-13 23:38:55 +00:00
|
|
|
acl->elements = NULL;
|
|
|
|
acl->alloc = 0;
|
|
|
|
acl->length = 0;
|
2007-12-21 06:46:47 +00:00
|
|
|
acl->has_negatives = ISC_FALSE;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2000-01-13 23:38:55 +00:00
|
|
|
ISC_LINK_INIT(acl, nextincache);
|
2000-05-08 14:38:29 +00:00
|
|
|
/*
|
|
|
|
* Must set magic early because we use dns_acl_detach() to clean up.
|
|
|
|
*/
|
2000-08-01 01:33:37 +00:00
|
|
|
acl->magic = DNS_ACL_MAGIC;
|
2000-01-13 23:38:55 +00:00
|
|
|
|
|
|
|
acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t));
|
|
|
|
if (acl->elements == NULL) {
|
|
|
|
result = ISC_R_NOMEMORY;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
acl->alloc = n;
|
|
|
|
memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
|
|
|
|
*target = acl;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
dns_acl_detach(&acl);
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
2007-09-12 01:09:08 +00:00
|
|
|
/*
|
|
|
|
* Create a new ACL and initialize it with the value "any" or "none",
|
|
|
|
* depending on the value of the "neg" parameter.
|
|
|
|
* "any" is a positive iptable entry with bit length 0.
|
|
|
|
* "none" is the same as "!any".
|
|
|
|
*/
|
2000-01-13 23:38:55 +00:00
|
|
|
static isc_result_t
|
2000-05-08 14:38:29 +00:00
|
|
|
dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
|
2000-01-13 23:38:55 +00:00
|
|
|
isc_result_t result;
|
|
|
|
dns_acl_t *acl = NULL;
|
2011-06-17 07:05:02 +00:00
|
|
|
|
2007-09-12 01:09:08 +00:00
|
|
|
result = dns_acl_create(mctx, 0, &acl);
|
2000-01-13 23:38:55 +00:00
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
2008-01-17 08:08:08 +00:00
|
|
|
|
|
|
|
result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
2008-01-18 23:46:58 +00:00
|
|
|
dns_acl_detach(&acl);
|
2008-01-17 08:08:08 +00:00
|
|
|
return (result);
|
2008-01-18 23:46:58 +00:00
|
|
|
}
|
2008-01-17 08:08:08 +00:00
|
|
|
|
2000-01-13 23:38:55 +00:00
|
|
|
*target = acl;
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
2007-09-12 01:09:08 +00:00
|
|
|
/*
|
|
|
|
* Create a new ACL that matches everything.
|
|
|
|
*/
|
2000-01-13 23:38:55 +00:00
|
|
|
isc_result_t
|
|
|
|
dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
|
|
|
|
return (dns_acl_anyornone(mctx, ISC_FALSE, target));
|
|
|
|
}
|
|
|
|
|
2007-09-12 01:09:08 +00:00
|
|
|
/*
|
|
|
|
* Create a new ACL that matches nothing.
|
|
|
|
*/
|
2000-01-13 23:38:55 +00:00
|
|
|
isc_result_t
|
|
|
|
dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
|
|
|
|
return (dns_acl_anyornone(mctx, ISC_TRUE, target));
|
|
|
|
}
|
|
|
|
|
2007-09-12 01:09:08 +00:00
|
|
|
/*
|
|
|
|
* If pos is ISC_TRUE, test whether acl is set to "{ any; }"
|
|
|
|
* If pos is ISC_FALSE, test whether acl is set to "{ none; }"
|
|
|
|
*/
|
|
|
|
static isc_boolean_t
|
|
|
|
dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
|
|
|
|
{
|
2007-12-20 01:48:29 +00:00
|
|
|
/* Should never happen but let's be safe */
|
|
|
|
if (acl == NULL ||
|
|
|
|
acl->iptable == NULL ||
|
|
|
|
acl->iptable->radix == NULL ||
|
|
|
|
acl->iptable->radix->head == NULL ||
|
|
|
|
acl->iptable->radix->head->prefix == NULL)
|
|
|
|
return (ISC_FALSE);
|
2007-09-12 01:09:08 +00:00
|
|
|
|
2008-09-12 04:54:39 +00:00
|
|
|
if (acl->length != 0 || acl->node_count != 1)
|
2007-09-12 01:09:08 +00:00
|
|
|
return (ISC_FALSE);
|
|
|
|
|
|
|
|
if (acl->iptable->radix->head->prefix->bitlen == 0 &&
|
2008-09-12 04:54:39 +00:00
|
|
|
acl->iptable->radix->head->data[0] != NULL &&
|
2008-09-26 16:44:49 +00:00
|
|
|
acl->iptable->radix->head->data[0] ==
|
2008-09-26 23:47:06 +00:00
|
|
|
acl->iptable->radix->head->data[1] &&
|
2008-04-29 01:01:42 +00:00
|
|
|
*(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
|
2007-09-12 01:09:08 +00:00
|
|
|
return (ISC_TRUE);
|
|
|
|
|
|
|
|
return (ISC_FALSE); /* All others */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test whether acl is set to "{ any; }"
|
|
|
|
*/
|
|
|
|
isc_boolean_t
|
|
|
|
dns_acl_isany(dns_acl_t *acl)
|
|
|
|
{
|
2007-12-20 01:48:29 +00:00
|
|
|
return (dns_acl_isanyornone(acl, ISC_TRUE));
|
2007-09-12 01:09:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test whether acl is set to "{ none; }"
|
|
|
|
*/
|
|
|
|
isc_boolean_t
|
|
|
|
dns_acl_isnone(dns_acl_t *acl)
|
|
|
|
{
|
2007-12-20 01:48:29 +00:00
|
|
|
return (dns_acl_isanyornone(acl, ISC_FALSE));
|
2007-09-12 01:09:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine whether a given address or signer matches a given ACL.
|
2008-01-18 23:46:58 +00:00
|
|
|
* For a match with a positive ACL element or iptable radix entry,
|
2007-09-12 01:09:08 +00:00
|
|
|
* return with a positive value in match; for a match with a negated ACL
|
|
|
|
* element or radix entry, return with a negative value in match.
|
|
|
|
*/
|
1999-12-16 23:11:07 +00:00
|
|
|
isc_result_t
|
2006-02-28 02:39:52 +00:00
|
|
|
dns_acl_match(const isc_netaddr_t *reqaddr,
|
|
|
|
const dns_name_t *reqsigner,
|
|
|
|
const dns_acl_t *acl,
|
|
|
|
const dns_aclenv_t *env,
|
1999-12-16 23:11:07 +00:00
|
|
|
int *match,
|
2007-09-12 01:09:08 +00:00
|
|
|
const dns_aclelement_t **matchelt)
|
1999-12-16 23:11:07 +00:00
|
|
|
{
|
2014-08-28 22:05:57 -07:00
|
|
|
return (dns_acl_match2(reqaddr, reqsigner, NULL, 0, NULL, acl, env,
|
|
|
|
match, matchelt));
|
|
|
|
}
|
|
|
|
|
|
|
|
isc_result_t
|
|
|
|
dns_acl_match2(const isc_netaddr_t *reqaddr,
|
|
|
|
const dns_name_t *reqsigner,
|
2014-08-29 23:45:22 +00:00
|
|
|
const isc_netaddr_t *ecs,
|
2014-08-28 22:05:57 -07:00
|
|
|
isc_uint8_t ecslen,
|
|
|
|
isc_uint8_t *scope,
|
|
|
|
const dns_acl_t *acl,
|
|
|
|
const dns_aclenv_t *env,
|
|
|
|
int *match,
|
|
|
|
const dns_aclelement_t **matchelt)
|
|
|
|
{
|
|
|
|
isc_uint16_t bitlen;
|
2007-09-12 01:09:08 +00:00
|
|
|
isc_prefix_t pfx;
|
2008-12-01 00:04:21 +00:00
|
|
|
isc_radix_node_t *node = NULL;
|
2014-08-28 22:05:57 -07:00
|
|
|
const isc_netaddr_t *addr = reqaddr;
|
2007-12-20 01:48:29 +00:00
|
|
|
isc_netaddr_t v4addr;
|
2007-09-12 01:09:08 +00:00
|
|
|
isc_result_t result;
|
2007-12-20 01:48:29 +00:00
|
|
|
int match_num = -1;
|
2001-01-17 17:44:49 +00:00
|
|
|
unsigned int i;
|
1999-12-16 23:11:07 +00:00
|
|
|
|
2000-03-14 04:01:52 +00:00
|
|
|
REQUIRE(reqaddr != NULL);
|
1999-12-16 23:11:07 +00:00
|
|
|
REQUIRE(matchelt == NULL || *matchelt == NULL);
|
2014-08-28 22:05:57 -07:00
|
|
|
REQUIRE(ecs != NULL || scope == NULL);
|
2007-09-12 01:09:08 +00:00
|
|
|
|
2014-08-28 22:05:57 -07:00
|
|
|
if (env != NULL && env->match_mapped &&
|
|
|
|
addr->family == AF_INET6 &&
|
|
|
|
IN6_IS_ADDR_V4MAPPED(&addr->type.in6))
|
|
|
|
{
|
|
|
|
isc_netaddr_fromv4mapped(&v4addr, addr);
|
2007-09-12 01:09:08 +00:00
|
|
|
addr = &v4addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Always match with host addresses. */
|
2014-08-28 22:05:57 -07:00
|
|
|
bitlen = (addr->family == AF_INET6) ? 128 : 32;
|
|
|
|
NETADDR_TO_PREFIX_T(addr, pfx, bitlen, ISC_FALSE);
|
2007-09-12 01:09:08 +00:00
|
|
|
|
|
|
|
/* Assume no match. */
|
|
|
|
*match = 0;
|
|
|
|
|
|
|
|
/* Search radix. */
|
|
|
|
result = isc_radix_search(acl->iptable->radix, &node, &pfx);
|
|
|
|
|
|
|
|
/* Found a match. */
|
|
|
|
if (result == ISC_R_SUCCESS && node != NULL) {
|
2014-08-28 22:05:57 -07:00
|
|
|
int off = ISC_RADIX_OFF(&pfx);
|
|
|
|
match_num = node->node_num[off];
|
|
|
|
if (*(isc_boolean_t *) node->data[off])
|
2007-12-20 01:48:29 +00:00
|
|
|
*match = match_num;
|
|
|
|
else
|
|
|
|
*match = -match_num;
|
|
|
|
}
|
2007-09-12 01:09:08 +00:00
|
|
|
|
2014-08-28 22:05:57 -07:00
|
|
|
isc_refcount_destroy(&pfx.refcount);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If ecs is not NULL, we search the radix tree again to
|
|
|
|
* see if we find a better match on an ECS node
|
|
|
|
*/
|
|
|
|
if (ecs != NULL) {
|
|
|
|
node = NULL;
|
|
|
|
addr = ecs;
|
|
|
|
|
|
|
|
if (env != NULL && env->match_mapped &&
|
|
|
|
addr->family == AF_INET6 &&
|
|
|
|
IN6_IS_ADDR_V4MAPPED(&addr->type.in6))
|
|
|
|
{
|
|
|
|
isc_netaddr_fromv4mapped(&v4addr, addr);
|
|
|
|
addr = &v4addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
NETADDR_TO_PREFIX_T(addr, pfx, ecslen, ISC_TRUE);
|
|
|
|
|
|
|
|
result = isc_radix_search(acl->iptable->radix, &node, &pfx);
|
|
|
|
if (result == ISC_R_SUCCESS && node != NULL) {
|
|
|
|
int off = ISC_RADIX_OFF(&pfx);
|
|
|
|
if (match_num == -1 ||
|
|
|
|
node->node_num[off] < match_num)
|
|
|
|
{
|
|
|
|
match_num = node->node_num[off];
|
|
|
|
if (scope != NULL)
|
|
|
|
*scope = node->bit;
|
|
|
|
if (*(isc_boolean_t *) node->data[off])
|
|
|
|
*match = match_num;
|
|
|
|
else
|
|
|
|
*match = -match_num;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isc_refcount_destroy(&pfx.refcount);
|
|
|
|
}
|
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
/* Now search non-radix elements for a match with a lower node_num. */
|
2001-01-17 17:44:49 +00:00
|
|
|
for (i = 0; i < acl->length; i++) {
|
1999-12-16 23:11:07 +00:00
|
|
|
dns_aclelement_t *e = &acl->elements[i];
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2007-12-21 06:46:47 +00:00
|
|
|
/* Already found a better match? */
|
2008-09-10 21:52:49 +00:00
|
|
|
if (match_num != -1 && match_num < e->node_num) {
|
2014-08-28 22:05:57 -07:00
|
|
|
break;
|
2008-09-10 21:52:49 +00:00
|
|
|
}
|
2007-12-21 06:46:47 +00:00
|
|
|
|
2014-08-28 22:05:57 -07:00
|
|
|
if (dns_aclelement_match2(reqaddr, reqsigner, ecs, ecslen,
|
|
|
|
scope, e, env, matchelt))
|
|
|
|
{
|
2007-12-20 01:48:29 +00:00
|
|
|
if (match_num == -1 || e->node_num < match_num) {
|
2014-08-28 22:05:57 -07:00
|
|
|
if (e->negative)
|
2007-12-20 01:48:29 +00:00
|
|
|
*match = -e->node_num;
|
|
|
|
else
|
|
|
|
*match = e->node_num;
|
|
|
|
}
|
2014-08-28 22:05:57 -07:00
|
|
|
break;
|
1999-12-16 23:11:07 +00:00
|
|
|
}
|
|
|
|
}
|
2007-09-12 01:09:08 +00:00
|
|
|
|
1999-12-16 23:11:07 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2007-09-12 01:09:08 +00:00
|
|
|
/*
|
|
|
|
* Merge the contents of one ACL into another. Call dns_iptable_merge()
|
|
|
|
* for the IP tables, then concatenate the element arrays.
|
|
|
|
*
|
|
|
|
* If pos is set to false, then the nested ACL is to be negated. This
|
|
|
|
* means reverse the sense of each *positive* element or IP table node,
|
|
|
|
* but leave negatives alone, so as to prevent a double-negative causing
|
2009-01-17 14:18:27 +00:00
|
|
|
* an unexpected positive match in the parent ACL.
|
2007-09-12 01:09:08 +00:00
|
|
|
*/
|
2002-10-29 04:40:26 +00:00
|
|
|
isc_result_t
|
2007-09-12 01:09:08 +00:00
|
|
|
dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
|
2002-10-29 04:40:26 +00:00
|
|
|
{
|
2007-09-14 01:46:06 +00:00
|
|
|
isc_result_t result;
|
2007-12-20 01:48:29 +00:00
|
|
|
unsigned int newalloc, nelem, i;
|
|
|
|
int max_node = 0, nodes;
|
|
|
|
|
|
|
|
/* Resize the element array if needed. */
|
|
|
|
if (dest->length + source->length > dest->alloc) {
|
|
|
|
void *newmem;
|
|
|
|
|
|
|
|
newalloc = dest->alloc + source->alloc;
|
|
|
|
if (newalloc < 4)
|
|
|
|
newalloc = 4;
|
|
|
|
|
|
|
|
newmem = isc_mem_get(dest->mctx,
|
|
|
|
newalloc * sizeof(dns_aclelement_t));
|
|
|
|
if (newmem == NULL)
|
|
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
|
2014-08-03 10:05:02 +10:00
|
|
|
/* Zero. */
|
|
|
|
memset(newmem, 0, newalloc * sizeof(dns_aclelement_t));
|
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
/* Copy in the original elements */
|
2014-01-08 16:27:10 -08:00
|
|
|
memmove(newmem, dest->elements,
|
|
|
|
dest->length * sizeof(dns_aclelement_t));
|
2007-12-20 01:48:29 +00:00
|
|
|
|
|
|
|
/* Release the memory for the old elements array */
|
|
|
|
isc_mem_put(dest->mctx, dest->elements,
|
|
|
|
dest->alloc * sizeof(dns_aclelement_t));
|
|
|
|
dest->elements = newmem;
|
|
|
|
dest->alloc = newalloc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now copy in the new elements, increasing their node_num
|
|
|
|
* values so as to keep the new ACL consistent. If we're
|
|
|
|
* negating, then negate positive elements, but keep negative
|
|
|
|
* elements the same for security reasons.
|
|
|
|
*/
|
|
|
|
nelem = dest->length;
|
|
|
|
dest->length += source->length;
|
|
|
|
for (i = 0; i < source->length; i++) {
|
|
|
|
if (source->elements[i].node_num > max_node)
|
|
|
|
max_node = source->elements[i].node_num;
|
|
|
|
|
|
|
|
/* Copy type. */
|
|
|
|
dest->elements[nelem + i].type = source->elements[i].type;
|
|
|
|
|
|
|
|
/* Adjust node numbering. */
|
2008-01-18 23:46:58 +00:00
|
|
|
dest->elements[nelem + i].node_num =
|
2007-12-20 01:48:29 +00:00
|
|
|
source->elements[i].node_num + dest->node_count;
|
|
|
|
|
|
|
|
/* Duplicate nested acl. */
|
2008-01-21 20:38:54 +00:00
|
|
|
if (source->elements[i].type == dns_aclelementtype_nestedacl &&
|
2007-12-20 01:48:29 +00:00
|
|
|
source->elements[i].nestedacl != NULL)
|
|
|
|
dns_acl_attach(source->elements[i].nestedacl,
|
|
|
|
&dest->elements[nelem + i].nestedacl);
|
|
|
|
|
|
|
|
/* Duplicate key name. */
|
|
|
|
if (source->elements[i].type == dns_aclelementtype_keyname) {
|
|
|
|
dns_name_init(&dest->elements[nelem+i].keyname, NULL);
|
|
|
|
result = dns_name_dup(&source->elements[i].keyname,
|
|
|
|
dest->mctx,
|
|
|
|
&dest->elements[nelem+i].keyname);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-05-01 15:03:11 +10:00
|
|
|
#ifdef HAVE_GEOIP
|
2014-04-30 20:20:56 -07:00
|
|
|
/* Duplicate GeoIP data */
|
|
|
|
if (source->elements[i].type == dns_aclelementtype_geoip) {
|
|
|
|
dest->elements[nelem + i].geoip_elem =
|
|
|
|
source->elements[i].geoip_elem;
|
|
|
|
}
|
2014-05-01 15:03:11 +10:00
|
|
|
#endif
|
2014-04-30 20:20:56 -07:00
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
/* reverse sense of positives if this is a negative acl */
|
2014-08-28 22:05:57 -07:00
|
|
|
if (!pos && !source->elements[i].negative) {
|
2007-12-20 01:48:29 +00:00
|
|
|
dest->elements[nelem + i].negative = ISC_TRUE;
|
|
|
|
} else {
|
2008-01-18 23:46:58 +00:00
|
|
|
dest->elements[nelem + i].negative =
|
2007-12-20 01:48:29 +00:00
|
|
|
source->elements[i].negative;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Merge the iptables. Make sure the destination ACL's
|
|
|
|
* node_count value is set correctly afterward.
|
|
|
|
*/
|
|
|
|
nodes = max_node + dest->node_count;
|
|
|
|
result = dns_iptable_merge(dest->iptable, source->iptable, pos);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
|
|
|
if (nodes > dest->node_count)
|
|
|
|
dest->node_count = nodes;
|
|
|
|
|
|
|
|
return (ISC_R_SUCCESS);
|
2002-10-29 04:40:26 +00:00
|
|
|
}
|
|
|
|
|
2007-09-12 01:09:08 +00:00
|
|
|
/*
|
|
|
|
* Like dns_acl_match, but matches against the single ACL element 'e'
|
|
|
|
* rather than a complete ACL, and returns ISC_TRUE iff it matched.
|
|
|
|
*
|
2009-01-17 14:18:27 +00:00
|
|
|
* To determine whether the match was positive or negative, the
|
2007-09-12 01:09:08 +00:00
|
|
|
* caller should examine e->negative. Since the element 'e' may be
|
|
|
|
* a reference to a named ACL or a nested ACL, a matching element
|
|
|
|
* returned through 'matchelt' is not necessarily 'e' itself.
|
|
|
|
*/
|
2000-11-10 03:16:26 +00:00
|
|
|
isc_boolean_t
|
2006-02-28 02:39:52 +00:00
|
|
|
dns_aclelement_match(const isc_netaddr_t *reqaddr,
|
|
|
|
const dns_name_t *reqsigner,
|
|
|
|
const dns_aclelement_t *e,
|
|
|
|
const dns_aclenv_t *env,
|
|
|
|
const dns_aclelement_t **matchelt)
|
2014-08-28 22:05:57 -07:00
|
|
|
{
|
|
|
|
return (dns_aclelement_match2(reqaddr, reqsigner, NULL, 0, NULL,
|
|
|
|
e, env, matchelt));
|
|
|
|
}
|
|
|
|
|
|
|
|
isc_boolean_t
|
|
|
|
dns_aclelement_match2(const isc_netaddr_t *reqaddr,
|
|
|
|
const dns_name_t *reqsigner,
|
2014-08-29 23:45:22 +00:00
|
|
|
const isc_netaddr_t *ecs,
|
2014-08-28 22:05:57 -07:00
|
|
|
isc_uint8_t ecslen,
|
|
|
|
isc_uint8_t *scope,
|
|
|
|
const dns_aclelement_t *e,
|
|
|
|
const dns_aclenv_t *env,
|
|
|
|
const dns_aclelement_t **matchelt)
|
2000-11-10 03:16:26 +00:00
|
|
|
{
|
|
|
|
dns_acl_t *inner = NULL;
|
|
|
|
int indirectmatch;
|
|
|
|
isc_result_t result;
|
2014-08-28 22:05:57 -07:00
|
|
|
#ifdef HAVE_GEOIP
|
|
|
|
const isc_netaddr_t *addr = NULL;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
REQUIRE(ecs != NULL || scope == NULL);
|
2000-11-10 03:16:26 +00:00
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
switch (e->type) {
|
|
|
|
case dns_aclelementtype_keyname:
|
2000-11-10 03:16:26 +00:00
|
|
|
if (reqsigner != NULL &&
|
2007-09-12 01:09:08 +00:00
|
|
|
dns_name_equal(reqsigner, &e->keyname)) {
|
2007-12-20 01:48:29 +00:00
|
|
|
if (matchelt != NULL)
|
|
|
|
*matchelt = e;
|
|
|
|
return (ISC_TRUE);
|
2013-02-27 17:19:39 -08:00
|
|
|
} else
|
2007-12-20 01:48:29 +00:00
|
|
|
return (ISC_FALSE);
|
|
|
|
|
|
|
|
case dns_aclelementtype_nestedacl:
|
|
|
|
inner = e->nestedacl;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dns_aclelementtype_localhost:
|
|
|
|
if (env == NULL || env->localhost == NULL)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
inner = env->localhost;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dns_aclelementtype_localnets:
|
|
|
|
if (env == NULL || env->localnets == NULL)
|
|
|
|
return (ISC_FALSE);
|
|
|
|
inner = env->localnets;
|
|
|
|
break;
|
|
|
|
|
2013-02-27 17:19:39 -08:00
|
|
|
#ifdef HAVE_GEOIP
|
|
|
|
case dns_aclelementtype_geoip:
|
2014-01-31 23:46:22 +00:00
|
|
|
if (env == NULL || env->geoip == NULL)
|
2014-01-30 17:03:32 -08:00
|
|
|
return (ISC_FALSE);
|
2014-08-28 22:05:57 -07:00
|
|
|
addr = (env->geoip_use_ecs && ecs != NULL) ? ecs : reqaddr;
|
|
|
|
return (dns_geoip_match(addr, scope, env->geoip,
|
|
|
|
&e->geoip_elem));
|
2013-02-27 17:19:39 -08:00
|
|
|
#endif
|
2007-12-20 01:48:29 +00:00
|
|
|
default:
|
|
|
|
/* Should be impossible. */
|
|
|
|
INSIST(0);
|
|
|
|
}
|
2008-01-18 23:46:58 +00:00
|
|
|
|
2014-08-28 22:05:57 -07:00
|
|
|
result = dns_acl_match2(reqaddr, reqsigner, ecs, ecslen, scope,
|
|
|
|
inner, env, &indirectmatch, matchelt);
|
2007-12-20 01:48:29 +00:00
|
|
|
INSIST(result == ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Treat negative matches in indirect ACLs as "no match".
|
|
|
|
* That way, a negated indirect ACL will never become a
|
|
|
|
* surprise positive match through double negation.
|
|
|
|
* XXXDCL this should be documented.
|
|
|
|
*/
|
|
|
|
if (indirectmatch > 0) {
|
|
|
|
if (matchelt != NULL)
|
|
|
|
*matchelt = e;
|
|
|
|
return (ISC_TRUE);
|
|
|
|
}
|
2008-01-18 23:46:58 +00:00
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
/*
|
|
|
|
* A negative indirect match may have set *matchelt, but we don't
|
|
|
|
* want it set when we return.
|
|
|
|
*/
|
|
|
|
if (matchelt != NULL)
|
|
|
|
*matchelt = NULL;
|
2008-01-18 23:46:58 +00:00
|
|
|
|
2000-11-10 03:16:26 +00:00
|
|
|
return (ISC_FALSE);
|
2008-01-18 23:46:58 +00:00
|
|
|
}
|
2000-11-10 03:16:26 +00:00
|
|
|
|
1999-12-16 23:11:07 +00:00
|
|
|
void
|
2000-05-08 14:38:29 +00:00
|
|
|
dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
|
1999-12-16 23:11:07 +00:00
|
|
|
REQUIRE(DNS_ACL_VALID(source));
|
2011-06-17 07:05:02 +00:00
|
|
|
|
2001-02-08 23:30:32 +00:00
|
|
|
isc_refcount_increment(&source->refcount, NULL);
|
1999-12-16 23:11:07 +00:00
|
|
|
*target = source;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2000-05-08 14:38:29 +00:00
|
|
|
destroy(dns_acl_t *dacl) {
|
1999-12-16 23:11:07 +00:00
|
|
|
unsigned int i;
|
2011-06-17 07:05:02 +00:00
|
|
|
|
|
|
|
INSIST(!ISC_LINK_LINKED(dacl, nextincache));
|
|
|
|
|
1999-12-16 23:11:07 +00:00
|
|
|
for (i = 0; i < dacl->length; i++) {
|
|
|
|
dns_aclelement_t *de = &dacl->elements[i];
|
2007-09-12 01:09:08 +00:00
|
|
|
if (de->type == dns_aclelementtype_keyname) {
|
|
|
|
dns_name_free(&de->keyname, dacl->mctx);
|
2007-09-14 01:46:06 +00:00
|
|
|
} else if (de->type == dns_aclelementtype_nestedacl) {
|
|
|
|
dns_acl_detach(&de->nestedacl);
|
1999-12-16 23:11:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (dacl->elements != NULL)
|
|
|
|
isc_mem_put(dacl->mctx, dacl->elements,
|
|
|
|
dacl->alloc * sizeof(dns_aclelement_t));
|
2000-08-11 01:53:47 +00:00
|
|
|
if (dacl->name != NULL)
|
|
|
|
isc_mem_free(dacl->mctx, dacl->name);
|
2007-12-20 01:48:29 +00:00
|
|
|
if (dacl->iptable != NULL)
|
|
|
|
dns_iptable_detach(&dacl->iptable);
|
2001-02-08 23:30:32 +00:00
|
|
|
isc_refcount_destroy(&dacl->refcount);
|
1999-12-16 23:11:07 +00:00
|
|
|
dacl->magic = 0;
|
2013-02-20 21:39:05 -08:00
|
|
|
isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl));
|
1999-12-16 23:11:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2000-05-08 14:38:29 +00:00
|
|
|
dns_acl_detach(dns_acl_t **aclp) {
|
1999-12-16 23:11:07 +00:00
|
|
|
dns_acl_t *acl = *aclp;
|
2001-02-08 23:30:32 +00:00
|
|
|
unsigned int refs;
|
2011-06-17 07:05:02 +00:00
|
|
|
|
1999-12-16 23:11:07 +00:00
|
|
|
REQUIRE(DNS_ACL_VALID(acl));
|
2011-06-17 07:05:02 +00:00
|
|
|
|
2001-02-08 23:30:32 +00:00
|
|
|
isc_refcount_decrement(&acl->refcount, &refs);
|
|
|
|
if (refs == 0)
|
1999-12-16 23:11:07 +00:00
|
|
|
destroy(acl);
|
|
|
|
*aclp = NULL;
|
|
|
|
}
|
|
|
|
|
2007-09-19 03:03:29 +00:00
|
|
|
|
|
|
|
static isc_once_t insecure_prefix_once = ISC_ONCE_INIT;
|
|
|
|
static isc_mutex_t insecure_prefix_lock;
|
|
|
|
static isc_boolean_t insecure_prefix_found;
|
|
|
|
|
|
|
|
static void
|
|
|
|
initialize_action(void) {
|
2007-12-20 01:48:29 +00:00
|
|
|
RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
|
2007-09-19 03:03:29 +00:00
|
|
|
}
|
2000-02-09 22:59:40 +00:00
|
|
|
|
2007-09-12 01:09:08 +00:00
|
|
|
/*
|
2016-05-26 21:23:19 +02:00
|
|
|
* Called via isc_radix_process() to find IP table nodes that are
|
2007-09-12 01:09:08 +00:00
|
|
|
* insecure.
|
|
|
|
*/
|
|
|
|
static void
|
2008-01-21 20:38:54 +00:00
|
|
|
is_insecure(isc_prefix_t *prefix, void **data) {
|
2014-08-28 22:05:57 -07:00
|
|
|
int bitlen, family, off;
|
2008-01-21 20:38:54 +00:00
|
|
|
|
|
|
|
bitlen = prefix->bitlen;
|
2008-09-26 21:12:02 +00:00
|
|
|
family = prefix->family;
|
2008-01-21 23:46:56 +00:00
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
/* Negated entries are always secure. */
|
2014-08-28 22:05:57 -07:00
|
|
|
off = ISC_RADIX_OFF(prefix);
|
|
|
|
if (data[off] != NULL && * (isc_boolean_t *) data[off])
|
2008-01-21 23:46:56 +00:00
|
|
|
return;
|
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
/* If loopback prefix found, return */
|
2008-01-21 20:38:54 +00:00
|
|
|
switch (family) {
|
2007-12-20 01:48:29 +00:00
|
|
|
case AF_INET:
|
2008-01-21 20:38:54 +00:00
|
|
|
if (bitlen == 32 &&
|
2007-09-12 01:09:08 +00:00
|
|
|
htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
|
2007-12-20 01:48:29 +00:00
|
|
|
return;
|
2000-12-01 18:22:17 +00:00
|
|
|
break;
|
2007-12-20 01:48:29 +00:00
|
|
|
case AF_INET6:
|
2008-01-21 20:38:54 +00:00
|
|
|
if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
|
2007-12-20 01:48:29 +00:00
|
|
|
return;
|
2000-12-01 18:22:17 +00:00
|
|
|
break;
|
2007-12-20 01:48:29 +00:00
|
|
|
default:
|
2000-12-01 18:22:17 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-09-12 01:09:08 +00:00
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
/* Non-negated, non-loopback */
|
2008-01-21 20:38:54 +00:00
|
|
|
insecure_prefix_found = ISC_TRUE; /* LOCKED */
|
2007-12-20 01:48:29 +00:00
|
|
|
return;
|
2000-12-01 18:22:17 +00:00
|
|
|
}
|
|
|
|
|
2007-09-12 01:09:08 +00:00
|
|
|
/*
|
|
|
|
* Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
|
|
|
|
* if it contains IP addresses other than those of the local host.
|
2008-01-18 23:46:58 +00:00
|
|
|
* This is intended for applications such as printing warning
|
2007-09-12 01:09:08 +00:00
|
|
|
* messages for suspect ACLs; it is not intended for making access
|
|
|
|
* control decisions. We make no guarantee that an ACL for which
|
|
|
|
* this function returns ISC_FALSE is safe.
|
|
|
|
*/
|
2000-12-01 18:22:17 +00:00
|
|
|
isc_boolean_t
|
2006-02-28 02:39:52 +00:00
|
|
|
dns_acl_isinsecure(const dns_acl_t *a) {
|
2000-12-01 18:22:17 +00:00
|
|
|
unsigned int i;
|
2007-09-19 03:03:29 +00:00
|
|
|
isc_boolean_t insecure;
|
|
|
|
|
|
|
|
RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
|
|
|
|
initialize_action) == ISC_R_SUCCESS);
|
2007-09-12 01:09:08 +00:00
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
/*
|
|
|
|
* Walk radix tree to find out if there are any non-negated,
|
|
|
|
* non-loopback prefixes.
|
|
|
|
*/
|
2007-09-19 03:03:29 +00:00
|
|
|
LOCK(&insecure_prefix_lock);
|
2007-12-20 01:48:29 +00:00
|
|
|
insecure_prefix_found = ISC_FALSE;
|
|
|
|
isc_radix_process(a->iptable->radix, is_insecure);
|
2007-09-19 03:03:29 +00:00
|
|
|
insecure = insecure_prefix_found;
|
|
|
|
UNLOCK(&insecure_prefix_lock);
|
2007-12-20 01:48:29 +00:00
|
|
|
if (insecure)
|
2013-02-27 17:19:39 -08:00
|
|
|
return (ISC_TRUE);
|
2008-01-18 23:46:58 +00:00
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
/* Now check non-radix elements */
|
2000-12-01 18:22:17 +00:00
|
|
|
for (i = 0; i < a->length; i++) {
|
|
|
|
dns_aclelement_t *e = &a->elements[i];
|
|
|
|
|
|
|
|
/* A negated match can never be insecure. */
|
|
|
|
if (e->negative)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (e->type) {
|
2007-12-20 01:48:29 +00:00
|
|
|
case dns_aclelementtype_keyname:
|
|
|
|
case dns_aclelementtype_localhost:
|
2000-12-01 18:22:17 +00:00
|
|
|
continue;
|
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
case dns_aclelementtype_nestedacl:
|
|
|
|
if (dns_acl_isinsecure(e->nestedacl))
|
|
|
|
return (ISC_TRUE);
|
|
|
|
continue;
|
2007-09-12 01:09:08 +00:00
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
case dns_aclelementtype_localnets:
|
2000-12-01 18:22:17 +00:00
|
|
|
return (ISC_TRUE);
|
|
|
|
|
2007-12-20 01:48:29 +00:00
|
|
|
default:
|
2000-12-01 18:22:17 +00:00
|
|
|
INSIST(0);
|
|
|
|
return (ISC_TRUE);
|
|
|
|
}
|
|
|
|
}
|
2007-09-12 01:09:08 +00:00
|
|
|
|
2000-12-01 18:22:17 +00:00
|
|
|
/* No insecure elements were found. */
|
|
|
|
return (ISC_FALSE);
|
|
|
|
}
|
|
|
|
|
2007-09-12 01:09:08 +00:00
|
|
|
/*
|
|
|
|
* Initialize ACL environment, setting up localhost and localnets ACLs
|
|
|
|
*/
|
2000-02-09 22:59:40 +00:00
|
|
|
isc_result_t
|
2000-05-08 14:38:29 +00:00
|
|
|
dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
|
2000-02-09 22:59:40 +00:00
|
|
|
isc_result_t result;
|
2011-06-17 07:05:02 +00:00
|
|
|
|
2000-02-09 22:59:40 +00:00
|
|
|
env->localhost = NULL;
|
|
|
|
env->localnets = NULL;
|
|
|
|
result = dns_acl_create(mctx, 0, &env->localhost);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto cleanup_nothing;
|
|
|
|
result = dns_acl_create(mctx, 0, &env->localnets);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto cleanup_localhost;
|
2001-03-26 21:33:07 +00:00
|
|
|
env->match_mapped = ISC_FALSE;
|
2013-02-27 17:19:39 -08:00
|
|
|
#ifdef HAVE_GEOIP
|
|
|
|
env->geoip = NULL;
|
2014-08-28 22:05:57 -07:00
|
|
|
env->geoip_use_ecs = ISC_FALSE;
|
2013-02-27 17:19:39 -08:00
|
|
|
#endif
|
2000-02-09 22:59:40 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
cleanup_localhost:
|
|
|
|
dns_acl_detach(&env->localhost);
|
|
|
|
cleanup_nothing:
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
2000-05-08 14:38:29 +00:00
|
|
|
void
|
|
|
|
dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
|
2000-02-09 22:59:40 +00:00
|
|
|
dns_acl_detach(&t->localhost);
|
|
|
|
dns_acl_attach(s->localhost, &t->localhost);
|
|
|
|
dns_acl_detach(&t->localnets);
|
|
|
|
dns_acl_attach(s->localnets, &t->localnets);
|
2001-03-26 21:33:07 +00:00
|
|
|
t->match_mapped = s->match_mapped;
|
2014-08-28 22:05:57 -07:00
|
|
|
#ifdef HAVE_GEOIP
|
|
|
|
t->geoip_use_ecs = s->geoip_use_ecs;
|
|
|
|
#endif
|
2000-02-09 22:59:40 +00:00
|
|
|
}
|
|
|
|
|
2000-05-08 14:38:29 +00:00
|
|
|
void
|
|
|
|
dns_aclenv_destroy(dns_aclenv_t *env) {
|
2000-02-09 22:59:40 +00:00
|
|
|
dns_acl_detach(&env->localhost);
|
|
|
|
dns_acl_detach(&env->localnets);
|
|
|
|
}
|