File gnutls-CVE-2024-12243.patch of Package gnutls.37572
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(-)
--- a/lib/datum.c
+++ b/lib/datum.c
@@ -27,10 +27,11 @@
#include "gnutls_int.h"
#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)
{
@@ -59,11 +60,15 @@ int
_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_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;
dat->data = m;
dat->size = data_size;
--- a/lib/x509/name_constraints.c
+++ b/lib/x509/name_constraints.c
@@ -31,53 +31,106 @@
#include <common.h>
#include <x509.h>
#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_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(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 (type == 0)
+ return false;
+
+ for (size_t i = 0; i < nc->permitted.size; i++) {
+ if (nc->permitted.data[i]->type == type)
+ return false;
- if (nc->permitted == NULL && nc->excluded == NULL)
- return 1;
- t = nc->permitted;
- while (t != NULL) {
- if (t->type == type)
- return 0;
- t = t->next;
}
- 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;
}
/*-
* validate_name_constraints_node:
* @type: type of name constraints
@@ -110,25 +163,19 @@ static int validate_name_constraints_nod
}
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);
ret =
@@ -165,29 +212,23 @@ int _gnutls_extract_name_constraints(asn
if (ret < 0) {
gnutls_assert();
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);
if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
gnutls_assert();
@@ -198,251 +239,311 @@ int _gnutls_extract_name_constraints(asn
cleanup:
gnutls_free(tmp.data);
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)
* 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));
+ 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
+ if (tmp !=
+ NULL) { // intersection for this type is not empty
// check bounds
- if (tmp->type > GNUTLS_SAN_MAX || tmp->type == 0) {
+ if (tmp->type > GNUTLS_SAN_MAX ||
+ tmp->type == 0) {
gnutls_free(tmp);
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ return gnutls_assert_val(
+ GNUTLS_E_INTERNAL_ERROR);
}
// 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;
+ types_with_empty_intersection[tmp->type - 1] =
+ 0;
+ // 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.
+ // 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
* excluded constraint with universal wildcard
* (since the intersection of permitted is now empty). */
for (type = 1; type <= GNUTLS_SAN_MAX; type++) {
- if (types_with_empty_intersection[type-1] == 0)
+ if (types_with_empty_intersection[type - 1] == 0)
continue;
- _gnutls_hard_log("Adding universal excluded name constraint for type %d.\n", type);
+ _gnutls_hard_log(
+ "Adding universal excluded name constraint for type %d.\n",
+ type);
switch (type) {
- case GNUTLS_SAN_IPADDRESS:
- // add universal restricted range for IPv4
- tmp = name_constraints_node_new(GNUTLS_SAN_IPADDRESS, NULL, 8);
- if (tmp == NULL) {
- _gnutls_name_constraints_node_free(dest);
- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
- }
- tmp->next = *_nc_excluded;
- *_nc_excluded = tmp;
- // add universal restricted range for IPv6
- tmp = name_constraints_node_new(GNUTLS_SAN_IPADDRESS, NULL, 32);
- if (tmp == NULL) {
- _gnutls_name_constraints_node_free(dest);
- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
- }
- tmp->next = *_nc_excluded;
- *_nc_excluded = tmp;
- break;
- case GNUTLS_SAN_DNSNAME:
- case GNUTLS_SAN_RFC822NAME:
- tmp = name_constraints_node_new(type, NULL, 0);
- if (tmp == NULL) {
- _gnutls_name_constraints_node_free(dest);
- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
- }
- tmp->next = *_nc_excluded;
- *_nc_excluded = tmp;
- break;
- default: // do nothing, at least one node was already moved in phase 1
- break;
+ case GNUTLS_SAN_IPADDRESS:
+ // add universal restricted range for IPv4
+ tmp = name_constraints_node_new(
+ nc, GNUTLS_SAN_IPADDRESS, NULL, 8);
+ if (tmp == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+ ret = name_constraints_node_list_add(excluded, tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ // add universal restricted range for IPv6
+ tmp = name_constraints_node_new(
+ nc, GNUTLS_SAN_IPADDRESS, NULL, 32);
+ if (tmp == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+ ret = name_constraints_node_list_add(excluded, tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ break;
+ case GNUTLS_SAN_DNSNAME:
+ case GNUTLS_SAN_RFC822NAME:
+ tmp = name_constraints_node_new(nc, type, NULL, 0);
+ if (tmp == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+ ret = name_constraints_node_list_add(excluded, tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ break;
+ default: // do nothing, at least one node was already moved in phase 1
+ break;
}
}
- return 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;
+ ret = GNUTLS_E_SUCCESS;
- 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;
}
@@ -508,23 +609,40 @@ int gnutls_x509_crt_get_name_constraints
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
*
* This function will deinitialize a name constraints type.
*
* Since: 3.3.0
**/
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);
}
/**
* gnutls_x509_name_constraints_init:
@@ -536,55 +654,48 @@ void gnutls_x509_name_constraints_deinit
*
* Since: 3.3.0
**/
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;
}
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;
}
/*-
@@ -605,22 +716,19 @@ int name_constraints_add(gnutls_x509_nam
-*/
int _gnutls_x509_name_constraints_merge(gnutls_x509_name_constraints_t nc,
gnutls_x509_name_constraints_t nc2)
{
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;
}
@@ -788,73 +896,83 @@ static unsigned email_matches(const gnut
* Inspect 2 name constraints nodes (of possibly different types) and allocate
* a new node with intersection of given constraints.
*
* 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
return GNUTLS_E_SUCCESS;
}
// 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;
assert(intersection->name.data != NULL);
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];
}
}
}
return GNUTLS_E_SUCCESS;
@@ -1144,14 +1262,21 @@ char name[MAX_CN];
size_t name_size;
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_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++) {
name_size = sizeof(name);
ret = gnutls_x509_crt_get_subject_alt_name2(cert,
@@ -1333,28 +1458,20 @@ unsigned found_one;
* if the extension is not present, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_name_constraints_get_permitted(gnutls_x509_name_constraints_t nc,
- unsigned idx,
- unsigned *type, gnutls_datum_t * name)
+ 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);
-
- tmp = tmp->next;
- }
+ const struct name_constraints_node_st *tmp;
- 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;
return 0;
}
@@ -1380,25 +1497,17 @@ int gnutls_x509_name_constraints_get_per
**/
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;
return 0;
}
--- a/lib/x509/x509_ext.c
+++ b/lib/x509/x509_ext.c
@@ -32,14 +32,10 @@
#include "virt-san.h"
#include <gnutls/x509-ext.h>
#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:
* @sans: The alternative names
*
@@ -387,28 +383,19 @@ int gnutls_x509_ext_import_name_constrai
ret = _gnutls_asn2err(result);
goto cleanup;
}
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,24 +403,14 @@ int gnutls_x509_ext_import_name_constrai
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
} else {
- _gnutls_name_constraints_node_free(nc->permitted);
- _gnutls_name_constraints_node_free(nc->excluded);
+ _gnutls_x509_name_constraints_clear(nc);
- 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);
+ ret = _gnutls_x509_name_constraints_extract(
+ c2, "permittedSubtrees", "excludedSubtrees", nc);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
}
@@ -465,27 +442,36 @@ int gnutls_x509_ext_export_name_constrai
gnutls_datum_t * ext)
{
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(), "PKIX1.NameConstraints", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
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) {
gnutls_assert();
ret = _gnutls_asn2err(result);
@@ -511,28 +497,34 @@ int gnutls_x509_ext_export_name_constrai
ret = _gnutls_asn2err(result);
goto cleanup;
}
ret =
- _gnutls_write_general_name(c2,
- "permittedSubtrees.?LAST.base",
- tmp->type,
- tmp->name.data,
- tmp->name.size);
+ _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);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
@@ -558,21 +550,18 @@ int gnutls_x509_ext_export_name_constrai
ret = _gnutls_asn2err(result);
goto cleanup;
}
ret =
- _gnutls_write_general_name(c2,
- "excludedSubtrees.?LAST.base",
- tmp->type,
- tmp->name.data,
- tmp->name.size);
+ _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);
if (ret < 0) {
--- a/lib/x509/x509_ext_int.h
+++ b/lib/x509/x509_ext_int.h
@@ -27,8 +27,13 @@ struct name_st {
unsigned int type;
gnutls_datum_t san;
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 /* GNUTLS_LIB_X509_X509_EXT_INT_H */
--- a/lib/x509/x509_int.h
+++ b/lib/x509/x509_int.h
@@ -527,24 +527,17 @@ int
_gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert,
const gnutls_x509_crl_t * crl_list,
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);
void _gnutls_x509_policies_erase(gnutls_x509_policies_t policies, unsigned int seq);