src->name.data, src->name.size);
}
-// 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);
-
/*-
* _gnutls_x509_name_constraints_is_empty:
* @nc: name constraints structure
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 *permitted2,
struct name_constraints_node_list_st *excluded)
{
- struct name_constraints_node_st *tmp;
- int ret, type, used;
- struct name_constraints_node_list_st removed = { .data = NULL,
- .size = 0,
- .capacity = 0 };
+ struct name_constraints_node_st *nc1, *nc2;
+ struct name_constraints_node_list_st result = { 0 };
+ struct name_constraints_node_list_st unsupp2 = { 0 };
+ enum name_constraint_relation rel;
+ unsigned type;
+ int ret = GNUTLS_E_SUCCESS;
+ size_t i, j, p1_unsupp = 0, p2_unsupp = 0;
+ type_bitmask_t universal_exclude_needed = 0;
+ type_bitmask_t types_in_p1 = 0, types_in_p2 = 0;
static const unsigned char universal_ip[32] = { 0 };
- /* bitmask to see if we need to add universal excluded constraints
- * (see phase 3 for details) */
- type_bitmask_t types_with_empty_intersection = 0;
-
if (permitted->size == 0 || permitted2->size == 0)
- return 0;
+ return GNUTLS_E_SUCCESS;
- /* Phase 1
- * 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) {
- 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
- type_bitmask_set(types_with_empty_intersection,
- t->type);
- found = t2;
- break;
- }
- }
+ /* make sorted views of the arrays */
+ ret = ensure_sorted(permitted);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ ret = ensure_sorted(permitted2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
- if (found != NULL && is_supported_type(t->type)) {
- /* 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--;
- permitted->dirty = true;
- continue;
+ /* deal with the leading unsupported types first: count, then union */
+ while (p1_unsupp < permitted->size &&
+ !is_supported_type(permitted->sorted_view[p1_unsupp]->type))
+ p1_unsupp++;
+ while (p2_unsupp < permitted2->size &&
+ !is_supported_type(permitted2->sorted_view[p2_unsupp]->type))
+ p2_unsupp++;
+ if (p1_unsupp) { /* copy p1 unsupported type pointers into result */
+ result.data = gnutls_calloc(
+ p1_unsupp, sizeof(struct name_constraints_node_st *));
+ if (!result.data) {
+ ret = GNUTLS_E_MEMORY_ERROR;
+ gnutls_assert();
+ goto cleanup;
+ }
+ memcpy(result.data, permitted->sorted_view,
+ p1_unsupp * sizeof(struct name_constraints_node_st *));
+ result.size = result.capacity = p1_unsupp;
+ result.dirty = true;
+ }
+ if (p2_unsupp) { /* union will make deep copies from p2 */
+ unsupp2.data = permitted2->sorted_view; /* so, just alias */
+ unsupp2.size = unsupp2.capacity = p2_unsupp;
+ unsupp2.dirty = false; /* we know it's sorted */
+ unsupp2.sorted_view = permitted2->sorted_view;
+ ret = name_constraints_node_list_union(nc, &result, &unsupp2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
}
- i++;
}
- /* Phase 2
- * iterate through all combinations from PERMITTED2 and PERMITTED
- * and create intersections of nodes with same type */
- 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;
- 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(nc, t, t2, &tmp);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
+ /* with that out of the way, pre-compute the supported types we have */
+ for (i = p1_unsupp; i < permitted->size; i++) {
+ type = permitted->sorted_view[i]->type;
+ if (type < 1 || type > GNUTLS_SAN_MAX) {
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto cleanup;
+ }
+ type_bitmask_set(types_in_p1, type);
+ }
+ for (j = p2_unsupp; j < permitted2->size; j++) {
+ type = permitted2->sorted_view[j]->type;
+ if (type < 1 || type > GNUTLS_SAN_MAX) {
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto cleanup;
+ }
+ type_bitmask_set(types_in_p2, type);
+ }
+ /* universal excludes might be needed for types intersecting to empty */
+ universal_exclude_needed = types_in_p1 & types_in_p2;
+
+ /* go through supported type NCs and intersect in a single pass */
+ i = p1_unsupp;
+ j = p2_unsupp;
+ while (i < permitted->size || j < permitted2->size) {
+ nc1 = (i < permitted->size) ? permitted->sorted_view[i] : NULL;
+ nc2 = (j < permitted2->size) ? permitted2->sorted_view[j] :
+ NULL;
+ rel = compare_name_constraint_nodes(nc1, nc2);
- if (t->type == t2->type)
- used = 1;
-
- // if intersection is not empty
- if (tmp !=
- NULL) { // intersection for this type is not empty
- // check bounds
- if (tmp->type > GNUTLS_SAN_MAX ||
- tmp->type == 0) {
- gnutls_free(tmp);
- return gnutls_assert_val(
- GNUTLS_E_INTERNAL_ERROR);
- }
- // we will not add universal excluded constraint for this type
- type_bitmask_clr(types_with_empty_intersection,
- tmp->type);
- // add intersection node to PERMITTED
- ret = name_constraints_node_list_add(permitted,
- tmp);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
- }
+ switch (rel) {
+ case NC_SORTS_BEFORE:
+ assert(nc1 != NULL); /* comparator-guaranteed */
+ /* if nothing to intersect with, shallow-copy nc1 */
+ if (!type_bitmask_in(types_in_p2, nc1->type))
+ ret = name_constraints_node_list_add(&result,
+ nc1);
+ i++; /* otherwise skip nc1 */
+ break;
+ case NC_SORTS_AFTER:
+ assert(nc2 != NULL); /* comparator-guaranteed */
+ /* if nothing to intersect with, deep-copy nc2 */
+ if (!type_bitmask_in(types_in_p1, nc2->type))
+ ret = name_constraints_node_add_copy(
+ nc, &result, nc2);
+ j++; /* otherwise skip nc2 */
+ break;
+ case NC_INCLUDED_BY: /* add nc1, shallow-copy */
+ assert(nc1 != NULL && nc2 != NULL); /* comparator */
+ type_bitmask_clr(universal_exclude_needed, nc1->type);
+ ret = name_constraints_node_list_add(&result, nc1);
+ i++;
+ break;
+ case NC_INCLUDES: /* pick nc2, deep-copy */
+ assert(nc1 != NULL && nc2 != NULL); /* comparator */
+ type_bitmask_clr(universal_exclude_needed, nc2->type);
+ ret = name_constraints_node_add_copy(nc, &result, nc2);
+ j++;
+ break;
+ case NC_EQUAL: /* pick whichever: nc1, shallow-copy */
+ assert(nc1 != NULL && nc2 != NULL); /* loop condition */
+ type_bitmask_clr(universal_exclude_needed, nc1->type);
+ ret = name_constraints_node_list_add(&result, nc1);
+ i++;
+ j++;
+ break;
}
- // 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) {
- ret = name_constraints_node_add_copy(nc, permitted, t2);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
}
}
- /* 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). */
+ /* finishing touch: add universal excluded constraints for types where
+ * both lists had constraints, but all intersections ended up empty */
for (type = 1; type <= GNUTLS_SAN_MAX; type++) {
- if (!type_bitmask_in(types_with_empty_intersection, type))
+ if (!type_bitmask_in(universal_exclude_needed, type))
continue;
_gnutls_hard_log(
"Adding universal excluded name constraint for type %d.\n",
goto cleanup;
}
break;
- default: // do nothing, at least one node was already moved in phase 1
- break;
+ default: /* unsupported type; should be unreacheable */
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto cleanup;
}
}
- ret = GNUTLS_E_SUCCESS;
+ gnutls_free(permitted->data);
+ gnutls_free(permitted->sorted_view);
+ permitted->data = result.data;
+ permitted->sorted_view = NULL;
+ permitted->size = result.size;
+ permitted->capacity = result.capacity;
+ permitted->dirty = true;
+
+ result.data = NULL;
+ ret = GNUTLS_E_SUCCESS;
cleanup:
- gnutls_free(removed.data);
+ name_constraints_node_list_clear(&result);
return ret;
}
return rel == NC_EQUAL || rel == NC_INCLUDED_BY;
}
-/*-
- * name_constraints_intersect_nodes:
- * @nc1: name constraints node 1
- * @nc2: name constraints node 2
- * @_intersection: newly allocated node with intersected constraints,
- * NULL if the intersection is empty
- *
- * 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(
- 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
- struct name_constraints_node_st *intersection = NULL;
- const struct name_constraints_node_st *to_copy = NULL;
- enum name_constraint_relation rel;
-
- *_intersection = NULL;
-
- if (node1->type != node2->type) {
- return GNUTLS_E_SUCCESS;
- }
- switch (node1->type) {
- case GNUTLS_SAN_DNSNAME:
- rel = compare_dns_names(&node1->name, &node2->name);
- switch (rel) {
- case NC_EQUAL: // equal means doesn't matter which one
- case NC_INCLUDES: // node2 is more specific
- to_copy = node2;
- break;
- case NC_INCLUDED_BY: // node1 is more specific
- to_copy = node1;
- break;
- case NC_SORTS_BEFORE: // no intersection
- case NC_SORTS_AFTER: // no intersection
- return GNUTLS_E_SUCCESS;
- }
- break;
- case GNUTLS_SAN_RFC822NAME:
- rel = compare_emails(&node1->name, &node2->name);
- switch (rel) {
- case NC_EQUAL: // equal means doesn't matter which one
- case NC_INCLUDES: // node2 is more specific
- to_copy = node2;
- break;
- case NC_INCLUDED_BY: // node1 is more specific
- to_copy = node1;
- break;
- case NC_SORTS_BEFORE: // no intersection
- case NC_SORTS_AFTER: // no intersection
- return GNUTLS_E_SUCCESS;
- }
- break;
- case GNUTLS_SAN_IPADDRESS:
- rel = compare_ip_ncs(&node1->name, &node2->name);
- switch (rel) {
- case NC_EQUAL: // equal means doesn't matter which one
- case NC_INCLUDES: // node2 is more specific
- to_copy = node2;
- break;
- case NC_INCLUDED_BY: // node1 is more specific
- to_copy = node1;
- break;
- case NC_SORTS_BEFORE: // no intersection
- case NC_SORTS_AFTER: // no intersection
- return GNUTLS_E_SUCCESS;
- }
- 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(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);
- }
-
- return GNUTLS_E_SUCCESS;
-}
-
/*
* Returns: true if the certification is acceptable, and false otherwise.
*/