]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
x509/name_constraints: implement name_constraints_node_list_union
authorAlexander Sosedkin <asosedkin@redhat.com>
Wed, 4 Feb 2026 12:30:08 +0000 (13:30 +0100)
committerAlexander Sosedkin <asosedkin@redhat.com>
Mon, 9 Feb 2026 11:59:26 +0000 (12:59 +0100)
Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
lib/x509/name_constraints.c

index 41f30d13b9022c2f74b715b2953608588589207f..de20dd8ef4159dbf0da6758e5489f969be0fad6b 100644 (file)
@@ -41,6 +41,7 @@
 #include "intprops.h"
 #include "minmax.h"
 
+#include <assert.h>
 #include <string.h>
 
 #define MAX_NC_CHECKS (1 << 20)
@@ -870,22 +871,95 @@ cleanup:
        return ret;
 }
 
-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)
+static int
+name_constraints_node_list_union(gnutls_x509_name_constraints_t nc,
+                                struct name_constraints_node_list_st *nodes,
+                                struct name_constraints_node_list_st *nodes2)
 {
        int ret;
+       size_t i = 0, j = 0;
+       struct name_constraints_node_st *nc1;
+       const struct name_constraints_node_st *nc2;
+       enum name_constraint_relation rel;
+       struct name_constraints_node_list_st result = { 0 };
+
+       if (nodes2->size == 0) /* nothing to do */
+               return GNUTLS_E_SUCCESS;
+
+       ret = ensure_sorted(nodes);
+       if (ret < 0) {
+               gnutls_assert();
+               goto cleanup;
+       }
+       ret = ensure_sorted(nodes2);
+       if (ret < 0) {
+               gnutls_assert();
+               goto cleanup;
+       }
+
+       /* traverse both lists in a single pass and merge them w/o duplicates */
+       while (i < nodes->size || j < nodes2->size) {
+               nc1 = (i < nodes->size) ? nodes->sorted_view[i] : NULL;
+               nc2 = (j < nodes2->size) ? nodes2->sorted_view[j] : NULL;
 
-       for (size_t i = 0; i < nodes2->size; i++) {
-               ret = name_constraints_node_add_copy(nc, nodes,
-                                                    nodes2->data[i]);
+               rel = compare_name_constraint_nodes(nc1, nc2);
+               switch (rel) {
+               case NC_SORTS_BEFORE:
+                       assert(nc1 != NULL); /* comparator-guaranteed */
+                       ret = name_constraints_node_list_add(&result, nc1);
+                       i++;
+                       break;
+               case NC_SORTS_AFTER:
+                       assert(nc2 != NULL); /* comparator-guaranteed */
+                       ret = name_constraints_node_add_copy(nc, &result, nc2);
+                       j++;
+                       break;
+               case NC_INCLUDES: /* nc1 is broader, shallow-copy it */
+                       assert(nc1 != NULL && nc2 != NULL); /* comparator */
+                       ret = name_constraints_node_list_add(&result, nc1);
+                       i++;
+                       j++;
+                       break;
+               case NC_INCLUDED_BY: /* nc2 is broader, deep-copy it */
+                       assert(nc1 != NULL && nc2 != NULL); /* comparator */
+                       ret = name_constraints_node_add_copy(nc, &result, nc2);
+                       i++;
+                       j++;
+                       break;
+               case NC_EQUAL:
+                       assert(nc1 != NULL && nc2 != NULL); /* loop condition */
+                       ret = name_constraints_node_list_add(&result, nc1);
+                       i++;
+                       j++;
+                       break;
+               }
                if (ret < 0) {
-                       return gnutls_assert_val(ret);
+                       gnutls_assert();
+                       goto cleanup;
                }
        }
 
-       return 0;
+       gnutls_free(nodes->data);
+       gnutls_free(nodes->sorted_view);
+       nodes->data = result.data;
+       nodes->sorted_view = NULL;
+       nodes->size = result.size;
+       nodes->capacity = result.capacity;
+       nodes->dirty = true;
+       /* since we know it's sorted, populate sorted_view almost for free */
+       nodes->sorted_view = gnutls_calloc(
+               nodes->size, sizeof(struct name_constraints_node_st *));
+       if (!nodes->sorted_view)
+               return GNUTLS_E_SUCCESS; /* we tried, no harm done */
+       memcpy(nodes->sorted_view, nodes->data,
+              nodes->size * sizeof(struct name_constraints_node_st *));
+       nodes->dirty = false;
+
+       result.data = NULL;
+       return GNUTLS_E_SUCCESS;
+cleanup:
+       name_constraints_node_list_clear(&result);
+       return gnutls_assert_val(ret);
 }
 
 /**
@@ -1026,7 +1100,7 @@ static int name_constraints_add(gnutls_x509_name_constraints_t nc,
  * @nc2: The name constraints to be merged with
  *
  * This function will merge the provided name constraints structures
- * as per RFC5280 p6.1.4. That is, the excluded constraints will be appended,
+ * as per RFC5280 p6.1.4. That is, the excluded constraints will be unioned,
  * and permitted will be intersected. The intersection assumes that @nc
  * is the root CA constraints.
  *
@@ -1048,8 +1122,8 @@ int _gnutls_x509_name_constraints_merge(gnutls_x509_name_constraints_t nc,
                return ret;
        }
 
-       ret = name_constraints_node_list_concat(nc, &nc->excluded,
-                                               &nc2->excluded);
+       ret = name_constraints_node_list_union(nc, &nc->excluded,
+                                              &nc2->excluded);
        if (ret < 0) {
                gnutls_assert();
                return ret;