gnutls/gnutls-3.8.9-CVE-2024-12243.patch
Alexander Sosedkin b38757470b Backport the fix for CVE-2024-12243
Resolves: RHEL-78580
2025-02-12 16:52:04 +01:00

1149 lines
34 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>
---
lib/datum.c | 7 +-
lib/x509/name_constraints.c | 595 +++++++++++++++++++++---------------
lib/x509/x509_ext.c | 80 +++--
lib/x509/x509_ext_int.h | 5 +
lib/x509/x509_int.h | 21 +-
5 files changed, 399 insertions(+), 309 deletions(-)
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 _gnutls_set_datum(gnutls_datum_t *dat, const void *data, size_t data_size)
@@ -60,7 +61,11 @@ int _gnutls_set_strdatum(gnutls_datum_t *dat, const void *data,
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_OK(data_size, 1, &capacity))
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ 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,51 +33,98 @@
#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);
-// 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);
+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_OK(new_capacity, 2, &new_capacity) ||
+ !INT_ADD_OK(new_capacity, 1, &new_capacity))
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ new_data = _gnutls_reallocarray(
+ 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(
+ 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;
}
/*-
@@ -115,21 +162,16 @@ static int validate_name_constraints_node(gnutls_x509_subject_alt_name_t type,
return GNUTLS_E_SUCCESS;
}
-int _gnutls_extract_name_constraints(asn1_node 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);
@@ -172,25 +214,19 @@ int _gnutls_extract_name_constraints(asn1_node 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;
}
assert(ret < 0);
@@ -205,84 +241,104 @@ cleanup:
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)
@@ -291,61 +347,73 @@ _gnutls_name_constraints_intersect(name_constraints_node_st **_nc,
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 !=
@@ -360,32 +428,34 @@ _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);
+ 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
@@ -400,63 +470,77 @@ _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;
@@ -524,6 +608,25 @@ cleanup:
return ret;
}
+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
@@ -534,9 +637,7 @@ cleanup:
**/
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);
}
@@ -552,12 +653,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;
}
@@ -565,36 +669,25 @@ static int name_constraints_add(gnutls_x509_name_constraints_t nc,
gnutls_x509_subject_alt_name_t type,
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;
}
@@ -620,14 +713,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;
@@ -804,50 +898,51 @@ static unsigned email_matches(const gnutls_datum_t *name,
*
* 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
+ 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
@@ -856,8 +951,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;
@@ -869,12 +965,12 @@ name_constraints_intersect_nodes(name_constraints_node_st *nc1,
_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(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];
+ node1->name.data[byte];
}
}
}
@@ -1177,10 +1273,17 @@ gnutls_x509_name_constraints_check_crt(gnutls_x509_name_constraints_t nc,
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_OK(nc->permitted.size, nc->excluded.size, &checks) ||
+ !INT_MULTIPLY_OK(checks, cert->san->size, &checks) ||
+ checks > MAX_NC_CHECKS) {
+ return gnutls_assert_val(0);
+ }
+
if (type == GNUTLS_SAN_RFC822NAME) {
found_one = 0;
for (idx = 0;; idx++) {
@@ -1378,20 +1481,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;
@@ -1421,20 +1517,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;
-
- 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->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
@@ -34,10 +34,6 @@
#include "intprops.h"
#define MAX_ENTRIES 64
-struct gnutls_subject_alt_names_st {
- struct name_st *names;
- unsigned int size;
-};
/**
* gnutls_subject_alt_names_init:
@@ -389,22 +385,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)) {
+ !_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,18 +405,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;
@@ -463,9 +444,10 @@ int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc,
int ret, result;
uint8_t null = 0;
asn1_node c2 = NULL;
- 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(_gnutls_get_pkix(),
@@ -475,11 +457,20 @@ 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 {
+ 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) {
@@ -506,21 +497,29 @@ int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc,
}
ret = _gnutls_write_general_name(
- c2, "permittedSubtrees.?LAST.base", tmp->type,
- tmp->name.data, tmp->name.size);
+ 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);
if (result != ASN1_SUCCESS) {
@@ -546,14 +545,13 @@ int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc,
}
ret = _gnutls_write_general_name(
- c2, "excludedSubtrees.?LAST.base", tmp->type,
- tmp->name.data, tmp->name.size);
+ 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,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);
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
@@ -503,20 +503,13 @@ int _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_node 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);
--
GitLab