acl.c revision e80f661db8ec9596eb977d6fc537484aa3662e22
2f99b54e8ec8e908ea894bc808d1c18a5f51a850Automatic Updater * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * Copyright (C) 1999-2002 Internet Software Consortium.
ec5347e2c775f027573ce5648b910361aa926c01Automatic Updater * Permission to use, copy, modify, and/or distribute this software for any
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafsson * purpose with or without fee is hereby granted, provided that the above
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafsson * copyright notice and this permission notice appear in all copies.
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * PERFORMANCE OF THIS SOFTWARE.
e80f661db8ec9596eb977d6fc537484aa3662e22Evan Hunt/* $Id: acl.c,v 1.47 2008/09/12 04:54:39 each Exp $ */
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * Create a new ACL, including an IP table and an array with room
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * for 'n' ACL elements. The elements are uninitialized and the
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * length is 0.
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrencedns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence * Work around silly limitation of isc_mem_get().
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews result = dns_iptable_create(mctx, &acl->iptable);
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence * Must set magic early because we use dns_acl_detach() to clean up.
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t));
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * Create a new ACL and initialize it with the value "any" or "none",
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * depending on the value of the "neg" parameter.
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * "any" is a positive iptable entry with bit length 0.
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * "none" is the same as "!any".
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrencedns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
1aba9fe67899522364a9dbc3ee5a14da081f0314Evan Hunt result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * Create a new ACL that matches everything.
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafssondns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson return (dns_acl_anyornone(mctx, ISC_FALSE, target));
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * Create a new ACL that matches nothing.
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafssondns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson return (dns_acl_anyornone(mctx, ISC_TRUE, target));
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * If pos is ISC_TRUE, test whether acl is set to "{ any; }"
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * If pos is ISC_FALSE, test whether acl is set to "{ none; }"
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntdns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Should never happen but let's be safe */
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt if (acl->iptable->radix->head->prefix->bitlen == 0 &&
28ad0be64ee756013c0f6a474fc447ee613ee0d1Evan Hunt *(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * Test whether acl is set to "{ any; }"
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * Test whether acl is set to "{ none; }"
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * Determine whether a given address or signer matches a given ACL.
2f012d936b5ccdf6520c96a4de23721dc58a2221Automatic Updater * For a match with a positive ACL element or iptable radix entry,
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * return with a positive value in match; for a match with a negated ACL
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * element or radix entry, return with a negative value in match.
8bb77cd31b7518fb5d2a6a9d75e16e4abd59df61Andreas Gustafsson unsigned int i;
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafsson REQUIRE(matchelt == NULL || *matchelt == NULL);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt if (env == NULL || env->match_mapped == ISC_FALSE ||
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt /* Always match with host addresses. */
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt /* Assume no match. */
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt /* Search radix. */
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt result = isc_radix_search(acl->iptable->radix, &node, &pfx);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt /* Found a match. */
262c39b2366bf79062f7f86b218947523dd1cbacEvan Hunt if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE)
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Now search non-radix elements for a match with a lower node_num. */
114c14f8adfc249cf2e5cdcb9007af46fed257e3Mark Andrews /* Already found a better match? */
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews if (match_num == -1 || e->node_num < match_num) {
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * Merge the contents of one ACL into another. Call dns_iptable_merge()
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * for the IP tables, then concatenate the element arrays.
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * If pos is set to false, then the nested ACL is to be negated. This
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * means reverse the sense of each *positive* element or IP table node,
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * but leave negatives alone, so as to prevent a double-negative causing
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * an unexpected postive match in the parent ACL.
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntdns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Resize the element array if needed. */
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews if (dest->length + source->length > dest->alloc) {
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Copy in the original elements */
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Release the memory for the old elements array */
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * Now copy in the new elements, increasing their node_num
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * values so as to keep the new ACL consistent. If we're
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * negating, then negate positive elements, but keep negative
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * elements the same for security reasons.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Copy type. */
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews dest->elements[nelem + i].type = source->elements[i].type;
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Adjust node numbering. */
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews source->elements[i].node_num + dest->node_count;
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Duplicate nested acl. */
262c39b2366bf79062f7f86b218947523dd1cbacEvan Hunt if (source->elements[i].type == dns_aclelementtype_nestedacl &&
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Duplicate key name. */
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews if (source->elements[i].type == dns_aclelementtype_keyname) {
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews dns_name_init(&dest->elements[nelem+i].keyname, NULL);
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews result = dns_name_dup(&source->elements[i].keyname,
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* reverse sense of positives if this is a negative acl */
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews if (!pos && source->elements[i].negative == ISC_FALSE) {
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * Merge the iptables. Make sure the destination ACL's
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * node_count value is set correctly afterward.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews result = dns_iptable_merge(dest->iptable, source->iptable, pos);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * Like dns_acl_match, but matches against the single ACL element 'e'
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * rather than a complete ACL, and returns ISC_TRUE iff it matched.
2f012d936b5ccdf6520c96a4de23721dc58a2221Automatic Updater * To determine whether the match was prositive or negative, the
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * caller should examine e->negative. Since the element 'e' may be
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * a reference to a named ACL or a nested ACL, a matching element
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * returned through 'matchelt' is not necessarily 'e' itself.
45e1bd63587102c3bb361eaca42ee7b714fb3542Mark Andrewsdns_aclelement_match(const isc_netaddr_t *reqaddr,
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews switch (e->type) {
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Should be impossible. */
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews result = dns_acl_match(reqaddr, reqsigner, inner, env,
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * Treat negative matches in indirect ACLs as "no match".
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * That way, a negated indirect ACL will never become a
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * surprise positive match through double negation.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * XXXDCL this should be documented.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * A negative indirect match may have set *matchelt, but we don't
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * want it set when we return.
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrencedns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
aa23a35d81a9618a40c4a9b44be48009553e4777Andreas Gustafsson isc_refcount_increment(&source->refcount, NULL);
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafsson unsigned int i;
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafsson dns_aclelement_t *de = &dacl->elements[i];
12e0477d4e132c9122312246ed60aaa646f819b2Mark Andrews } else if (de->type == dns_aclelementtype_nestedacl) {
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafsson isc_mem_put(dacl->mctx, dacl, sizeof(*dacl));
aa23a35d81a9618a40c4a9b44be48009553e4777Andreas Gustafsson isc_refcount_decrement(&acl->refcount, &refs);
69f3cb5abcb38f105c653c7b3df7cec33b87b292Mark Andrewsstatic isc_once_t insecure_prefix_once = ISC_ONCE_INIT;
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * Called via isc_radix_walk() to find IP table nodes that are
262c39b2366bf79062f7f86b218947523dd1cbacEvan Hunt /* Bitlen 0 means "any" or "none", which is always treated as IPv4 */
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Negated entries are always secure. */
262c39b2366bf79062f7f86b218947523dd1cbacEvan Hunt secure = * (isc_boolean_t *)data[ISC_IS6(family)];
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* If loopback prefix found, return */
262c39b2366bf79062f7f86b218947523dd1cbacEvan Hunt if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Non-negated, non-loopback */
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * if it contains IP addresses other than those of the local host.
2f012d936b5ccdf6520c96a4de23721dc58a2221Automatic Updater * This is intended for applications such as printing warning
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * messages for suspect ACLs; it is not intended for making access
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * control decisions. We make no guarantee that an ACL for which
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * this function returns ISC_FALSE is safe.
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson unsigned int i;
69f3cb5abcb38f105c653c7b3df7cec33b87b292Mark Andrews RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * Walk radix tree to find out if there are any non-negated,
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * non-loopback prefixes.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews isc_radix_process(a->iptable->radix, is_insecure);
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews /* Now check non-radix elements */
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson for (i = 0; i < a->length; i++) {
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson /* A negated match can never be insecure. */
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson /* No insecure elements were found. */
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * Initialize ACL environment, setting up localhost and localnets ACLs
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrencedns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson result = dns_acl_create(mctx, 0, &env->localhost);
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson result = dns_acl_create(mctx, 0, &env->localnets);
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrencedns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson dns_acl_attach(s->localhost, &t->localhost);