File gnutls-CVE-2024-12243.patch of Package gnutls.37573

From 4760bc63531e3f5039e70ede91a20e1194410892 Mon Sep 17 00:00:00 2001
From: Daiki Ueno <ueno@gnu.org>
Date: Mon, 18 Nov 2024 17:23:46 +0900
Subject: [PATCH] x509: optimize name constraints processing

This switches the representation name constraints from linked lists to
array lists to optimize the lookup performance from O(n) to O(1), also
enforces a limit of name constraint checks against subject alternative
names.

Signed-off-by: Daiki Ueno <ueno@gnu.org>

Index: gnutls-3.6.7/lib/datum.c
===================================================================
--- gnutls-3.6.7.orig/lib/datum.c
+++ gnutls-3.6.7/lib/datum.c
@@ -29,6 +29,7 @@
 #include <num.h>
 #include <datum.h>
 #include "errors.h"
+#include "intprops.h"
 
 int
 _gnutls_set_datum(gnutls_datum_t * dat, const void *data, size_t data_size)
@@ -61,10 +62,16 @@ _gnutls_set_strdatum(gnutls_datum_t * da
 		return 0;
 	}
 
-	dat->data = gnutls_malloc(data_size+1);
-	if (dat->data == NULL)
+	size_t capacity;
+	if (INT_ADD_OVERFLOW(data_size, 1))
+			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+	capacity = data_size + 1;
+
+	unsigned char *m = gnutls_malloc(capacity);
+	if (!m)
 		return GNUTLS_E_MEMORY_ERROR;
 
+	dat->data = m;
 	dat->size = data_size;
 	memcpy(dat->data, data, data_size);
 	dat->data[data_size] = 0;
Index: gnutls-3.6.7/lib/x509/name_constraints.c
===================================================================
--- gnutls-3.6.7.orig/lib/x509/name_constraints.c
+++ gnutls-3.6.7/lib/x509/name_constraints.c
@@ -33,49 +33,102 @@
 #include <gnutls/x509-ext.h>
 #include <x509_b64.h>
 #include <x509_int.h>
+#include <x509_ext_int.h>
 #include <libtasn1.h>
 
 #include "ip.h"
 #include "ip-in-cidr.h"
+#include "intprops.h"
+
+#define MAX_NC_CHECKS (1 << 20)
+
+struct name_constraints_node_st {
+	unsigned type;
+	gnutls_datum_t name;
+};
+
+struct name_constraints_node_list_st {
+	struct name_constraints_node_st **data;
+	size_t size;
+	size_t capacity;
+};
+
+struct gnutls_name_constraints_st {
+	struct name_constraints_node_list_st nodes; /* owns elements */
+	struct name_constraints_node_list_st permitted; /* borrows elements */
+	struct name_constraints_node_list_st excluded; /* borrows elements */
+};
+
+static struct name_constraints_node_st *
+name_constraints_node_new(gnutls_x509_name_constraints_t nc, unsigned type,
+			  unsigned char *data, unsigned int size);
+
+static int
+name_constraints_node_list_add(struct name_constraints_node_list_st *list,
+			       struct name_constraints_node_st *node)
+{
+	if (!list->capacity || list->size == list->capacity) {
+		size_t new_capacity = list->capacity;
+		struct name_constraints_node_st **new_data;
+
+		if (INT_MULTIPLY_OVERFLOW(new_capacity, 2))
+			return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+		new_capacity *= 2;
+		if (INT_ADD_OVERFLOW(new_capacity, 1))
+			return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+		new_capacity += 1;
+		new_data = gnutls_realloc(
+			list->data,
+			new_capacity * sizeof(struct name_constraints_node_st *)
+		);
+		if (!new_data)
+			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+		list->capacity = new_capacity;
+		list->data = new_data;
+	}
+	list->data[list->size++] = node;
+	return 0;
+}
 
 // for documentation see the implementation
-static int name_constraints_intersect_nodes(name_constraints_node_st * nc1,
-					    name_constraints_node_st * nc2,
-					    name_constraints_node_st ** intersection);
+static int name_constraints_intersect_nodes(
+	gnutls_x509_name_constraints_t nc,
+	const struct name_constraints_node_st *node1,
+	const struct name_constraints_node_st *node2,
+	struct name_constraints_node_st **intersection);
 
 /*-
- * is_nc_empty:
+ * _gnutls_x509_name_constraints_is_empty:
  * @nc: name constraints structure
- * @type: type (gnutls_x509_subject_alt_name_t)
+ * @type: type (gnutls_x509_subject_alt_name_t or 0)
  *
  * Test whether given name constraints structure has any constraints (permitted
  * or excluded) of a given type. @nc must be allocated (not NULL) before the call.
+ * If @type is 0, type checking will be skipped.
  *
- * Returns: 0 if @nc contains constraints of type @type, 1 otherwise
+ * Returns: false if @nc contains constraints of type @type, true otherwise
  -*/
