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; isc_netaddr_t *addr; isc_netaddr_t v4addr; int indirectmatch; isc_result_t result; switch (e->type) { case dns_aclelementtype_ipprefix: if (env == NULL || env->match_mapped == ISC_FALSE || reqaddr->family != AF_INET6 || !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6)) addr = reqaddr; else { isc_netaddr_fromv4mapped(&v4addr, reqaddr); addr = &v4addr; } if (isc_netaddr_eqprefix(addr, &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); }
/* * Determine whether a given address or signer matches a given ACL. * For a match with a positive ACL element or iptable radix entry, * 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. */ isc_result_t dns_acl_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner, const dns_acl_t *acl, const dns_aclenv_t *env, int *match, const dns_aclelement_t **matchelt) { isc_uint16_t bitlen, family; isc_prefix_t pfx; isc_radix_node_t *node = NULL; const isc_netaddr_t *addr; isc_netaddr_t v4addr; isc_result_t result; int match_num = -1; unsigned int i; REQUIRE(reqaddr != NULL); REQUIRE(matchelt == NULL || *matchelt == NULL); if (env == NULL || env->match_mapped == ISC_FALSE || reqaddr->family != AF_INET6 || !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6)) addr = reqaddr; else { isc_netaddr_fromv4mapped(&v4addr, reqaddr); addr = &v4addr; } /* Always match with host addresses. */ family = addr->family; bitlen = family == AF_INET6 ? 128 : 32; NETADDR_TO_PREFIX_T(addr, pfx, bitlen); /* 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) { match_num = node->node_num[ISC_IS6(family)]; if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE) *match = match_num; else *match = -match_num; } /* Now search non-radix elements for a match with a lower node_num. */ for (i = 0; i < acl->length; i++) { dns_aclelement_t *e = &acl->elements[i]; /* Already found a better match? */ if (match_num != -1 && match_num < e->node_num) { isc_refcount_destroy(&pfx.refcount); return (ISC_R_SUCCESS); } if (dns_aclelement_match(reqaddr, reqsigner, e, env, matchelt)) { if (match_num == -1 || e->node_num < match_num) { if (e->negative == ISC_TRUE) *match = -e->node_num; else *match = e->node_num; } isc_refcount_destroy(&pfx.refcount); return (ISC_R_SUCCESS); } } isc_refcount_destroy(&pfx.refcount); return (ISC_R_SUCCESS); }
isc_result_t dns_acl_match2(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner, const isc_netaddr_t *ecs, 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; isc_prefix_t pfx; isc_radix_node_t *node = NULL; const isc_netaddr_t *addr = reqaddr; isc_netaddr_t v4addr; isc_result_t result; int match_num = -1; unsigned int i; REQUIRE(reqaddr != NULL); REQUIRE(matchelt == NULL || *matchelt == NULL); REQUIRE(ecs != NULL || scope == NULL); if (env != NULL && env->match_mapped && addr->family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&addr->type.in6)) { isc_netaddr_fromv4mapped(&v4addr, addr); addr = &v4addr; } /* Always match with host addresses. */ bitlen = (addr->family == AF_INET6) ? 128 : 32; NETADDR_TO_PREFIX_T(addr, pfx, bitlen, ISC_FALSE); /* 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) { int off = ISC_RADIX_OFF(&pfx); match_num = node->node_num[off]; if (*(isc_boolean_t *) node->data[off]) *match = match_num; else *match = -match_num; } 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); } /* Now search non-radix elements for a match with a lower node_num. */ for (i = 0; i < acl->length; i++) { dns_aclelement_t *e = &acl->elements[i]; /* Already found a better match? */ if (match_num != -1 && match_num < e->node_num) { break; } if (dns_aclelement_match2(reqaddr, reqsigner, ecs, ecslen, scope, e, env, matchelt)) { if (match_num == -1 || e->node_num < match_num) { if (e->negative) *match = -e->node_num; else *match = e->node_num; } break; } } return (ISC_R_SUCCESS); }