From 4760bc63531e3f5039e70ede91a20e1194410892 Mon Sep 17 00:00:00 2001 From: Daiki Ueno 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 --- 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 #include "x509_b64.h" #include "x509_int.h" +#include "x509_ext_int.h" #include #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