-static unsigned is_nc_empty(struct gnutls_name_constraints_st* nc, unsigned type)
+bool _gnutls_x509_name_constraints_is_empty(gnutls_x509_name_constraints_t nc,
+					    unsigned type)
 {
-	name_constraints_node_st *t;
+	if (nc->permitted.size == 0 && nc->excluded.size == 0)
+		return true;
 
-	if (nc->permitted == NULL && nc->excluded == NULL)
-		return 1;
+	if (type == 0)
+		return false;
 
-	t = nc->permitted;
-	while (t != NULL) {
-		if (t->type == type)
-			return 0;
-		t = t->next;
+	for (size_t i = 0; i < nc->permitted.size; i++) {
+		if (nc->permitted.data[i]->type == type)
+			return false;
 	}
 
-	t = nc->excluded;
-	while (t != NULL) {
-		if (t->type == type)
-			return 0;
-		t = t->next;
+	for (size_t i = 0; i < nc->excluded.size; i++) {
+		if (nc->excluded.data[i]->type == type)
+			return false;
 	}
 
 	/* no constraint for that type exists */
-	return 1;
+	return true;
 }
 
 /*-
@@ -111,21 +164,16 @@ static int validate_name_constraints_nod
 	return GNUTLS_E_SUCCESS;
 }
 
-int _gnutls_extract_name_constraints(ASN1_TYPE c2, const char *vstr,
-									 name_constraints_node_st ** _nc)
+static int extract_name_constraints(gnutls_x509_name_constraints_t nc,
+									 				    asn1_node c2, const char *vstr,
+									 				    struct name_constraints_node_list_st *nodes)
 {
 	int ret;
 	char tmpstr[128];
 	unsigned indx = 0;
 	gnutls_datum_t tmp = { NULL, 0 };
 	unsigned int type;
-	struct name_constraints_node_st *nc, *prev;
-
-	prev = *_nc;
-	if (prev != NULL) {
-		while(prev->next != NULL)
-			prev = prev->next;
-	}
+	struct name_constraints_node_st *node;
 
 	do {
 		indx++;
@@ -145,25 +193,20 @@ int _gnutls_extract_name_constraints(ASN
 			goto cleanup;
 		}
 
-		nc = gnutls_malloc(sizeof(struct name_constraints_node_st));
-		if (nc == NULL) {
+		node = name_constraints_node_new(nc, type, tmp.data, tmp.size);
+		_gnutls_free_datum(&tmp);
+		if (node == NULL) {
 			gnutls_assert();
 			ret = GNUTLS_E_MEMORY_ERROR;
 			goto cleanup;
 		}
 
-		memcpy(&nc->name, &tmp, sizeof(gnutls_datum_t));
-		nc->type = type;
-		nc->next = NULL;
-
-		if (prev == NULL) {
-			*_nc = prev = nc;
-		} else {
-			prev->next = nc;
-			prev = nc;
+		ret = name_constraints_node_list_add(nodes, node);
+		if (ret < 0) {
+				gnutls_assert();
+				goto cleanup;
 		}
 
-		tmp.data = NULL;
 	} while (ret >= 0);
 
 	if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
@@ -178,145 +221,176 @@ int _gnutls_extract_name_constraints(ASN
 }
 
 /*-
- * _gnutls_name_constraints_node_free:
+ * name_constraints_node_free:
  * @node: name constriants node
  *
- * Deallocate a list of name constraints nodes starting at the given node.
+ * Deallocate a name constraints node.
  -*/
-void _gnutls_name_constraints_node_free(name_constraints_node_st *node)
+void name_constraints_node_free(struct name_constraints_node_st *node)
 {
-	name_constraints_node_st *next, *t;
-
-	t = node;
-	while (t != NULL) {
-		next = t->next;
-		gnutls_free(t->name.data);
-		gnutls_free(t);
-		t = next;
+	if (node) {
+		gnutls_free(node->name.data);
+		gnutls_free(node);
 	}
 }
 
+
+int _gnutls_x509_name_constraints_extract(asn1_node c2,
+										   					  const char *permitted_name,
+										   					  const char *excluded_name,
+										   					  gnutls_x509_name_constraints_t nc)
+{
+	int ret;
+	ret = extract_name_constraints(nc, c2, permitted_name, &nc->permitted);
+	if (ret < 0)
+		return gnutls_assert_val(ret);
+	ret = extract_name_constraints(nc, c2, excluded_name, &nc->excluded);
+	if (ret < 0)
+		return gnutls_assert_val(ret);
+
+	return ret;
+}
+
 /*-
  * name_constraints_node_new:
  * @type: name constraints type to set (gnutls_x509_subject_alt_name_t)
+ * @nc: a %gnutls_x509_name_constraints_t
  * @data: name.data to set or NULL
  * @size: name.size to set
  *
  * Allocate a new name constraints node and set its type, name size and name data.
- * If @data is set to NULL, name data will be an array of \x00 (the length of @size).
- * The .next pointer is set to NULL.
  *
  * Returns: Pointer to newly allocated node or NULL in case of memory error.
  -*/
-static name_constraints_node_st* name_constraints_node_new(unsigned type,
-							   unsigned char *data,
-							   unsigned int size)
+static struct name_constraints_node_st *
+name_constraints_node_new(gnutls_x509_name_constraints_t nc, unsigned type,
+						   			  unsigned char *data, unsigned int size)
 {
-	name_constraints_node_st *tmp = gnutls_malloc(sizeof(struct name_constraints_node_st));
+	struct name_constraints_node_st *tmp;
+	int ret;
+	tmp = gnutls_calloc(1, sizeof(struct name_constraints_node_st));
 	if (tmp == NULL)
 		return NULL;
+
 	tmp->type = type;
-	tmp->next = NULL;
-	tmp->name.size = size;
-	tmp->name.data = NULL;
-	if (tmp->name.size > 0) {
 
-		tmp->name.data = gnutls_malloc(tmp->name.size);
-		if (tmp->name.data == NULL) {
+	if (data) {
+		ret = _gnutls_set_strdatum(&tmp->name, data, size);
+		if (ret < 0) {
+			gnutls_assert();
 			gnutls_free(tmp);
 			return NULL;
 		}
-		if (data != NULL) {
-			memcpy(tmp->name.data, data, size);
-		} else {
-			memset(tmp->name.data, 0, size);
-		}
 	}
+
+	ret = name_constraints_node_list_add(&nc->nodes, tmp);
+	if (ret < 0) {
+		gnutls_assert();
+		name_constraints_node_free(tmp);
+		return NULL;
+	}
+
 	return tmp;
 }
 
 /*-
- * @brief _gnutls_name_constraints_intersect:
- * @_nc: first name constraints list (permitted)
- * @_nc2: name constraints list to merge with (permitted)
- * @_nc_excluded: Corresponding excluded name constraints list
+ * @brief name_constraints_node_list_intersect:           *
+ * @nc: %gnutls_x509_name_constraints_t
+ * @permitted: first name constraints list (permitted)
+ * @permitted2: name constraints list to merge with (permitted)
+ * @excluded: Corresponding excluded name constraints list
  *
- * This function finds the intersection of @_nc and @_nc2. The result is placed in @_nc,
- * the original @_nc is deallocated. @_nc2 is not changed. If necessary, a universal
+ * This function finds the intersection of @permitted and @permitted2. The result is placed i*n @permitted,
+ * the original @permitted is modified. @permitted2 is not changed. If necessary, a universal
  * excluded name constraint node of the right type is added to the list provided
- * in @_nc_excluded.
+ * in @excluded.
  *
  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
  -*/
-static
-int _gnutls_name_constraints_intersect(name_constraints_node_st ** _nc,
-				       name_constraints_node_st * _nc2,
-				       name_constraints_node_st ** _nc_excluded)
+static int name_constraints_node_list_intersect(
+		gnutls_x509_name_constraints_t nc,
+		struct name_constraints_node_list_st *permitted,
+		const struct name_constraints_node_list_st *permitted2,
+		struct name_constraints_node_list_st *excluded)
 {
-	name_constraints_node_st *nc, *nc2, *t, *tmp, *dest = NULL, *prev = NULL;
+	struct name_constraints_node_st *tmp;
 	int ret, type, used;
-
+	struct name_constraints_node_list_st removed = { .data = NULL,
+													.size = 0,
+													.capacity = 0 };
 	/* temporary array to see, if we need to add universal excluded constraints
 	 * (see phase 3 for details)
 	 * indexed directly by (gnutls_x509_subject_alt_name_t enum - 1) */
 	unsigned char types_with_empty_intersection[GNUTLS_SAN_MAX];
 	memset(types_with_empty_intersection, 0, sizeof(types_with_empty_intersection));
 
-	if (*_nc == NULL || _nc2 == NULL)
+	if (permitted->size == 0 || permitted2->size == 0)
 		return 0;
 
 	/* Phase 1
-	 * For each name in _NC, if a _NC2 does not contain a name
-	 * with the same type, preserve the original name.
-	 * Do this also for node of unknown type (not DNS, email, IP */
-	t = nc = *_nc;
-	while (t != NULL) {
-		name_constraints_node_st *next = t->next;
-		nc2 = _nc2;
-		while (nc2 != NULL) {
-			if (t->type == nc2->type) {
+	 * For each name in PERMITTED, if a PERMITTED2 does not contain a name
+	 * with the same type, move the original name to REMOVED.
+	 * Do this also for node of unknown type (not DNS, email, IP) */
+	for (size_t i = 0; i < permitted->size;) {
+		struct name_constraints_node_st *t = permitted->data[i];
+		const struct name_constraints_node_st *found = NULL;
+
+		for (size_t j = 0; j < permitted2->size; j++) {
+			const struct name_constraints_node_st *t2 =
+				permitted2->data[j];
+			if (t->type == t2->type) {
 				// check bounds (we will use 't->type' as index)
-				if (t->type > GNUTLS_SAN_MAX || t->type == 0)
-					return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+				if (t->type > GNUTLS_SAN_MAX || t->type == 0) {
+					gnutls_assert();
+					ret = GNUTLS_E_INTERNAL_ERROR;
+					goto cleanup;
+				}
 				// note the possibility of empty intersection for this type
 				// if we add something to the intersection in phase 2,
 				// we will reset this flag back to 0 then
 				types_with_empty_intersection[t->type - 1] = 1;
+				found = t2;
 				break;
 			}
-			nc2 = nc2->next;
 		}
-		if (nc2 == NULL ||
-			(t->type != GNUTLS_SAN_DNSNAME &&
-			 t->type != GNUTLS_SAN_RFC822NAME &&
-			 t->type != GNUTLS_SAN_IPADDRESS)
-		   ) {
-			/* move node from NC to DEST */
-			if (prev != NULL)
-				prev->next = next;
-			else
-				prev = nc = next;
-			t->next = dest;
-			dest = t;
-		} else {
-			prev = t;
+
+		if (found != NULL && (t->type == GNUTLS_SAN_DNSNAME ||
+				      t->type == GNUTLS_SAN_RFC822NAME ||
+				      t->type == GNUTLS_SAN_IPADDRESS)) {
+			/* move node from PERMITTED to REMOVED */
+			ret = name_constraints_node_list_add(&removed, t);
+			if (ret < 0) {
+				gnutls_assert();
+				goto cleanup;
+			}
+			/* remove node by swapping */
+			if (i < permitted->size - 1)
+				permitted->data[i] =
+					permitted->data[permitted->size - 1];
+			permitted->size--;
+			continue;
 		}
-		t = next;
+		i++;
 	}
 
 	/* Phase 2
-	 * iterate through all combinations from nc2 and nc1
+	 * iterate through all combinations from PERMITTED2 and PERMITTED
 	 * and create intersections of nodes with same type */
-	nc2 = _nc2;
-	while (nc2 != NULL) {
-		// current nc2 node has not yet been used for any intersection
-		// (and is not in DEST either)
+	for (size_t i = 0; i < permitted2->size; i++) {
+		const struct name_constraints_node_st *t2 = permitted2->data[i];
+
+		// current PERMITTED2 node has not yet been used for any intersection
+		// (and is not in REMOVED either)
 		used = 0;
-		t = nc;
-		while (t != NULL) {
+		for (size_t j = 0; j < removed.size; j++) {
+			const struct name_constraints_node_st *t =
+				removed.data[j];
 			// save intersection of name constraints into tmp
-			ret = name_constraints_intersect_nodes(t, nc2, &tmp);
-			if (ret < 0) return gnutls_assert_val(ret);
+			ret = name_constraints_intersect_nodes(nc, t, t2, &tmp);
+			if (ret < 0) {
+				gnutls_assert();
+				goto cleanup;
+			}
 			used = 1;
 			// if intersection is not empty
 			if (tmp != NULL) { // intersection for this type is not empty
@@ -327,31 +401,34 @@ int _gnutls_name_constraints_intersect(n
 				}
 				// we will not add universal excluded constraint for this type
 				types_with_empty_intersection[tmp->type - 1] = 0;
-				// add intersection node to DEST
-				tmp->next = dest;
-				dest = tmp;
+				// add intersection node to PERMITTED
+				ret = name_constraints_node_list_add(permitted,
+								     tmp);
+				if (ret < 0) {
+					gnutls_assert();
+					goto cleanup;
+				}
 			}
-			t = t->next;
 		}
-		// if the node from nc2 was not used for intersection, copy it to DEST
+		// if the node from PERMITTED2 was not used for intersection, copy it to DEST
 		// Beware: also copies nodes other than DNS, email, IP,
 		//	 since their counterpart may have been moved in phase 1.
 		if (!used) {
-			tmp = name_constraints_node_new(nc2->type, nc2->name.data, nc2->name.size);
+			tmp = name_constraints_node_new(
+				nc, t2->type, t2->name.data, t2->name.size);
 			if (tmp == NULL) {
-				_gnutls_name_constraints_node_free(dest);
-				return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+				gnutls_assert();
+				ret = GNUTLS_E_MEMORY_ERROR;
+				goto cleanup;
+			}
+			ret = name_constraints_node_list_add(permitted, tmp);
+			if (ret < 0) {
+				gnutls_assert();
+				goto cleanup;
 			}
-			tmp->next = dest;
-			dest = tmp;
 		}
-		nc2 = nc2->next;
 	}
 
-	/* replace the original with the new */
-	_gnutls_name_constraints_node_free(nc);
-	*_nc = dest;
-
 	/* Phase 3
 	 * For each type: If we have empty permitted name constraints now
 	 * and we didn't have at the beginning, we have to add a new
@@ -364,60 +441,75 @@ int _gnutls_name_constraints_intersect(n
 		switch (type) {
 			case GNUTLS_SAN_IPADDRESS:
 				// add universal restricted range for IPv4
-				tmp = name_constraints_node_new(GNUTLS_SAN_IPADDRESS, NULL, 8);
+				tmp = name_constraints_node_new(nc, GNUTLS_SAN_IPADDRESS, NULL, 8);
 				if (tmp == NULL) {
-					_gnutls_name_constraints_node_free(dest);
-					return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+					gnutls_assert();
+					ret = GNUTLS_E_MEMORY_ERROR;
+					goto cleanup;
+				}
+				ret = name_constraints_node_list_add(excluded, tmp);
+				if (ret < 0) {
+					gnutls_assert();
+					goto cleanup;
 				}
-				tmp->next = *_nc_excluded;
-				*_nc_excluded = tmp;
 				// add universal restricted range for IPv6
-				tmp = name_constraints_node_new(GNUTLS_SAN_IPADDRESS, NULL, 32);
+				tmp = name_constraints_node_new(nc, GNUTLS_SAN_IPADDRESS, NULL, 32);
 				if (tmp == NULL) {
-					_gnutls_name_constraints_node_free(dest);
-					return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+					gnutls_assert();
+					ret = GNUTLS_E_MEMORY_ERROR;
+					goto cleanup;
+				}
+				ret = name_constraints_node_list_add(excluded, tmp);
+				if (ret < 0) {
+					gnutls_assert();
+					goto cleanup;
 				}
-				tmp->next = *_nc_excluded;
-				*_nc_excluded = tmp;
 				break;
 			case GNUTLS_SAN_DNSNAME:
 			case GNUTLS_SAN_RFC822NAME:
-				tmp = name_constraints_node_new(type, NULL, 0);
+				tmp = name_constraints_node_new(nc, type, NULL, 0);
 				if (tmp == NULL) {
-					_gnutls_name_constraints_node_free(dest);
-					return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+					gnutls_assert();
+					ret = GNUTLS_E_MEMORY_ERROR;
+					goto cleanup;
+				}
+				ret = name_constraints_node_list_add(excluded, tmp);
+				if (ret < 0) {
+					gnutls_assert();
+					goto cleanup;
 				}
-				tmp->next = *_nc_excluded;
-				*_nc_excluded = tmp;
 				break;
 			default: // do nothing, at least one node was already moved in phase 1
 				break;
 		}
 	}
-	return GNUTLS_E_SUCCESS;
-}
+	ret = GNUTLS_E_SUCCESS;
 
-static int _gnutls_name_constraints_append(name_constraints_node_st **_nc,
-										   name_constraints_node_st *_nc2)
-{
-	name_constraints_node_st *nc, *nc2;
-	struct name_constraints_node_st *tmp;
-
-	if (_nc2 == NULL)
-		return 0;
-
-	nc2 = _nc2;
-	while (nc2) {
-		nc = *_nc;
+cleanup:
+	gnutls_free(removed.data);
+	return ret;
+}
 
-		tmp = name_constraints_node_new(nc2->type, nc2->name.data, nc2->name.size);
-		if (tmp == NULL)
+static int name_constraints_node_list_concat(
+	gnutls_x509_name_constraints_t nc,
+	struct name_constraints_node_list_st *nodes,
+	const struct name_constraints_node_list_st *nodes2)
+{
+	for (size_t i = 0; i < nodes2->size; i++) {
+		const struct name_constraints_node_st *node = nodes2->data[i];
+		struct name_constraints_node_st *tmp;
+		int ret;
+
+		tmp = name_constraints_node_new(nc, node->type, node->name.data,
+						node->name.size);
+		if (tmp == NULL) {
 			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-
-		tmp->next = nc;
-		*_nc = tmp;
-
-		nc2 = nc2->next;
+		}
+		ret = name_constraints_node_list_add(nodes, tmp);
+		if (ret < 0) {
+			name_constraints_node_free(tmp);
+			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+		}
 	}
 
 	return 0;
@@ -487,6 +579,25 @@ int gnutls_x509_crt_get_name_constraints
 
 }
 
+void _gnutls_x509_name_constraints_clear(gnutls_x509_name_constraints_t nc)
+{
+	for (size_t i = 0; i < nc->nodes.size; i++) {
+		struct name_constraints_node_st *node = nc->nodes.data[i];
+		name_constraints_node_free(node);
+	}
+	gnutls_free(nc->nodes.data);
+	nc->nodes.capacity = 0;
+	nc->nodes.size = 0;
+
+	gnutls_free(nc->permitted.data);
+	nc->permitted.capacity = 0;
+	nc->permitted.size = 0;
+
+	gnutls_free(nc->excluded.data);
+	nc->excluded.capacity = 0;
+	nc->excluded.size = 0;
+}
+
 /**
  * gnutls_x509_name_constraints_deinit:
  * @nc: The nameconstraints
@@ -497,9 +608,7 @@ int gnutls_x509_crt_get_name_constraints
  **/
 void gnutls_x509_name_constraints_deinit(gnutls_x509_name_constraints_t nc)
 {
-	_gnutls_name_constraints_node_free(nc->permitted);
-	_gnutls_name_constraints_node_free(nc->excluded);
-
+	_gnutls_x509_name_constraints_clear(nc);
 	gnutls_free(nc);
 }
 
@@ -515,12 +624,15 @@ void gnutls_x509_name_constraints_deinit
  **/
 int gnutls_x509_name_constraints_init(gnutls_x509_name_constraints_t *nc)
 {
-	*nc = gnutls_calloc(1, sizeof(struct gnutls_name_constraints_st));
-	if (*nc == NULL) {
+	struct gnutls_name_constraints_st *tmp;
+
+	tmp = gnutls_calloc(1, sizeof(struct gnutls_name_constraints_st));
+	if (tmp == NULL) {
 		gnutls_assert();
 		return GNUTLS_E_MEMORY_ERROR;
 	}
 
+	*nc = tmp;
 	return 0;
 }
 
@@ -530,36 +642,25 @@ int name_constraints_add(gnutls_x509_nam
 			 const gnutls_datum_t * name,
 			 unsigned permitted)
 {
-	struct name_constraints_node_st * tmp, *prev = NULL;
+	struct name_constraints_node_st *tmp;
+	struct name_constraints_node_list_st *nodes;
 	int ret;
 
 	ret = validate_name_constraints_node(type, name);
 	if (ret < 0)
 		return gnutls_assert_val(ret);
 
-	if (permitted != 0)
-		prev = tmp = nc->permitted;
-	else
-		prev = tmp = nc->excluded;
-
-	while(tmp != NULL) {
-		tmp = tmp->next;
-		if (tmp != NULL)
-			prev = tmp;
-	}
+	nodes = permitted ? &nc->permitted : &nc->excluded;
 
-	tmp = name_constraints_node_new(type, name->data, name->size);
+	tmp = name_constraints_node_new(nc, type, name->data, name->size);
 	if (tmp == NULL)
 		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-	tmp->next = NULL;
 
-	if (prev == NULL) {
-		if (permitted != 0)
-			nc->permitted = tmp;
-		else
-			nc->excluded = tmp;
-	} else
-		prev->next = tmp;
+	ret = name_constraints_node_list_add(nodes, tmp);
+	if (ret < 0) {
+		name_constraints_node_free(tmp);
+		return gnutls_assert_val(ret);
+	}
 
 	return 0;
 }
@@ -585,17 +686,15 @@ int _gnutls_x509_name_constraints_merge(
 {
 	int ret;
 
-	ret =
-	    _gnutls_name_constraints_intersect(&nc->permitted,
-						   nc2->permitted, &nc->excluded);
+	ret = name_constraints_node_list_intersect(
+		nc, &nc->permitted, &nc2->permitted, &nc->excluded);
 	if (ret < 0) {
 		gnutls_assert();
 		return ret;
 	}
 
-	ret =
-	    _gnutls_name_constraints_append(&nc->excluded,
-					    nc2->excluded);
+	ret = name_constraints_node_list_concat(nc, &nc->excluded,
+						&nc2->excluded);
 	if (ret < 0) {
 		gnutls_assert();
 		return ret;
@@ -767,47 +866,51 @@ static unsigned email_matches(const gnut
  *
  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
  -*/
-static int
-name_constraints_intersect_nodes(name_constraints_node_st * nc1,
-				 name_constraints_node_st * nc2,
-				 name_constraints_node_st ** _intersection)
+static int name_constraints_intersect_nodes(
+	gnutls_x509_name_constraints_t nc,
+	const struct name_constraints_node_st *node1,
+	const struct name_constraints_node_st *node2,
+	struct name_constraints_node_st **_intersection)
 {
 	// presume empty intersection
-	name_constraints_node_st *intersection = NULL;
-	name_constraints_node_st *to_copy = NULL;
+	struct name_constraints_node_st *intersection = NULL;
+	const struct name_constraints_node_st *to_copy = NULL;
 	unsigned iplength = 0;
 	unsigned byte;
 
 	*_intersection = NULL;
 
-	if (nc1->type != nc2->type) {
+	if (node1->type != node2->type) {
 		return GNUTLS_E_SUCCESS;
 	}
-	switch (nc1->type) {
+	switch (node1->type) {
 	case GNUTLS_SAN_DNSNAME:
-		if (!dnsname_matches(&nc2->name, &nc1->name))
+		if (!dnsname_matches(&node2->name, &node1->name))
 			return GNUTLS_E_SUCCESS;
-		to_copy = nc2;
+		to_copy = node2;
 		break;
 	case GNUTLS_SAN_RFC822NAME:
-		if (!email_matches(&nc2->name, &nc1->name))
+		if (!email_matches(&node2->name, &node1->name))
 			return GNUTLS_E_SUCCESS;
-		to_copy = nc2;
+		to_copy = node2;
 		break;
 	case GNUTLS_SAN_IPADDRESS:
-		if (nc1->name.size != nc2->name.size)
+		if (node1->name.size != node2->name.size)
 			return GNUTLS_E_SUCCESS;
-		iplength = nc1->name.size/2;
+		iplength = node1->name.size / 2;
 		for (byte = 0; byte < iplength; byte++) {
-			if (((nc1->name.data[byte]^nc2->name.data[byte]) // XOR of addresses
-				 & nc1->name.data[byte+iplength]  // AND mask from nc1
-				 & nc2->name.data[byte+iplength]) // AND mask from nc2
-				 != 0) {
+			if (((node1->name.data[byte] ^
+			      node2->name.data[byte]) // XOR of addresses
+			     & node1->name.data[byte +
+						iplength] // AND mask from nc1
+			     & node2->name.data[byte +
+						iplength]) // AND mask from nc2
+			    != 0) {
 				// CIDRS do not intersect
 				return GNUTLS_E_SUCCESS;
 			}
 		}
-		to_copy = nc2;
+		to_copy = node2;
 		break;
 	default:
 		// for other types, we don't know how to do the intersection, assume empty
@@ -816,7 +919,9 @@ name_constraints_intersect_nodes(name_co
 
 	// copy existing node if applicable
 	if (to_copy != NULL) {
-		*_intersection = name_constraints_node_new(to_copy->type, to_copy->name.data, to_copy->name.size);
+		*_intersection = name_constraints_node_new(nc, to_copy->type,
+							   to_copy->name.data,
+							   to_copy->name.size);
 		if (*_intersection == NULL)
 			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
 		intersection = *_intersection;
@@ -825,11 +930,15 @@ name_constraints_intersect_nodes(name_co
 
 		if (intersection->type == GNUTLS_SAN_IPADDRESS) {
 			// make sure both IP addresses are correctly masked
-			_gnutls_mask_ip(intersection->name.data, intersection->name.data+iplength, iplength);
-			_gnutls_mask_ip(nc1->name.data, nc1->name.data+iplength, iplength);
+			_gnutls_mask_ip(intersection->name.data,
+					intersection->name.data + iplength,
+					iplength);
+			_gnutls_mask_ip(node1->name.data,
+					node1->name.data + iplength, iplength);
 			// update intersection, if necessary (we already know one is subset of other)
 			for (byte = 0; byte < 2 * iplength; byte++) {
-				intersection->name.data[byte] |= nc1->name.data[byte];
+				intersection->name.data[byte] |=
+					node1->name.data[byte];
 			}
 		}
 	}
@@ -1124,10 +1233,20 @@ int ret;
 unsigned idx, t, san_type;
 gnutls_datum_t n;
 unsigned found_one;
+size_t checks;
 
-	if (is_nc_empty(nc, type) != 0)
+	if (_gnutls_x509_name_constraints_is_empty(nc, type) != 0)
 		return 1; /* shortcut; no constraints to check */
 
+	if (INT_ADD_OVERFLOW(nc->permitted.size, nc->excluded.size))
+		return gnutls_assert_val(0);
+	checks = nc->permitted.size + nc->excluded.size;
+	if (INT_MULTIPLY_OVERFLOW(checks, cert->san->size))
+		return gnutls_assert_val(0);
+	checks *= cert->san->size;
+	if (checks > MAX_NC_CHECKS)
+		return gnutls_assert_val(0);
+
 	if (type == GNUTLS_SAN_RFC822NAME) {
 		idx = found_one = 0;
 		do {
@@ -1316,21 +1435,13 @@ int gnutls_x509_name_constraints_get_per
 					       unsigned idx,
 					       unsigned *type, gnutls_datum_t * name)
 {
-	unsigned int i;
-	struct name_constraints_node_st * tmp = nc->permitted;
-
-	for (i = 0; i < idx; i++) {
-		if (tmp == NULL)
-			return
-			    gnutls_assert_val
-			    (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+	const struct name_constraints_node_st *tmp;
 
-		tmp = tmp->next;
-	}
-
-	if (tmp == NULL)
+	if (idx >= nc->permitted.size)
 		return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
 
+	tmp = nc->permitted.data[idx];
+
 	*type = tmp->type;
 	*name = tmp->name;
 
@@ -1360,21 +1471,13 @@ int gnutls_x509_name_constraints_get_exc
 					      unsigned idx,
 					      unsigned *type, gnutls_datum_t * name)
 {
-	unsigned int i;
-	struct name_constraints_node_st * tmp = nc->excluded;
-
-	for (i = 0; i < idx; i++) {
-		if (tmp == NULL)
-			return
-			    gnutls_assert_val
-			    (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
-
-		tmp = tmp->next;
-	}
+	const struct name_constraints_node_st *tmp;
 
-	if (tmp == NULL)
+	if (idx >= nc->excluded.size)
 		return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
 
+	tmp = nc->excluded.data[idx];
+
 	*type = tmp->type;
 	*name = tmp->name;
 
Index: gnutls-3.6.7/lib/x509/x509_ext.c
===================================================================
--- gnutls-3.6.7.orig/lib/x509/x509_ext.c
+++ gnutls-3.6.7/lib/x509/x509_ext.c
@@ -33,10 +33,6 @@
 #include <gnutls/x509-ext.h>
 
 #define MAX_ENTRIES 64
-struct gnutls_subject_alt_names_st {
-	struct name_st *names;
-	unsigned int size;
-};
 
 /**
  * gnutls_subject_alt_names_init:
@@ -387,24 +383,15 @@ int gnutls_x509_ext_import_name_constrai
 	}
 
 	if (flags & GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND &&
-	    (nc->permitted != NULL || nc->excluded != NULL)) {
-		ret = gnutls_x509_name_constraints_init (&nc2);
+	    !_gnutls_x509_name_constraints_is_empty(nc, 0)) {
+		ret = gnutls_x509_name_constraints_init(&nc2);
 		if (ret < 0) {
 			gnutls_assert();
 			goto cleanup;
 		}
 
-		ret =
-		    _gnutls_extract_name_constraints(c2, "permittedSubtrees",
-						     &nc2->permitted);
-		if (ret < 0) {
-			gnutls_assert();
-			goto cleanup;
-		}
-
-		ret =
-		    _gnutls_extract_name_constraints(c2, "excludedSubtrees",
-						     &nc2->excluded);
+		ret = _gnutls_x509_name_constraints_extract(
+			c2, "permittedSubtrees", "excludedSubtrees", nc2);
 		if (ret < 0) {
 			gnutls_assert();
 			goto cleanup;
@@ -416,20 +403,10 @@ int gnutls_x509_ext_import_name_constrai
 			goto cleanup;
 		}
 	} else {
-		_gnutls_name_constraints_node_free(nc->permitted);
-		_gnutls_name_constraints_node_free(nc->excluded);
 
-		ret =
-		    _gnutls_extract_name_constraints(c2, "permittedSubtrees",
-						     &nc->permitted);
-		if (ret < 0) {
-			gnutls_assert();
-			goto cleanup;
-		}
-
-		ret =
-		    _gnutls_extract_name_constraints(c2, "excludedSubtrees",
-						     &nc->excluded);
+		_gnutls_x509_name_constraints_clear(nc);
+		ret = _gnutls_x509_name_constraints_extract(
+			c2, "permittedSubtrees", "excludedSubtrees", nc);
 		if (ret < 0) {
 			gnutls_assert();
 			goto cleanup;
@@ -464,10 +441,11 @@ int gnutls_x509_ext_export_name_constrai
 {
 	int ret, result;
 	uint8_t null = 0;
-	ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
-	struct name_constraints_node_st *tmp;
+	asn1_node c2 = ASN1_TYPE_EMPTY;
+	unsigned rtype;
+	gnutls_datum_t rname;
 
-	if (nc->permitted == NULL && nc->excluded == NULL)
+	if (_gnutls_x509_name_constraints_is_empty(nc, 0))
 		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
 
 	result = asn1_create_element
@@ -477,13 +455,22 @@ int gnutls_x509_ext_export_name_constrai
 		return _gnutls_asn2err(result);
 	}
 
-	if (nc->permitted == NULL) {
+	ret = gnutls_x509_name_constraints_get_permitted(nc, 0, &rtype, &rname);
+	if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
 		(void)asn1_write_value(c2, "permittedSubtrees", NULL, 0);
 	} else {
-		tmp = nc->permitted;
-		do {
-			result =
-			    asn1_write_value(c2, "permittedSubtrees", "NEW", 1);
+		for (unsigned i = 0;; i++) {
+			ret = gnutls_x509_name_constraints_get_permitted(
+				nc, i, &rtype, &rname);
+			if (ret < 0) {
+				if (ret ==
+				    GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+					break;
+				gnutls_assert();
+				goto cleanup;
+			}
+			result = asn1_write_value(c2, "permittedSubtrees",
+						  "NEW", 1);
 			if (result != ASN1_SUCCESS) {
 				gnutls_assert();
 				ret = _gnutls_asn2err(result);
@@ -510,27 +497,32 @@ int gnutls_x509_ext_export_name_constrai
 				goto cleanup;
 			}
 
-			ret =
-			    _gnutls_write_general_name(c2,
-						       "permittedSubtrees.?LAST.base",
-						       tmp->type,
-						       tmp->name.data,
-						       tmp->name.size);
+			ret = _gnutls_write_general_name(
+				c2, "permittedSubtrees.?LAST.base", rtype,
+				rname.data, rname.size);
 			if (ret < 0) {
 				gnutls_assert();
 				goto cleanup;
 			}
-			tmp = tmp->next;
-		} while (tmp != NULL);
+		}
 	}
 
-	if (nc->excluded == NULL) {
+	ret = gnutls_x509_name_constraints_get_excluded(nc, 0, &rtype, &rname);
+	if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
 		(void)asn1_write_value(c2, "excludedSubtrees", NULL, 0);
 	} else {
-		tmp = nc->excluded;
-		do {
-			result =
-			    asn1_write_value(c2, "excludedSubtrees", "NEW", 1);
+		for (unsigned i = 0;; i++) {
+			ret = gnutls_x509_name_constraints_get_excluded(
+				nc, i, &rtype, &rname);
+			if (ret < 0) {
+				if (ret ==
+				    GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+					break;
+				gnutls_assert();
+				goto cleanup;
+			}
+			result = asn1_write_value(c2, "excludedSubtrees", "NEW",
+						  1);
 			if (result != ASN1_SUCCESS) {
 				gnutls_assert();
 				ret = _gnutls_asn2err(result);
@@ -557,19 +549,14 @@ int gnutls_x509_ext_export_name_constrai
 				goto cleanup;
 			}
 
-			ret =
-			    _gnutls_write_general_name(c2,
-						       "excludedSubtrees.?LAST.base",
-						       tmp->type,
-						       tmp->name.data,
-						       tmp->name.size);
+			ret = _gnutls_write_general_name(
+				c2, "excludedSubtrees.?LAST.base", rtype,
+				rname.data, rname.size);
 			if (ret < 0) {
 				gnutls_assert();
 				goto cleanup;
 			}
-			tmp = tmp->next;
-		} while (tmp != NULL);
-
+		}
 	}
 
 	ret = _gnutls_x509_der_encode(c2, "", ext, 0);
Index: gnutls-3.6.7/lib/x509/x509_ext_int.h
===================================================================
--- gnutls-3.6.7.orig/lib/x509/x509_ext_int.h
+++ gnutls-3.6.7/lib/x509/x509_ext_int.h
@@ -29,6 +29,11 @@ struct name_st {
 	gnutls_datum_t othername_oid;
 };
 
+struct gnutls_subject_alt_names_st {
+	struct name_st *names;
+	unsigned int size;
+};
+
 int _gnutls_alt_name_process(gnutls_datum_t *out, unsigned type, const gnutls_datum_t *san, unsigned raw);
 
 #endif
Index: gnutls-3.6.7/lib/x509/x509_int.h
===================================================================
--- gnutls-3.6.7.orig/lib/x509/x509_int.h
+++ gnutls-3.6.7/lib/x509/x509_int.h
@@ -520,20 +520,13 @@ _gnutls_x509_crt_check_revocation(gnutls
 				  int crl_list_length,
 				  gnutls_verify_output_function func);
 
-typedef struct gnutls_name_constraints_st {
-	struct name_constraints_node_st * permitted;
-	struct name_constraints_node_st * excluded;
-} gnutls_name_constraints_st;
-
-typedef struct name_constraints_node_st {
-	unsigned type;
-	gnutls_datum_t name;
-	struct name_constraints_node_st *next;
-} name_constraints_node_st;
-
-int _gnutls_extract_name_constraints(ASN1_TYPE c2, const char *vstr,
-				    name_constraints_node_st ** _nc);
-void _gnutls_name_constraints_node_free (name_constraints_node_st *node);
+bool _gnutls_x509_name_constraints_is_empty(gnutls_x509_name_constraints_t nc,
+					    unsigned type);
+int _gnutls_x509_name_constraints_extract(asn1_node c2,
+					  const char *permitted_name,
+					  const char *excluded_name,
+					  gnutls_x509_name_constraints_t nc);
+void _gnutls_x509_name_constraints_clear(gnutls_x509_name_constraints_t nc);
 int _gnutls_x509_name_constraints_merge(gnutls_x509_name_constraints_t nc,
 					gnutls_x509_name_constraints_t nc2);
 
openSUSE Build Service is sponsored by