Exemplo n.º 1
0
/*
 * Recursively pre-parse an ACL definition to find the total number
 * of non-IP-prefix elements (localhost, localnets, key) in all nested
 * ACLs, so that the parent will have enough space allocated for the
 * elements table after all the nested ACLs have been merged in to the
 * parent.
 */
static int
count_acl_elements(const cfg_obj_t *caml, const cfg_obj_t *cctx,
		   isc_boolean_t *has_negative)
{
	const cfg_listelt_t *elt;
	const cfg_obj_t *cacl = NULL;
	isc_result_t result;
	int n = 0;

	if (has_negative != NULL)
		*has_negative = ISC_FALSE;

	for (elt = cfg_list_first(caml);
	     elt != NULL;
	     elt = cfg_list_next(elt)) {
		const cfg_obj_t *ce = cfg_listelt_value(elt);

		/* negated element; just get the value. */
		if (cfg_obj_istuple(ce)) {
			ce = cfg_tuple_get(ce, "value");
			if (has_negative != NULL)
				*has_negative = ISC_TRUE;
		}

		if (cfg_obj_istype(ce, &cfg_type_keyref)) {
			n++;
		} else if (cfg_obj_islist(ce)) {
			isc_boolean_t negative;
			n += count_acl_elements(ce, cctx, &negative);
			if (negative)
				n++;
		} else if (cfg_obj_isstring(ce)) {
			const char *name = cfg_obj_asstring(ce);
			if (strcasecmp(name, "localhost") == 0 ||
#ifdef SUPPORT_GEOIP
			    strncasecmp(name, "country_", 8) == 0 ||
#endif
			    strcasecmp(name, "localnets") == 0) {
				n++;
			} else if (strcasecmp(name, "any") != 0 &&
				   strcasecmp(name, "none") != 0) {
				result = get_acl_def(cctx, name, &cacl);
				if (result == ISC_R_SUCCESS)
					n += count_acl_elements(cacl, cctx,
								NULL) + 1;
			}
		}
	}

	return n;
}
Exemplo n.º 2
0
static isc_result_t ATTR_NONNULLS ATTR_CHECKRESULT
get_match_type(const cfg_obj_t *obj, unsigned int *value)
{
	const char *str;

	if (!cfg_obj_istuple(obj)) {
		log_bug("tuple is expected");
		return ISC_R_UNEXPECTED;
	}
	obj = cfg_tuple_get(obj, "matchtype");
	if (!cfg_obj_isstring(obj)) {
		log_bug("matchtype is not defined");
		return ISC_R_UNEXPECTED;
	}
	str = cfg_obj_asstring(obj);

	MATCH("name", DNS_SSUMATCHTYPE_NAME);
	MATCH("subdomain", DNS_SSUMATCHTYPE_SUBDOMAIN);
	MATCH("zonesub", DNS_SSUMATCHTYPE_SUBDOMAIN);
	MATCH("wildcard", DNS_SSUMATCHTYPE_WILDCARD);
	MATCH("self", DNS_SSUMATCHTYPE_SELF);
#if defined(DNS_SSUMATCHTYPE_SELFSUB) && defined(DNS_SSUMATCHTYPE_SELFWILD)
	MATCH("selfsub", DNS_SSUMATCHTYPE_SELFSUB);
	MATCH("selfwild", DNS_SSUMATCHTYPE_SELFWILD);
#endif
#ifdef DNS_SSUMATCHTYPE_SELFMS
	MATCH("ms-self", DNS_SSUMATCHTYPE_SELFMS);
#endif
#ifdef DNS_SSUMATCHTYPE_SELFKRB5
	MATCH("krb5-self", DNS_SSUMATCHTYPE_SELFKRB5);
#endif
#ifdef DNS_SSUMATCHTYPE_SUBDOMAINMS
	MATCH("ms-subdomain", DNS_SSUMATCHTYPE_SUBDOMAINMS);
#endif
#ifdef DNS_SSUMATCHTYPE_SUBDOMAINKRB5
	MATCH("krb5-subdomain", DNS_SSUMATCHTYPE_SUBDOMAINKRB5);
#endif
#if defined(DNS_SSUMATCHTYPE_TCPSELF) && defined(DNS_SSUMATCHTYPE_6TO4SELF)
	MATCH("tcp-self", DNS_SSUMATCHTYPE_TCPSELF);
	MATCH("6to4-self", DNS_SSUMATCHTYPE_6TO4SELF);
#endif
#if defined(DNS_SSUMATCHTYPE_EXTERNAL)
	MATCH("external", DNS_SSUMATCHTYPE_EXTERNAL);
#endif

	log_bug("unsupported match type '%s'", str);
	return ISC_R_NOTIMPLEMENTED;
}
Exemplo n.º 3
0
static isc_result_t ATTR_NONNULLS ATTR_CHECKRESULT
get_fixed_name(const cfg_obj_t *obj, const char *name, dns_fixedname_t *fname)
{
	isc_result_t result;
	isc_buffer_t buf;
	const char *str;
	size_t len;

	REQUIRE(fname != NULL);

	if (!cfg_obj_istuple(obj)) {
		log_bug("configuration object is not a tuple");
		return ISC_R_UNEXPECTED;
	}
	obj = cfg_tuple_get(obj, name);

	if (!cfg_obj_isstring(obj))
		return ISC_R_NOTFOUND;
	str = cfg_obj_asstring(obj);

	len = strlen(str);
	isc_buffer_init(&buf, (char *)str, len);

	/*
	 * Workaround for https://bugzilla.redhat.com/show_bug.cgi?id=728925
	 *
	 * ipa-server-install script could create SSU rules with
	 * double-dot-ending FQDNs. Silently "adjust" such wrong FQDNs.
	 */
	if (str[len - 1] == '.' && str[len - 2] == '.')
		isc_buffer_add(&buf, len - 1);
	else
		isc_buffer_add(&buf, len);

	dns_fixedname_init(fname);

	result = dns_name_fromtext(dns_fixedname_name(fname), &buf,
				   dns_rootname, ISC_FALSE, NULL);
	if (result != ISC_R_SUCCESS)
		log_error("'%s' is not a valid name", str);

	return result;
}
Exemplo n.º 4
0
static isc_result_t ATTR_NONNULLS ATTR_CHECKRESULT
get_mode(const cfg_obj_t *obj, isc_boolean_t *value)
{
	const char *str;

	if (!cfg_obj_istuple(obj)) {
		log_bug("tuple is expected");
		return ISC_R_UNEXPECTED;
	}
	obj = cfg_tuple_get(obj, "mode");
	if (!cfg_obj_isstring(obj)) {
		log_bug("mode is not defined");
		return ISC_R_UNEXPECTED;
	}
	str = cfg_obj_asstring(obj);

	MATCH("grant", ISC_TRUE);
	MATCH("deny", ISC_FALSE);

	log_bug("unsupported ACL mode '%s'", str);
	return ISC_R_NOTIMPLEMENTED;
}
Exemplo n.º 5
0
isc_result_t
ns_acl_fromconfig(cfg_obj_t *caml,
		  cfg_obj_t *cctx,
		  ns_aclconfctx_t *ctx,
		  isc_mem_t *mctx,
		  dns_acl_t **target)
{
	isc_result_t result;
	unsigned int count;
	dns_acl_t *dacl = NULL;
	dns_aclelement_t *de;
	cfg_listelt_t *elt;

	REQUIRE(target != NULL && *target == NULL);

	count = 0;
	for (elt = cfg_list_first(caml);
	     elt != NULL;
	     elt = cfg_list_next(elt))
		count++;

	result = dns_acl_create(mctx, count, &dacl);
	if (result != ISC_R_SUCCESS)
		return (result);

	de = dacl->elements;
	for (elt = cfg_list_first(caml);
	     elt != NULL;
	     elt = cfg_list_next(elt))
	{
		cfg_obj_t *ce = cfg_listelt_value(elt);
		if (cfg_obj_istuple(ce)) {
			/* This must be a negated element. */
			ce = cfg_tuple_get(ce, "value");
			de->negative = ISC_TRUE;
		} else {
			de->negative = ISC_FALSE;
		}

		if (cfg_obj_isnetprefix(ce)) {
			/* Network prefix */
			de->type = dns_aclelementtype_ipprefix;

			cfg_obj_asnetprefix(ce,
					    &de->u.ip_prefix.address,
					    &de->u.ip_prefix.prefixlen);
		} else if (cfg_obj_istype(ce, &cfg_type_keyref)) {
			/* Key name */
			de->type = dns_aclelementtype_keyname;
			dns_name_init(&de->u.keyname, NULL);
			result = convert_keyname(ce, mctx, &de->u.keyname);
			if (result != ISC_R_SUCCESS)
				goto cleanup;
		} else if (cfg_obj_islist(ce)) {
			/* Nested ACL */
			de->type = dns_aclelementtype_nestedacl;
			result = ns_acl_fromconfig(ce, cctx, ctx, mctx,
						   &de->u.nestedacl);
			if (result != ISC_R_SUCCESS)
				goto cleanup;
		} else if (cfg_obj_isstring(ce)) {
			/* ACL name */
			char *name = cfg_obj_asstring(ce);
			if (strcasecmp(name, "localhost") == 0) {
				de->type = dns_aclelementtype_localhost;
			} else if (strcasecmp(name, "localnets") == 0) {
				de->type = dns_aclelementtype_localnets;
			}  else if (strcasecmp(name, "any") == 0) {
				de->type = dns_aclelementtype_any;
			}  else if (strcasecmp(name, "none") == 0) {
				de->type = dns_aclelementtype_any;
				de->negative = ISC_TF(! de->negative);
			} else {
				de->type = dns_aclelementtype_nestedacl;
				result = convert_named_acl(ce, cctx, ctx, mctx,
							   &de->u.nestedacl);
				if (result != ISC_R_SUCCESS)
					goto cleanup;
			}
		} else {
			cfg_obj_log(ce, dns_lctx, ISC_LOG_WARNING,
				    "address match list contains "
				    "unsupported element type");
			result = ISC_R_FAILURE;
			goto cleanup;
		}
		de++;
		dacl->length++;
	}

	*target = dacl;
	return (ISC_R_SUCCESS);

 cleanup:
	dns_acl_detach(&dacl);
	return (result);
}
Exemplo n.º 6
0
isc_result_t
cfg_acl_fromconfig2(const cfg_obj_t *caml, const cfg_obj_t *cctx,
		   isc_log_t *lctx, cfg_aclconfctx_t *ctx,
		   isc_mem_t *mctx, unsigned int nest_level,
		   isc_uint16_t family, dns_acl_t **target)
{
	isc_result_t result;
	dns_acl_t *dacl = NULL, *inneracl = NULL;
	dns_aclelement_t *de;
	const cfg_listelt_t *elt;
	dns_iptable_t *iptab;
	int new_nest_level = 0;

	if (nest_level != 0)
		new_nest_level = nest_level - 1;

	REQUIRE(target != NULL);
	REQUIRE(*target == NULL || DNS_ACL_VALID(*target));

	if (*target != NULL) {
		/*
		 * If target already points to an ACL, then we're being
		 * called recursively to configure a nested ACL.  The
		 * nested ACL's contents should just be absorbed into its
		 * parent ACL.
		 */
		dns_acl_attach(*target, &dacl);
		dns_acl_detach(target);
	} else {
		/*
		 * Need to allocate a new ACL structure.  Count the items
		 * in the ACL definition that will require space in the
		 * elements table.  (Note that if nest_level is nonzero,
		 * *everything* goes in the elements table.)
		 */
		isc_uint32_t nelem;

		if (nest_level == 0) {
			result = count_acl_elements(caml, cctx, lctx, ctx,
						    mctx, &nelem, NULL);
			if (result != ISC_R_SUCCESS)
				return (result);
		} else
			nelem = cfg_list_length(caml, ISC_FALSE);

		result = dns_acl_create(mctx, nelem, &dacl);
		if (result != ISC_R_SUCCESS)
			return (result);
	}

	de = dacl->elements;
	for (elt = cfg_list_first(caml);
	     elt != NULL;
	     elt = cfg_list_next(elt)) {
		const cfg_obj_t *ce = cfg_listelt_value(elt);
		isc_boolean_t neg = ISC_FALSE;

		INSIST(dacl->length <= dacl->alloc);

		if (cfg_obj_istuple(ce)) {
			/* Might be a negated element */
			const cfg_obj_t *negated =
				cfg_tuple_get(ce, "negated");
			if (! cfg_obj_isvoid(negated)) {
				neg = ISC_TRUE;
				dacl->has_negatives = ISC_TRUE;
				ce = negated;
			}
		}

		/*
		 * If nest_level is nonzero, then every element is
		 * to be stored as a separate, nested ACL rather than
		 * merged into the main iptable.
		 */
		iptab = dacl->iptable;

		if (nest_level != 0) {
			result = dns_acl_create(mctx,
						cfg_list_length(ce, ISC_FALSE),
						&de->nestedacl);
			if (result != ISC_R_SUCCESS)
				goto cleanup;
			iptab = de->nestedacl->iptable;
		}

		if (cfg_obj_isnetprefix(ce)) {
			/* Network prefix */
			isc_netaddr_t	addr;
			unsigned int	bitlen;

			cfg_obj_asnetprefix(ce, &addr, &bitlen);
			if (family != 0 && family != addr.family) {
				char buf[ISC_NETADDR_FORMATSIZE + 1];
				isc_netaddr_format(&addr, buf, sizeof(buf));
				cfg_obj_log(ce, lctx, ISC_LOG_WARNING,
					    "'%s': incorrect address family; "
					    "ignoring", buf);
				if (nest_level != 0)
					dns_acl_detach(&de->nestedacl);
				continue;
			}
			result = isc_netaddr_prefixok(&addr, bitlen);
			if (result != ISC_R_SUCCESS) {
				char buf[ISC_NETADDR_FORMATSIZE + 1];
				isc_netaddr_format(&addr, buf, sizeof(buf));
				cfg_obj_log(ce, lctx, ISC_LOG_WARNING,
					    "'%s/%u': address/prefix length "
					    "mismatch", buf, bitlen);
			}

			/*
			 * If nesting ACLs (nest_level != 0), we negate
			 * the nestedacl element, not the iptable entry.
			 */
			result = dns_iptable_addprefix(iptab, &addr, bitlen,
					      ISC_TF(nest_level != 0 || !neg));
			if (result != ISC_R_SUCCESS)
				goto cleanup;

			if (nest_level > 0) {
				INSIST(dacl->length < dacl->alloc);
				de->type = dns_aclelementtype_nestedacl;
				de->negative = neg;
			} else
				continue;
		} else if (cfg_obj_islist(ce)) {
			/*
			 * If we're nesting ACLs, put the nested
			 * ACL onto the elements list; otherwise
			 * merge it into *this* ACL.  We nest ACLs
			 * in two cases: 1) sortlist, 2) if the
			 * nested ACL contains negated members.
			 */
			if (inneracl != NULL)
				dns_acl_detach(&inneracl);
			result = cfg_acl_fromconfig(ce, cctx, lctx,
						    ctx, mctx, new_nest_level,
						    &inneracl);
			if (result != ISC_R_SUCCESS)
				goto cleanup;
nested_acl:
			if (nest_level > 0 || inneracl->has_negatives) {
				INSIST(dacl->length < dacl->alloc);
				de->type = dns_aclelementtype_nestedacl;
				de->negative = neg;
				if (de->nestedacl != NULL)
					dns_acl_detach(&de->nestedacl);
				dns_acl_attach(inneracl,
					       &de->nestedacl);
				dns_acl_detach(&inneracl);
				/* Fall through. */
			} else {
				INSIST(dacl->length + inneracl->length
				       <= dacl->alloc);
				dns_acl_merge(dacl, inneracl,
					      ISC_TF(!neg));
				de += inneracl->length;  /* elements added */
				dns_acl_detach(&inneracl);
				INSIST(dacl->length <= dacl->alloc);
				continue;
			}
		} else if (cfg_obj_istype(ce, &cfg_type_keyref)) {
			/* Key name. */
			INSIST(dacl->length < dacl->alloc);
			de->type = dns_aclelementtype_keyname;
			de->negative = neg;
			dns_name_init(&de->keyname, NULL);
			result = convert_keyname(ce, lctx, mctx,
						 &de->keyname);
			if (result != ISC_R_SUCCESS)
				goto cleanup;
#ifdef HAVE_GEOIP
		} else if (cfg_obj_istuple(ce) &&
			   cfg_obj_isvoid(cfg_tuple_get(ce, "negated")))
		{
			INSIST(dacl->length < dacl->alloc);
			result = parse_geoip_element(ce, lctx, ctx, de);
			if (result != ISC_R_SUCCESS)
				goto cleanup;
			de->type = dns_aclelementtype_geoip;
			de->negative = neg;
#endif /* HAVE_GEOIP */
		} else if (cfg_obj_isstring(ce)) {
			/* ACL name. */
			const char *name = cfg_obj_asstring(ce);
			if (strcasecmp(name, "any") == 0) {
				/* Iptable entry with zero bit length. */
				result = dns_iptable_addprefix(iptab, NULL, 0,
					      ISC_TF(nest_level != 0 || !neg));
				if (result != ISC_R_SUCCESS)
					goto cleanup;

				if (nest_level != 0) {
					INSIST(dacl->length < dacl->alloc);
					de->type = dns_aclelementtype_nestedacl;
					de->negative = neg;
				} else
					continue;
			} else if (strcasecmp(name, "none") == 0) {
				/* none == !any */
				/*
				 * We don't unconditional set
				 * dacl->has_negatives and
				 * de->negative to true so we can handle
				 * "!none;".
				 */
				result = dns_iptable_addprefix(iptab, NULL, 0,
					      ISC_TF(nest_level != 0 || neg));
				if (result != ISC_R_SUCCESS)
					goto cleanup;

				if (!neg)
					dacl->has_negatives = !neg;

				if (nest_level != 0) {
					INSIST(dacl->length < dacl->alloc);
					de->type = dns_aclelementtype_nestedacl;
					de->negative = !neg;
				} else
					continue;
			} else if (strcasecmp(name, "localhost") == 0) {
				INSIST(dacl->length < dacl->alloc);
				de->type = dns_aclelementtype_localhost;
				de->negative = neg;
			} else if (strcasecmp(name, "localnets") == 0) {
				INSIST(dacl->length < dacl->alloc);
				de->type = dns_aclelementtype_localnets;
				de->negative = neg;
			} else {
				if (inneracl != NULL)
					dns_acl_detach(&inneracl);
				/*
				 * This call should just find the cached
				 * of the named acl.
				 */
				result = convert_named_acl(ce, cctx, lctx, ctx,
							   mctx, new_nest_level,
							   &inneracl);
				if (result != ISC_R_SUCCESS)
					goto cleanup;

				goto nested_acl;
			}
		} else {
			cfg_obj_log(ce, lctx, ISC_LOG_WARNING,
				    "address match list contains "
				    "unsupported element type");
			result = ISC_R_FAILURE;
			goto cleanup;
		}

		/*
		 * This should only be reached for localhost, localnets
		 * and keyname elements, and nested ACLs if nest_level is
		 * nonzero (i.e., in sortlists).
		 */
		if (de->nestedacl != NULL &&
		    de->type != dns_aclelementtype_nestedacl)
			dns_acl_detach(&de->nestedacl);

		dacl->node_count++;
		de->node_num = dacl->node_count;

		dacl->length++;
		de++;
		INSIST(dacl->length <= dacl->alloc);
	}

	dns_acl_attach(dacl, target);
	result = ISC_R_SUCCESS;

 cleanup:
	if (inneracl != NULL)
		dns_acl_detach(&inneracl);
	dns_acl_detach(&dacl);
	return (result);
}
Exemplo n.º 7
0
/*
 * Recursively pre-parse an ACL definition to find the total number
 * of non-IP-prefix elements (localhost, localnets, key) in all nested
 * ACLs, so that the parent will have enough space allocated for the
 * elements table after all the nested ACLs have been merged in to the
 * parent.
 */
