mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-05 09:05:40 +00:00
option allows updates based on the source IP address, to alert users to the fact that this is insecure and becoming increasingly so as servers capable of update forwarding are being deployed.
413 lines
9.4 KiB
C
413 lines
9.4 KiB
C
/*
|
|
* Copyright (C) 1999, 2000 Internet Software Consortium.
|
|
*
|
|
* Permission to use, copy, modify, and 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 INTERNET SOFTWARE CONSORTIUM
|
|
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
|
* INTERNET SOFTWARE CONSORTIUM 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: acl.c,v 1.17 2000/12/01 18:22:15 gson Exp $ */
|
|
|
|
#include <config.h>
|
|
|
|
#include <isc/mem.h>
|
|
#include <isc/string.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/acl.h>
|
|
|
|
isc_result_t
|
|
dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
|
|
isc_result_t result;
|
|
dns_acl_t *acl;
|
|
|
|
/*
|
|
* Work around silly limitation of isc_mem_get().
|
|
*/
|
|
if (n == 0)
|
|
n = 1;
|
|
|
|
acl = isc_mem_get(mctx, sizeof(*acl));
|
|
if (acl == NULL)
|
|
return (ISC_R_NOMEMORY);
|
|
acl->mctx = mctx;
|
|
acl->name = NULL;
|
|
acl->refcount = 1;
|
|
acl->elements = NULL;
|
|
acl->alloc = 0;
|
|
acl->length = 0;
|
|
|
|
ISC_LINK_INIT(acl, nextincache);
|
|
/*
|
|
* Must set magic early because we use dns_acl_detach() to clean up.
|
|
*/
|
|
acl->magic = DNS_ACL_MAGIC;
|
|
|
|
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);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_acl_appendelement(dns_acl_t *acl, dns_aclelement_t *elt) {
|
|
if (acl->length + 1 > acl->alloc) {
|
|
/*
|
|
* Resize the ACL.
|
|
*/
|
|
unsigned int newalloc;
|
|
void *newmem;
|
|
|
|
newalloc = acl->alloc * 2;
|
|
if (newalloc < 4)
|
|
newalloc = 4;
|
|
newmem = isc_mem_get(acl->mctx,
|
|
newalloc * sizeof(dns_aclelement_t));
|
|
if (newmem == NULL)
|
|
return (ISC_R_NOMEMORY);
|
|
memcpy(newmem, acl->elements,
|
|
acl->length * sizeof(dns_aclelement_t));
|
|
isc_mem_put(acl->mctx, acl->elements,
|
|
acl->alloc * sizeof(dns_aclelement_t));
|
|
acl->elements = newmem;
|
|
acl->alloc = newalloc;
|
|
}
|
|
/*
|
|
* Append the new element.
|
|
*/
|
|
acl->elements[acl->length++] = *elt;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static isc_result_t
|
|
dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
|
|
isc_result_t result;
|
|
dns_acl_t *acl = NULL;
|
|
result = dns_acl_create(mctx, 1, &acl);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
acl->elements[0].negative = neg;
|
|
acl->elements[0].type = dns_aclelementtype_any;
|
|
acl->length = 1;
|
|
*target = acl;
|
|
return (result);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
|
|
return (dns_acl_anyornone(mctx, ISC_FALSE, target));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
|
|
return (dns_acl_anyornone(mctx, ISC_TRUE, target));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_acl_match(isc_netaddr_t *reqaddr,
|
|
dns_name_t *reqsigner,
|
|
dns_acl_t *acl,
|
|
dns_aclenv_t *env,
|
|
int *match,
|
|
dns_aclelement_t **matchelt)
|
|
{
|
|
unsigned int i;
|
|
|
|
REQUIRE(reqaddr != NULL);
|
|
REQUIRE(matchelt == NULL || *matchelt == NULL);
|
|
|
|
for (i = 0; i < acl->length; i++) {
|
|
dns_aclelement_t *e = &acl->elements[i];
|
|
|
|
if (dns_aclelement_match(reqaddr, reqsigner,
|
|
e, env, matchelt)) {
|
|
*match = e->negative ? -(i+1) : (i+1);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
}
|
|
/* No match. */
|
|
*match = 0;
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_aclelement_match(isc_netaddr_t *reqaddr,
|
|
dns_name_t *reqsigner,
|
|
dns_aclelement_t *e,
|
|
dns_aclenv_t *env,
|
|
dns_aclelement_t **matchelt)
|
|
{
|
|
dns_acl_t *inner = NULL;
|
|
int indirectmatch;
|
|
isc_result_t result;
|
|
|
|
switch (e->type) {
|
|
case dns_aclelementtype_ipprefix:
|
|
if (isc_netaddr_eqprefix(reqaddr,
|
|
&e->u.ip_prefix.address,
|
|
e->u.ip_prefix.prefixlen))
|
|
goto matched;
|
|
break;
|
|
|
|
case dns_aclelementtype_keyname:
|
|
if (reqsigner != NULL &&
|
|
dns_name_equal(reqsigner, &e->u.keyname))
|
|
goto matched;
|
|
break;
|
|
|
|
case dns_aclelementtype_nestedacl:
|
|
inner = e->u.nestedacl;
|
|
nested:
|
|
result = dns_acl_match(reqaddr, reqsigner,
|
|
inner,
|
|
env,
|
|
&indirectmatch, matchelt);
|
|
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)
|
|
goto matchelt_set;
|
|
|
|
/*
|
|
* A negative indirect match may have set *matchelt,
|
|
* but we don't want it set when we return.
|
|
*/
|
|
if (matchelt != NULL)
|
|
*matchelt = NULL;
|
|
break;
|
|
|
|
case dns_aclelementtype_any:
|
|
matched:
|
|
if (matchelt != NULL)
|
|
*matchelt = e;
|
|
matchelt_set:
|
|
return (ISC_TRUE);
|
|
|
|
case dns_aclelementtype_localhost:
|
|
if (env != NULL && env->localhost != NULL) {
|
|
inner = env->localhost;
|
|
goto nested;
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
case dns_aclelementtype_localnets:
|
|
if (env != NULL && env->localnets != NULL) {
|
|
inner = env->localnets;
|
|
goto nested;
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
default:
|
|
INSIST(0);
|
|
break;
|
|
}
|
|
|
|
return (ISC_FALSE);
|
|
}
|
|
|
|
void
|
|
dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
|
|
REQUIRE(DNS_ACL_VALID(source));
|
|
INSIST(source->refcount > 0);
|
|
source->refcount++;
|
|
*target = source;
|
|
}
|
|
|
|
static void
|
|
destroy(dns_acl_t *dacl) {
|
|
unsigned int i;
|
|
for (i = 0; i < dacl->length; i++) {
|
|
dns_aclelement_t *de = &dacl->elements[i];
|
|
switch (de->type) {
|
|
case dns_aclelementtype_keyname:
|
|
dns_name_free(&de->u.keyname, dacl->mctx);
|
|
break;
|
|
case dns_aclelementtype_nestedacl:
|
|
dns_acl_detach(&de->u.nestedacl);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (dacl->elements != NULL)
|
|
isc_mem_put(dacl->mctx, dacl->elements,
|
|
dacl->alloc * sizeof(dns_aclelement_t));
|
|
if (dacl->name != NULL)
|
|
isc_mem_free(dacl->mctx, dacl->name);
|
|
dacl->magic = 0;
|
|
isc_mem_put(dacl->mctx, dacl, sizeof(*dacl));
|
|
}
|
|
|
|
void
|
|
dns_acl_detach(dns_acl_t **aclp) {
|
|
dns_acl_t *acl = *aclp;
|
|
REQUIRE(DNS_ACL_VALID(acl));
|
|
INSIST(acl->refcount > 0);
|
|
acl->refcount--;
|
|
if (acl->refcount == 0)
|
|
destroy(acl);
|
|
*aclp = NULL;
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_aclelement_equal(dns_aclelement_t *ea, dns_aclelement_t *eb) {
|
|
if (ea->type != eb->type)
|
|
return (ISC_FALSE);
|
|
switch (ea->type) {
|
|
case dns_aclelementtype_ipprefix:
|
|
if (ea->u.ip_prefix.prefixlen !=
|
|
eb->u.ip_prefix.prefixlen)
|
|
return (ISC_FALSE);
|
|
return (isc_netaddr_equal(&ea->u.ip_prefix.address,
|
|
&eb->u.ip_prefix.address));
|
|
case dns_aclelementtype_keyname:
|
|
return (dns_name_equal(&ea->u.keyname, &eb->u.keyname));
|
|
case dns_aclelementtype_nestedacl:
|
|
return (dns_acl_equal(ea->u.nestedacl, eb->u.nestedacl));
|
|
case dns_aclelementtype_localhost:
|
|
case dns_aclelementtype_localnets:
|
|
case dns_aclelementtype_any:
|
|
return (ISC_TRUE);
|
|
default:
|
|
INSIST(0);
|
|
return (ISC_FALSE);
|
|
}
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_acl_equal(dns_acl_t *a, dns_acl_t *b) {
|
|
unsigned int i;
|
|
if (a == b)
|
|
return (ISC_TRUE);
|
|
if (a->length != b->length)
|
|
return (ISC_FALSE);
|
|
for (i = 0; i < a->length; i++) {
|
|
if (! dns_aclelement_equal(&a->elements[i],
|
|
&b->elements[i]))
|
|
return (ISC_FALSE);
|
|
}
|
|
return (ISC_TRUE);
|
|
}
|
|
|
|
#ifndef INADDR_LOOPBACK
|
|
#define INADDR_LOOPBACK (unsigned long)0x7F000001UL
|
|
#endif
|
|
|
|
static isc_boolean_t
|
|
is_loopback(dns_aclipprefix_t *p) {
|
|
switch (p->address.family) {
|
|
case AF_INET:
|
|
if (p->prefixlen == 32 &&
|
|
htonl(p->address.type.in.s_addr) == INADDR_LOOPBACK)
|
|
return (ISC_TRUE);
|
|
break;
|
|
case AF_INET6:
|
|
if (p->prefixlen == 128 &&
|
|
IN6_IS_ADDR_LOOPBACK(&p->address.type.in6))
|
|
return (ISC_TRUE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return (ISC_FALSE);
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_acl_isinsecure(dns_acl_t *a) {
|
|
unsigned int i;
|
|
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) {
|
|
case dns_aclelementtype_ipprefix:
|
|
/* The loopback address is considered secure. */
|
|
if (! is_loopback(&e->u.ip_prefix))
|
|
return (ISC_TRUE);
|
|
continue;
|
|
|
|
case dns_aclelementtype_keyname:
|
|
case dns_aclelementtype_localhost:
|
|
continue;
|
|
|
|
case dns_aclelementtype_nestedacl:
|
|
if (dns_acl_isinsecure(e->u.nestedacl))
|
|
return (ISC_TRUE);
|
|
continue;
|
|
|
|
case dns_aclelementtype_localnets:
|
|
case dns_aclelementtype_any:
|
|
return (ISC_TRUE);
|
|
|
|
default:
|
|
INSIST(0);
|
|
return (ISC_TRUE);
|
|
}
|
|
}
|
|
/* No insecure elements were found. */
|
|
return (ISC_FALSE);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
|
|
isc_result_t result;
|
|
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;
|
|
return (ISC_R_SUCCESS);
|
|
|
|
cleanup_localhost:
|
|
dns_acl_detach(&env->localhost);
|
|
cleanup_nothing:
|
|
return (result);
|
|
}
|
|
|
|
void
|
|
dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
|
|
dns_acl_detach(&t->localhost);
|
|
dns_acl_attach(s->localhost, &t->localhost);
|
|
dns_acl_detach(&t->localnets);
|
|
dns_acl_attach(s->localnets, &t->localnets);
|
|
}
|
|
|
|
void
|
|
dns_aclenv_destroy(dns_aclenv_t *env) {
|
|
dns_acl_detach(&env->localhost);
|
|
dns_acl_detach(&env->localnets);
|
|
}
|