tags 501800 +patch
thanks

After further discussions with upstream authors :

Evan Hunt wrotes :

--8<--------------------------------
Thanks to  Emmanuel Bouthenot for the assistance, I was able to
reproduce the issue with his instructions.  It turned out to be a bug
that was fixed in 9.5.1b2.

There are several other ACL problems that have been fixed in that
release and in 9.5.1b3, which is due out in a few days.  I'd recommend
using 9.5.1 when it's complete (in about a month, most likely), but
I'm told Debian is planning is to release 9.5.0-P2 plus patches
instead.  So I've rolled all the ACL fixes in the 9.5.1 pipeline up
into a single patch and attached it to this email. This reflects the
following changes:


2474.   [bug]           ACL structures could be allocated with insufficient
                        space, causing an array overrun. [RT #18765]
2470.   [bug]           Elements of the isc_radix_node_t could be incorrectly
                        overwritten.  [RT# 18719]
2456.   [bug]           In ACLs, ::/0 and 0.0.0.0/0 would both match any
                        address, regardless of family.  They now
                        correctly distinguish IPv4 from IPv6.  [RT #18559]
2441.   [bug]           isc_radix_insert() could copy radix tree nodes
                        incompletely. [RT #18573]
2439.   [bug]           Potential NULL dereference in dns_acl_isanyornone().
                        [RT #18559]

(For the record, the bug you hit was 2441.)

Thanks again and let me know if you have any questions.
-------------------------------->8--

The patch is attached.


Cheers,

-- 
Emmanuel Bouthenot
 mail : [EMAIL PROTECTED]
  gpg : 0x414EC36E
  jid : [EMAIL PROTECTED]
  irc : kolter@(freenode|oftc)
Index: lib/dns/acl.c
===================================================================
RCS file: /proj/cvs/prod/bind9/lib/dns/acl.c,v
retrieving revision 1.37.2.7
diff -u -u -r1.37.2.7 acl.c
--- lib/dns/acl.c	29 Apr 2008 01:04:14 -0000	1.37.2.7
+++ lib/dns/acl.c	28 Oct 2008 00:25:02 -0000
@@ -148,7 +148,10 @@
 		return (ISC_FALSE);
 
 	if (acl->iptable->radix->head->prefix->bitlen == 0 &&
-	    *(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
+  	    acl->iptable->radix->head->data[0] != NULL &&
+	    acl->iptable->radix->head->data[0] ==
+	    acl->iptable->radix->head->data[1] &&
+  	    *(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
 		return (ISC_TRUE);
 
 	return (ISC_FALSE); /* All others */
@@ -220,8 +223,6 @@
 
 	/* Found a match. */
 	if (result == ISC_R_SUCCESS && node != NULL) {
-		if (node->bit == 0)
-			family = AF_INET;
 		match_num = node->node_num[ISC_IS6(family)];
 		if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE)
 			*match = match_num;
@@ -491,9 +492,8 @@
 	isc_boolean_t secure;
 	int bitlen, family;
 
-	/* Bitlen 0 means "any" or "none", which is always treated as IPv4 */
 	bitlen = prefix->bitlen;
-	family = bitlen ? prefix->family : AF_INET;
+	family = prefix->family;
 
 	/* Negated entries are always secure. */
 	secure = * (isc_boolean_t *)data[ISC_IS6(family)];
Index: lib/dns/iptable.c
===================================================================
RCS file: /proj/cvs/prod/bind9/lib/dns/iptable.c,v
retrieving revision 1.5.46.3
diff -u -u -r1.5.46.3 iptable.c
--- lib/dns/iptable.c	21 Jan 2008 21:02:24 -0000	1.5.46.3
+++ lib/dns/iptable.c	28 Oct 2008 00:25:02 -0000
@@ -70,22 +70,39 @@
 
 	NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
 
-	/* Bitlen 0 means "any" or "none", which is always treated as IPv4 */
-	family = bitlen ? pfx.family : AF_INET;
-
 	result = isc_radix_insert(tab->radix, &node, NULL, &pfx);
-
-	if (result != ISC_R_SUCCESS)
+	if (result != ISC_R_SUCCESS) {
+		isc_refcount_destroy(&pfx.refcount);
 		return(result);
+	}
 
-	/* If the node already contains data, don't overwrite it */
-	if (node->data[ISC_IS6(family)] == NULL) {
-		if (pos)
-			node->data[ISC_IS6(family)] = &dns_iptable_pos;
-		else
-			node->data[ISC_IS6(family)] = &dns_iptable_neg;
+	/* If a node already contains data, don't overwrite it */
+	family = pfx.family;
+	if (family == AF_UNSPEC) {
+ 		/* "any" or "none" */
+ 		INSIST(pfx.bitlen == 0);
+ 		if (pos) {
+ 			if (node->data[0] == NULL)
+ 				node->data[0] = &dns_iptable_pos;
+ 			if (node->data[1] == NULL)
+ 				node->data[1] = &dns_iptable_pos;
+ 		} else {
+ 			if (node->data[0] == NULL)
+ 				node->data[0] = &dns_iptable_neg;
+ 			if (node->data[1] == NULL)
+ 				node->data[1] = &dns_iptable_neg;
+ 		}
+ 	} else {
+ 		/* any other prefix */
+ 		if (node->data[ISC_IS6(family)] == NULL) {
+ 			if (pos)
+ 				node->data[ISC_IS6(family)] = &dns_iptable_pos;
+ 			else
+ 				node->data[ISC_IS6(family)] = &dns_iptable_neg;
+ 		}
 	}
 
+	isc_refcount_destroy(&pfx.refcount);
 	return (ISC_R_SUCCESS);
 }
 
Index: lib/isc/radix.c
===================================================================
RCS file: /proj/cvs/prod/bind9/lib/isc/radix.c,v
retrieving revision 1.9.6.5.2.1
diff -u -u -r1.9.6.5.2.1 radix.c
--- lib/isc/radix.c	24 Jul 2008 02:03:22 -0000	1.9.6.5.2.1
+++ lib/isc/radix.c	28 Oct 2008 00:25:03 -0000
@@ -53,7 +53,7 @@
 
 	REQUIRE(target != NULL);
 
-	if (family != AF_INET6 && family != AF_INET)
+	if (family != AF_INET6 && family != AF_INET && family != AF_UNSPEC)
 		return (ISC_R_NOTIMPLEMENTED);
 
 	prefix = isc_mem_get(mctx, sizeof(isc_prefix_t));
@@ -64,6 +64,7 @@
 		prefix->bitlen = (bitlen >= 0) ? bitlen : 128;
 		memcpy(&prefix->add.sin6, dest, 16);
 	} else {
+		/* AF_UNSPEC is "any" or "none"--treat it as AF_INET */
 		prefix->bitlen = (bitlen >= 0) ? bitlen : 32;
 		memcpy(&prefix->add.sin, dest, 4);
 	}
@@ -95,7 +96,8 @@
 _ref_prefix(isc_mem_t *mctx, isc_prefix_t **target, isc_prefix_t *prefix) {
 	INSIST(prefix != NULL);
 	INSIST((prefix->family == AF_INET && prefix->bitlen <= 32) ||
-	       (prefix->family == AF_INET6 && prefix->bitlen <= 128));
+	       (prefix->family == AF_INET6 && prefix->bitlen <= 128) ||
+	       (prefix->family == AF_UNSPEC && prefix->bitlen == 0));
 	REQUIRE(target != NULL);
 
 	/* If this prefix is a static allocation, copy it into new memory */
@@ -236,7 +238,7 @@
 	isc_radix_node_t *stack[RADIX_MAXBITS + 1];
 	u_char *addr;
 	isc_uint32_t bitlen;
-	int family, tfamily = -1;
+	int tfamily = -1;
 	int cnt = 0;
 
 	REQUIRE(radix != NULL);
@@ -276,16 +278,12 @@
 		if (_comp_with_mask(isc_prefix_tochar(node->prefix),
 				    isc_prefix_tochar(prefix),
 				    node->prefix->bitlen)) {
-			/* Bitlen 0 means "any" or "none",
-			   which is always treated as IPv4 */
-			family = node->prefix->bitlen ?
-				 prefix->family : AF_INET;
-			if (node->node_num[ISC_IS6(family)] != -1 &&
+			if (node->node_num[ISC_IS6(prefix->family)] != -1 &&
 				 ((*target == NULL) ||
 				  (*target)->node_num[ISC_IS6(tfamily)] >
-				   node->node_num[ISC_IS6(family)])) {
+				   node->node_num[ISC_IS6(prefix->family)])) {
 				*target = node;
-				tfamily = family;
+				tfamily = prefix->family;
 			}
 		}
 	}
@@ -303,7 +301,7 @@
 {
 	isc_radix_node_t *node, *new_node, *parent, *glue = NULL;
 	u_char *addr, *test_addr;
-	isc_uint32_t bitlen, family, check_bit, differ_bit;
+	isc_uint32_t bitlen, fam, check_bit, differ_bit;
 	isc_uint32_t i, j, r;
 	isc_result_t result;
 
@@ -317,9 +315,7 @@
 	INSIST(prefix != NULL);
 
 	bitlen = prefix->bitlen;
-
-	/* Bitlen 0 means "any" or "none", which is always treated as IPv4 */
-	family = bitlen ? prefix->family : AF_INET;
+	fam = prefix->family;
 
 	if (radix->head == NULL) {
 		node = isc_mem_get(radix->mctx, sizeof(isc_radix_node_t));
@@ -353,8 +349,14 @@
 			node->data[0] = source->data[0];
 			node->data[1] = source->data[1];
 		} else {
-			node->node_num[ISC_IS6(family)] =
-				++radix->num_added_node;
+			if (fam == AF_UNSPEC) {
+				/* "any" or "none" */
+				node->node_num[0] = node->node_num[1] =
+					++radix->num_added_node;
+			} else {
+				node->node_num[ISC_IS6(fam)] =
+					++radix->num_added_node;
+			}
 			node->data[0] = NULL;
 			node->data[1] = NULL;
 		}
@@ -417,25 +419,71 @@
 	if (differ_bit == bitlen && node->bit == bitlen) {
 		if (node->prefix != NULL) {
 			/* Set node_num only if it hasn't been set before */
-			if (node->node_num[ISC_IS6(family)] == -1)
-				node->node_num[ISC_IS6(family)] =
-					 ++radix->num_added_node;
+			if (source != NULL) {
+				/* Merging node */
+				if (node->node_num[0] == -1 &&
+				    source->node_num[0] != -1) {
+					node->node_num[0] =
+						radix->num_added_node +
+						source->node_num[0];
+					node->data[0] = source->data[0];
+				}
+				if (node->node_num[1] == -1 &&
+				    source->node_num[0] != -1) {
+					node->node_num[1] =
+						radix->num_added_node +
+						source->node_num[1];
+					node->data[1] = source->data[1];
+				}
+			} else {
+				if (fam == AF_UNSPEC) {
+ 					/* "any" or "none" */
+ 					int next = radix->num_added_node + 1;
+ 					if (node->node_num[0] == -1) {
+ 						node->node_num[0] = next;
+ 						radix->num_added_node = next;
+ 					}
+ 					if (node->node_num[1] == -1) {
+ 						node->node_num[1] = next;
+ 						radix->num_added_node = next;
+ 					}
+ 				} else {
+ 					if (node->node_num[ISC_IS6(fam)] == -1)
+ 						node->node_num[ISC_IS6(fam)]
+ 						   = ++radix->num_added_node;
+ 				}
+			}
 			*target = node;
 			return (ISC_R_SUCCESS);
+		} else {
+			result =
+				_ref_prefix(radix->mctx, &node->prefix, prefix);
+			if (result != ISC_R_SUCCESS)
+				return (result);
 		}
-		result = _ref_prefix(radix->mctx, &node->prefix, prefix);
-		if (result != ISC_R_SUCCESS)
-			return (result);
 		INSIST(node->data[0] == NULL && node->node_num[0] == -1 &&
 		       node->data[1] == NULL && node->node_num[1] == -1);
 		if (source != NULL) {
 			/* Merging node */
-			node->node_num[ISC_IS6(family)] =
-				radix->num_added_node +
-				source->node_num[ISC_IS6(family)];
+			if (source->node_num[0] != -1) {
+				node->node_num[0] = radix->num_added_node +
+						    source->node_num[0];
+				node->data[0] = source->data[0];
+			}
+			if (source->node_num[1] != -1) {
+				node->node_num[1] = radix->num_added_node +
+						    source->node_num[1];
+				node->data[1] = source->data[1];
+			}
 		} else {
-			node->node_num[ISC_IS6(family)] =
-				++radix->num_added_node;
+			if (fam == AF_UNSPEC) {
+				/* "any" or "none" */
+				node->node_num[0] = node->node_num[1] =
+					++radix->num_added_node;
+			} else {
+				node->node_num[ISC_IS6(fam)] =
+					++radix->num_added_node;
+			}
 		}
 		*target = node;
 		return (ISC_R_SUCCESS);
@@ -477,7 +525,14 @@
 		new_node->data[0] = source->data[0];
 		new_node->data[1] = source->data[1];
 	} else {
-		new_node->node_num[ISC_IS6(family)] = ++radix->num_added_node;
+		if (fam == AF_UNSPEC) {
+			/* "any" or "none" */
+			new_node->node_num[0] = new_node->node_num[1] =
+				++radix->num_added_node;
+		} else {
+			new_node->node_num[ISC_IS6(fam)] =
+				++radix->num_added_node;
+		}
 		new_node->data[0] = NULL;
 		new_node->data[1] = NULL;
 	}
@@ -525,7 +580,7 @@
 		glue->node_num[0] = glue->node_num[1] = -1;
 		radix->num_active_node++;
 		if (differ_bit < radix->maxbits &&
-		    BIT_TEST(addr[differ_bit >> 3], 0x80 >> (differ_bit & 0x07))) {
+		    BIT_TEST(addr[differ_bit>>3], 0x80 >> (differ_bit & 07))) {
 			glue->r = new_node;
 			glue->l = node;
 		} else {
Index: lib/isc/include/isc/radix.h
===================================================================
RCS file: /proj/cvs/prod/bind9/lib/isc/include/isc/radix.h,v
retrieving revision 1.5.46.4
diff -u -u -r1.5.46.4 radix.h
--- lib/isc/include/isc/radix.h	21 Jan 2008 23:46:23 -0000	1.5.46.4
+++ lib/isc/include/isc/radix.h	28 Oct 2008 00:25:03 -0000
@@ -37,7 +37,7 @@
 #define NETADDR_TO_PREFIX_T(na,pt,bits) \
 	do { \
 		memset(&(pt), 0, sizeof(pt)); \
-		if((bits) && (na) != NULL) { \
+		if((na) != NULL) { \
 			(pt).family = (na)->family; \
 			(pt).bitlen = (bits); \
 			if ((pt).family == AF_INET6) { \
@@ -46,14 +46,16 @@
 			} else \
 				memcpy(&(pt).add.sin, &(na)->type.in, \
 				       ((bits)+7)/8); \
-		} else \
-			(pt).family = AF_INET; \
+		} else { \
+			(pt).family = AF_UNSPEC; \
+			(pt).bitlen = 0; \
+		} \
 		isc_refcount_init(&(pt).refcount, 0); \
 	} while(0)
 
 typedef struct isc_prefix {
-    unsigned int family;	/* AF_INET | AF_INET6 */
-    unsigned int bitlen;
+    unsigned int family;	/* AF_INET | AF_INET6, or AF_UNSPEC for "any" */
+    unsigned int bitlen;	/* 0 for "any" */
     isc_refcount_t refcount;
     union {
 		struct in_addr sin;
Index: lib/isccfg/aclconf.c
===================================================================
RCS file: /proj/cvs/prod/bind9/lib/isccfg/aclconf.c,v
retrieving revision 1.17.100.2
diff -u -u -r1.17.100.2 aclconf.c
--- lib/isccfg/aclconf.c	24 Jul 2008 23:48:39 -0000	1.17.100.2
+++ lib/isccfg/aclconf.c	28 Oct 2008 00:25:03 -0000
@@ -160,6 +160,51 @@
 	return (dns_name_dup(dns_fixedname_name(&fixname), mctx, dnsname));
 }
 
+/*
+ * 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)
+{
+	const cfg_listelt_t *elt;
+	const cfg_obj_t *cacl = NULL;
+	isc_result_t result;
+	int n = 0;
+
+	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 (cfg_obj_istype(ce, &cfg_type_keyref)) {
+			n++;
+		} else if (cfg_obj_islist(ce)) {
+			n += count_acl_elements(ce, cctx);
+		} else if (cfg_obj_isstring(ce)) {
+			const char *name = cfg_obj_asstring(ce);
+			if (strcasecmp(name, "localhost") == 0 ||
+			    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) + 1;
+			}
+		}
+	}
+
+	return n;
+}
+
 isc_result_t
 cfg_acl_fromconfig(const cfg_obj_t *caml,
 		   const cfg_obj_t *cctx,
@@ -194,14 +239,18 @@
 	} else {
 		/*
 		 * Need to allocate a new ACL structure.  Count the items
-		 * in the ACL definition and allocate space for that many
-		 * elements (even though some or all of them may end up in
-		 * the iptable instead of the element array).
+		 * in the ACL definition that will require space in the
+		 * elemnts table.  (Note that if nest_level is nonzero,
+		 * *everything* goes in the elements table.)
 		 */
-		isc_boolean_t recurse = ISC_TF(nest_level == 0);
-		result = dns_acl_create(mctx,
-					cfg_list_length(caml, recurse),
-					&dacl);
+		int nelem;
+
+		if (nest_level == 0)
+			nelem = count_acl_elements(caml, cctx);
+		else
+			nelem = cfg_list_length(caml, ISC_FALSE);
+
+		result = dns_acl_create(mctx, nelem, &dacl);
 		if (result != ISC_R_SUCCESS)
 			return (result);
 	}
@@ -209,8 +258,7 @@
 	de = dacl->elements;
 	for (elt = cfg_list_first(caml);
 	     elt != NULL;
-	     elt = cfg_list_next(elt))
-	{
+	     elt = cfg_list_next(elt)) {
 		const cfg_obj_t *ce = cfg_listelt_value(elt);
 		isc_boolean_t	neg;
 

Reply via email to