static isc_result_t
count_acl_elements(const cfg_obj_t *caml, const cfg_obj_t *cctx,
		   isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx,
		   isc_uint32_t *count, isc_boolean_t *has_negative)
{
	const cfg_listelt_t *elt;
	isc_result_t result;
	isc_uint32_t n = 0;

	REQUIRE(count != NULL);

	if (has_negative != NULL)
		*has_negative = ISC_FALSE;

	for (elt = cfg_list_first(caml);
	     elt != NULL;
	     elt = cfg_list_next(elt)) {
		const cfg_obj_t *ce = cfg_listelt_value(elt);

		/* might be a negated element, in which case get the value. */
		if (cfg_obj_istuple(ce)) {
			const cfg_obj_t *negated =
				cfg_tuple_get(ce, "negated");
			if (! cfg_obj_isvoid(negated)) {
				ce = negated;
				if (has_negative != NULL)
					*has_negative = ISC_TRUE;
			}
		}

		if (cfg_obj_istype(ce, &cfg_type_keyref)) {
			n++;
		} else if (cfg_obj_islist(ce)) {
			isc_boolean_t negative;
			isc_uint32_t sub;
			result = count_acl_elements(ce, cctx, lctx, ctx, mctx,
						    &sub, &negative);
			if (result != ISC_R_SUCCESS)
				return (result);
			n += sub;
			if (negative)
				n++;
#ifdef HAVE_GEOIP
		} else if (cfg_obj_istuple(ce) &&
			   cfg_obj_isvoid(cfg_tuple_get(ce, "negated")))
		{
			n++;
#endif /* HAVE_GEOIP */
		} else if (cfg_obj_isstring(ce)) {
			const char *name = cfg_obj_asstring(ce);
			if (strcasecmp(name, "localhost") == 0 ||
			    strcasecmp(name, "localnets") == 0 ||
			    strcasecmp(name, "none") == 0)
			{
				n++;
			} else if (strcasecmp(name, "any") != 0) {
				dns_acl_t *inneracl = NULL;
				/*
				 * Convert any named acls we reference now if
				 * they have not already been converted.
				 */
				result = convert_named_acl(ce, cctx, lctx, ctx,
							   mctx, 0, &inneracl);
				if (result == ISC_R_SUCCESS) {
					if (inneracl->has_negatives)
						n++;
					else
						n += inneracl->length;
					dns_acl_detach(&inneracl);
				} else
					return (result);
			}
		}
	}

	*count = n;
	return (ISC_R_SUCCESS);
}
Exemplo n.º 8
0
isc_result_t
cfg_acl_fromconfig(const cfg_obj_t *caml,
		   const cfg_obj_t *cctx,
		   isc_log_t *lctx,
		   cfg_aclconfctx_t *ctx,
		   isc_mem_t *mctx,
		   unsigned int nest_level,
		   dns_acl_t **target)
{
	isc_result_t result;
	dns_acl_t *dacl = NULL, *inneracl = NULL;
	dns_aclelement_t *de;
	const cfg_listelt_t *elt;
	dns_iptable_t *iptab;
	int new_nest_level = 0;

	if (nest_level != 0)
		new_nest_level = nest_level - 1;

	REQUIRE(target != NULL);
	REQUIRE(*target == NULL || DNS_ACL_VALID(*target));

	if (*target != NULL) {
		/*
		 * If target already points to an ACL, then we're being
		 * called recursively to configure a nested ACL.  The
		 * nested ACL's contents should just be absorbed into its
		 * parent ACL.
		 */
		dns_acl_attach(*target, &dacl);
		dns_acl_detach(target);
	} else {
		/*
		 * Need to allocate a new ACL structure.  Count the items
		 * in the ACL definition that will require space in the
		 * elements table.  (Note that if nest_level is nonzero,
		 * *everything* goes in the elements table.)
		 */
		int nelem;

		if (nest_level == 0)
			nelem = count_acl_elements(caml, cctx, NULL);
		else
			nelem = cfg_list_length(caml, ISC_FALSE);

		result = dns_acl_create(mctx, nelem, &dacl);
		if (result != ISC_R_SUCCESS)
			return (result);
	}

	de = dacl->elements;
	for (elt = cfg_list_first(caml);
	     elt != NULL;
	     elt = cfg_list_next(elt)) {
		const cfg_obj_t *ce = cfg_listelt_value(elt);
		isc_boolean_t	neg;

		if (cfg_obj_istuple(ce)) {
			/* This must be a negated element. */
			ce = cfg_tuple_get(ce, "value");
			neg = ISC_TRUE;
			dacl->has_negatives = ISC_TRUE;
		} else
			neg = ISC_FALSE;

		/*
		 * If nest_level is nonzero, then every element is
		 * to be stored as a separate, nested ACL rather than
		 * merged into the main iptable.
		 */
		iptab = dacl->iptable;

		if (nest_level != 0) {
			result = dns_acl_create(mctx,
						cfg_list_length(ce, ISC_FALSE),
						&de->nestedacl);
			if (result != ISC_R_SUCCESS)
				goto cleanup;
			iptab = de->nestedacl->iptable;
		}

		if (cfg_obj_isnetprefix(ce)) {
			/* Network prefix */
			isc_netaddr_t	addr;
			unsigned int	bitlen;

			cfg_obj_asnetprefix(ce, &addr, &bitlen);

			/*
			 * If nesting ACLs (nest_level != 0), we negate
			 * the nestedacl element, not the iptable entry.
			 */
			result = dns_iptable_addprefix(iptab, &addr, bitlen,
					      ISC_TF(nest_level != 0 || !neg));
			if (result != ISC_R_SUCCESS)
				goto cleanup;

			if (nest_level > 0) {
				de->type = dns_aclelementtype_nestedacl;
				de->negative = neg;
			} else
				continue;
		} else if (cfg_obj_islist(ce)) {
			/*
			 * If we're nesting ACLs, put the nested
			 * ACL onto the elements list; otherwise
			 * merge it into *this* ACL.  We nest ACLs
			 * in two cases: 1) sortlist, 2) if the
			 * nested ACL contains negated members.
			 */
			if (inneracl != NULL)
				dns_acl_detach(&inneracl);
			result = cfg_acl_fromconfig(ce, cctx, lctx,
						    ctx, mctx, new_nest_level,
						    &inneracl);
			if (result != ISC_R_SUCCESS)
				goto cleanup;
nested_acl:
			if (nest_level > 0 || inneracl->has_negatives) {
				de->type = dns_aclelementtype_nestedacl;
				de->negative = neg;
				if (de->nestedacl != NULL)
					dns_acl_detach(&de->nestedacl);
				dns_acl_attach(inneracl,
					       &de->nestedacl);
				dns_acl_detach(&inneracl);
				/* Fall through. */
			} else {
				dns_acl_merge(dacl, inneracl,
					      ISC_TF(!neg));
				de += inneracl->length;  /* elements added */
				dns_acl_detach(&inneracl);
				continue;
			}
		} else if (cfg_obj_istype(ce, &cfg_type_keyref)) {
			/* Key name. */
			de->type = dns_aclelementtype_keyname;
			de->negative = neg;
			dns_name_init(&de->keyname, NULL);
			result = convert_keyname(ce, lctx, mctx,
						 &de->keyname);
			if (result != ISC_R_SUCCESS)
				goto cleanup;
		} else if (cfg_obj_isstring(ce)) {
			/* ACL name. */
			const char *name = cfg_obj_asstring(ce);
			if (strcasecmp(name, "any") == 0) {
				/* Iptable entry with zero bit length. */
				result = dns_iptable_addprefix(iptab, NULL, 0,
					      ISC_TF(nest_level != 0 || !neg));
				if (result != ISC_R_SUCCESS)
					goto cleanup;

				if (nest_level != 0) {
					de->type = dns_aclelementtype_nestedacl;
					de->negative = neg;
				} else
					continue;
			} else if (strcasecmp(name, "none") == 0) {
				/* none == !any */
				/*
				 * We don't unconditional set
				 * dacl->has_negatives and
				 * de->negative to true so we can handle
				 * "!none;".
				 */
				result = dns_iptable_addprefix(iptab, NULL, 0,
					      ISC_TF(nest_level != 0 || neg));
				if (result != ISC_R_SUCCESS)
					goto cleanup;

				if (!neg)
					dacl->has_negatives = !neg;

				if (nest_level != 0) {
					de->type = dns_aclelementtype_nestedacl;
					de->negative = !neg;
				} else
					continue;
#ifdef SUPPORT_GEOIP
			} else if ((0 == (strncmp("country_", name, 8))) && (10 == strlen(name))) {
				/* It is a country code */
				de->type = dns_aclelementtype_ipcountry;
				de->country[0] = name[8];
				de->country[1] = name[9];
				de->country[2] = '\0';
#endif
			} else if (strcasecmp(name, "localhost") == 0) {
				de->type = dns_aclelementtype_localhost;
				de->negative = neg;
			} else if (strcasecmp(name, "localnets") == 0) {
				de->type = dns_aclelementtype_localnets;
				de->negative = neg;
			} else {
				if (inneracl != NULL)
					dns_acl_detach(&inneracl);
				result = convert_named_acl(ce, cctx, lctx, ctx,
							   mctx, new_nest_level,
							   &inneracl);
				if (result != ISC_R_SUCCESS)
					goto cleanup;

				goto nested_acl;
			}
		} else {
			cfg_obj_log(ce, lctx, ISC_LOG_WARNING,
				    "address match list contains "
				    "unsupported element type");
			result = ISC_R_FAILURE;
			goto cleanup;
		}

		/*
		 * This should only be reached for localhost, localnets
		 * and keyname elements, and nested ACLs if nest_level is
		 * nonzero (i.e., in sortlists).
		 */
		if (de->nestedacl != NULL &&
		    de->type != dns_aclelementtype_nestedacl)
			dns_acl_detach(&de->nestedacl);

		dacl->node_count++;
		de->node_num = dacl->node_count;

		dacl->length++;
		de++;
		INSIST(dacl->length <= dacl->alloc);
	}

	dns_acl_attach(dacl, target);
	result = ISC_R_SUCCESS;

 cleanup:
	if (inneracl != NULL)
		dns_acl_detach(&inneracl);
	dns_acl_detach(&dacl);
	return (result);
}