gnutls/gnutls-3.8.9-CVE-2024-12243.patch
Alexander Sosedkin a075413675 Backport the fix for CVE-2024-12243
Resolves: RHEL-85573
2025-04-01 11:03:16 +02:00

1169 lines
35 KiB
Diff

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>
diff --git a/lib/datum.c b/lib/datum.c
index 66e016965d..5577c2b4ab 100644
--- a/lib/datum.c
+++ b/lib/datum.c
@@ -29,6 +29,7 @@
#include <num.h>
#include <datum.h>
#include "errors.h"
+#include "intprops.h"
/* On error, @dat is not changed. */
int
@@ -61,7 +62,12 @@ _gnutls_set_strdatum(gnutls_datum_t * dat, const void *data, size_t data_size)
if (data == NULL)
return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
- unsigned char *m = gnutls_malloc(data_size + 1);
+ 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;
diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c
index 8327a9d94e..3c6e306303 100644
--- a/lib/x509/name_constraints.c
+++ b/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_node(gnutls_x509_subject_alt_name_t type,
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;
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;
for (indx=1;;indx++) {
snprintf(tmpstr, sizeof(tmpstr), "%s.?%u.base", vstr, indx);
@@ -144,25 +192,19 @@ int _gnutls_extract_name_constraints(ASN1_TYPE c2, const char *vstr,
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;
}
if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
@@ -176,84 +218,103 @@ int _gnutls_extract_name_constraints(ASN1_TYPE c2, const char *vstr,
return ret;
}
+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;
+}
+
/*-
- * _gnutls_name_constraints_node_free:
+ * name_constraints_node_free:
* @node: name constraints 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)
+static 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);
}
}
/*-
* 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 in @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)
@@ -261,61 +322,73 @@ int _gnutls_name_constraints_intersect(name_constraints_node_st ** _nc,
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
@@ -326,31 +399,34 @@ int _gnutls_name_constraints_intersect(name_constraints_node_st ** _nc,
}
// 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
@@ -363,60 +439,75 @@ int _gnutls_name_constraints_intersect(name_constraints_node_st ** _nc,
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;
+
+cleanup:
+ gnutls_free(removed.data);
+ return ret;
}
-static int _gnutls_name_constraints_append(name_constraints_node_st **_nc,
- name_constraints_node_st *_nc2)
+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)
{
- name_constraints_node_st *nc, *nc2;
- struct name_constraints_node_st *tmp;
-
- if (_nc2 == NULL)
- return 0;
-
- nc2 = _nc2;
- while (nc2) {
- nc = *_nc;
-
- tmp = name_constraints_node_new(nc2->type, nc2->name.data, nc2->name.size);
- if (tmp == NULL)
+ 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;
@@ -486,6 +577,25 @@ int gnutls_x509_crt_get_name_constraints(gnutls_x509_crt_t crt,
}
+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
@@ -496,9 +606,7 @@ int gnutls_x509_crt_get_name_constraints(gnutls_x509_crt_t crt,
**/
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);
}
@@ -514,12 +622,15 @@ void gnutls_x509_name_constraints_deinit(gnutls_x509_name_constraints_t nc)
**/
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;
}
@@ -529,36 +640,25 @@ int name_constraints_add(gnutls_x509_name_constraints_t nc,
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;
+ nodes = permitted ? &nc->permitted : &nc->excluded;
- while(tmp != NULL) {
- tmp = tmp->next;
- if (tmp != NULL)
- prev = tmp;
- }
-
- 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;
}
@@ -584,17 +684,15 @@ int _gnutls_x509_name_constraints_merge(gnutls_x509_name_constraints_t nc,
{
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;
@@ -766,47 +864,51 @@ static unsigned email_matches(const gnutls_datum_t *name, const gnutls_datum_t *
*
* 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
@@ -815,7 +917,9 @@ name_constraints_intersect_nodes(name_constraints_node_st * nc1,
// 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;
@@ -824,11 +928,15 @@ name_constraints_intersect_nodes(name_constraints_node_st * nc1,
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];
}
}
}
@@ -1122,10 +1230,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) {
found_one = 0;
for (idx=0;;idx++) {
@@ -1314,21 +1432,13 @@ int gnutls_x509_name_constraints_get_permitted(gnutls_x509_name_constraints_t nc
unsigned idx,
unsigned *type, gnutls_datum_t * name)
{
- unsigned int i;
- struct name_constraints_node_st * tmp = nc->permitted;
+ const struct name_constraints_node_st *tmp;
- for (i = 0; i < idx; i++) {
- if (tmp == NULL)
- return
- gnutls_assert_val
- (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
-
- 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;
@@ -1358,21 +1468,13 @@ int gnutls_x509_name_constraints_get_excluded(gnutls_x509_name_constraints_t nc,
unsigned idx,
unsigned *type, gnutls_datum_t * name)
{
- unsigned int i;
- struct name_constraints_node_st * tmp = nc->excluded;
+ const struct name_constraints_node_st *tmp;
- for (i = 0; i < idx; i++) {
- if (tmp == NULL)
- return
- gnutls_assert_val
- (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
-
- tmp = tmp->next;
- }
-
- 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;
diff --git a/lib/x509/x509_ext.c b/lib/x509/x509_ext.c
index ae7216f23f..1714578de6 100644
--- a/lib/x509/x509_ext.c
+++ b/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:
@@ -384,24 +380,15 @@ int gnutls_x509_ext_import_name_constraints(const gnutls_datum_t * ext,
}
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;
@@ -413,20 +400,10 @@ int gnutls_x509_ext_import_name_constraints(const gnutls_datum_t * ext,
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;
- }
+ _gnutls_x509_name_constraints_clear(nc);
-
- ret =
- _gnutls_extract_name_constraints(c2, "excludedSubtrees",
- &nc->excluded);
+ ret = _gnutls_x509_name_constraints_extract(
+ c2, "permittedSubtrees", "excludedSubtrees", nc);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -461,10 +438,11 @@ int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc,
{
int ret, result;
uint8_t null = 0;
- ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
+ asn1_node c2 = ASN1_TYPE_EMPTY;
- struct name_constraints_node_st *tmp;
+ 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
@@ -474,13 +452,22 @@ int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc,
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);
@@ -507,27 +494,32 @@ int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc,
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 {
+ 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);
+ result = asn1_write_value(c2, "excludedSubtrees", "NEW",
+ 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
@@ -554,19 +546,14 @@ int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc,
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);
diff --git a/lib/x509/x509_ext_int.h b/lib/x509/x509_ext_int.h
index 558d619565..b37d749976 100644
--- a/lib/x509/x509_ext_int.h
+++ b/lib/x509/x509_ext_int.h
@@ -29,5 +29,10 @@ 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);
diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h
index 693a4dd924..30f8051a70 100644
--- a/lib/x509/x509_int.h
+++ b/lib/x509/x509_int.h
@@ -522,20 +522,13 @@ _gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert,
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);