summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <ueno@gnu.org>2020-11-15 09:57:37 +0100
committerDaiki Ueno <ueno@gnu.org>2020-11-19 15:38:00 +0100
commitd268f19510a95f92d11d8f8dc7d94fcae4d765cc (patch)
tree05d38c8730f7497fd71780494f2c747f4e540cfa
parent8c35ab1a25268d4a4935cb915eb1c1b5a18a3dca (diff)
downloadgnutls-d268f19510a95f92d11d8f8dc7d94fcae4d765cc.tar.gz
_gnutls_sort_clist: simplify the calling convention
Signed-off-by: Daiki Ueno <ueno@gnu.org>
-rw-r--r--lib/pcert.c10
-rw-r--r--lib/x509/common.c95
-rw-r--r--lib/x509/common.h7
-rw-r--r--lib/x509/verify-high.c10
-rw-r--r--lib/x509/x509.c17
5 files changed, 74 insertions, 65 deletions
diff --git a/lib/pcert.c b/lib/pcert.c
index 525baff6fc..89d3d40e63 100644
--- a/lib/pcert.c
+++ b/lib/pcert.c
@@ -120,17 +120,17 @@ int gnutls_pcert_import_x509_list(gnutls_pcert_st * pcert_list,
if (flags & GNUTLS_X509_CRT_LIST_SORT && *ncrt > 1) {
if (*ncrt > DEFAULT_MAX_VERIFY_DEPTH) {
- ret = _gnutls_check_if_sorted(crt, *ncrt);
+ ret = _gnutls_check_if_sorted(s, *ncrt);
if (ret < 0) {
gnutls_assert();
return GNUTLS_E_CERTIFICATE_LIST_UNSORTED;
}
} else {
- s = _gnutls_sort_clist(sorted, crt, ncrt, NULL);
- if (s == crt) {
- gnutls_assert();
- return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+ for (i = 0; i < *ncrt; i++) {
+ sorted[i] = s[i];
}
+ s = sorted;
+ *ncrt = _gnutls_sort_clist(s, *ncrt);
}
}
diff --git a/lib/x509/common.c b/lib/x509/common.c
index a1f6d62e13..9e22c2c97a 100644
--- a/lib/x509/common.c
+++ b/lib/x509/common.c
@@ -1690,38 +1690,52 @@ _gnutls_check_valid_key_id(const gnutls_datum_t *key_id,
return result;
}
-/* Takes a certificate list and orders it with subject, issuer order.
+/* Sort a certficate list in place with subject, issuer order. @clist_size must
+ * be equal to or less than %DEFAULT_MAX_VERIFY_DEPTH.
*
- * *clist_size contains the size of the ordered list (which is always less or
- * equal to the original).
- * @func: the function to call to elements outside the sort.
+ * Returns the index in clist where the initial contiguous segment ends. If the
+ * chain contains multiple segments separated by gaps (e.g., missing issuers),
+ * the caller shall call this function multiple times.
*
- * This function is intentionally kept simple to be easily verified
- * so that it can be used with untrusted chains. The introduction
- * of the func parameter added significant complexity in that aspect.
- * If more demanding use-cases need to be handled, consider splitting
- * that function.
+ * For example, suppose we want the following certificate chain as a result of
+ * sorting:
*
- * Returns the sorted list which may be the original clist.
+ * A -> (B) -> C -> D -> E -> (F) -> G -> H -> I
*
+ * from the input [A, C, E, D, G, I, H] (B and F are missing).
+ *
+ * On the first call of this function:
+ *
+ * _gnutls_sort_clist(clist, clist_size)
+ *
+ * it returns 1, meaning that the first segment only contains A. The content of
+ * @clist will remain the same [A, C, E, D, G, I, H].
+ *
+ * Then the caller will call this function again, starting from the second
+ * element:
+ *
+ * _gnutls_sort_clist(&clist[1], clist_size - 1)
+ *
+ * This time it returns 3, meaning that the first segment contains [C, D, E].
+ * The content of @clist will be [A, C, D, E, G, I, H].
+ *
+ * Finally, after calling the function on the remaining elements:
+ *
+ * _gnutls_sort_clist(&clist[1 + 3], clist_size - (1 + 3))
+ *
+ * It will return 3, meaning that the first segment contains [G, H, I]. At this
+ * point, sorting of @clist is complete.
*/
-gnutls_x509_crt_t *_gnutls_sort_clist(gnutls_x509_crt_t
- sorted[DEFAULT_MAX_VERIFY_DEPTH],
- gnutls_x509_crt_t *clist,
- unsigned int *clist_size,
- gnutls_cert_vfunc func)
+unsigned int _gnutls_sort_clist(gnutls_x509_crt_t *clist,
+ unsigned int clist_size)
{
int prev;
- unsigned int j, i;
+ unsigned int i, j, k;
int issuer[DEFAULT_MAX_VERIFY_DEPTH]; /* contain the index of the issuers */
bool insorted[DEFAULT_MAX_VERIFY_DEPTH]; /* non zero if clist[i] used in sorted list */
- unsigned orig_size = *clist_size;
+ gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH];
- /* Do not bother sorting if too many certificates are given.
- * Prevent any DoS attacks.
- */
- if (*clist_size > DEFAULT_MAX_VERIFY_DEPTH)
- return clist;
+ assert(clist_size <= DEFAULT_MAX_VERIFY_DEPTH);
for (i = 0; i < DEFAULT_MAX_VERIFY_DEPTH; i++) {
issuer[i] = -1;
@@ -1732,51 +1746,46 @@ gnutls_x509_crt_t *_gnutls_sort_clist(gnutls_x509_crt_t
* in issuer array. O(n^2) so consider that before
* increasing DEFAULT_MAX_VERIFY_DEPTH.
*/
- for (i = 0; i < *clist_size; i++) {
- for (j = 1; j < *clist_size; j++) {
+ for (i = 0; i < clist_size; i++) {
+ for (j = 1; j < clist_size; j++) {
if (i == j)
continue;
- if (gnutls_x509_crt_check_issuer(clist[i],
- clist[j]) != 0) {
+ if (gnutls_x509_crt_check_issuer(clist[i], clist[j])) {
issuer[i] = j;
break;
}
}
}
- /* the first element is always included */
sorted[0] = clist[0];
insorted[0] = 1;
- if (issuer[0] == -1) {
- *clist_size = 1;
- goto exit;
- }
-
prev = 0;
- for (i = 1; i < *clist_size; i++) {
+ for (i = 1; i < clist_size; i++) {
prev = issuer[prev];
if (prev < 0) { /* no issuer */
- *clist_size = i;
break;
}
+
sorted[i] = clist[prev];
insorted[prev] = 1;
}
- exit:
- if (func) {
- /* call func() on all the elements that were
- * not used in the sorted list */
- for (i = 1; i < orig_size; i++) {
- if (insorted[i] == 0) {
- func(clist[i]);
- }
+ /* append the remaining certs */
+ for (j = 1, k = i; j < clist_size; j++) {
+ if (!insorted[j]) {
+ sorted[k++] = clist[j];
}
}
- return sorted;
+ /* write out the sorted list */
+ assert(k == clist_size);
+ for (j = 0; j < clist_size; j++) {
+ clist[j] = sorted[j];
+ }
+
+ return i;
}
int _gnutls_check_if_sorted(gnutls_x509_crt_t * crt, int nr)
diff --git a/lib/x509/common.h b/lib/x509/common.h
index 483bd1de6c..7bca9ddf42 100644
--- a/lib/x509/common.h
+++ b/lib/x509/common.h
@@ -283,11 +283,8 @@ int x509_crt_to_raw_pubkey(gnutls_x509_crt_t crt,
typedef void (*gnutls_cert_vfunc)(gnutls_x509_crt_t);
-gnutls_x509_crt_t *_gnutls_sort_clist(gnutls_x509_crt_t
- sorted[DEFAULT_MAX_VERIFY_DEPTH],
- gnutls_x509_crt_t * clist,
- unsigned int *clist_size,
- gnutls_cert_vfunc func);
+unsigned int _gnutls_sort_clist(gnutls_x509_crt_t *clist,
+ unsigned int clist_size);
int _gnutls_check_if_sorted(gnutls_x509_crt_t * crt, int nr);
diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c
index 763c527a59..9c4f292f05 100644
--- a/lib/x509/verify-high.c
+++ b/lib/x509/verify-high.c
@@ -1310,8 +1310,14 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list,
}
}
- if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_UNSORTED_CHAIN))
- cert_list = _gnutls_sort_clist(sorted, cert_list, &cert_list_size, NULL);
+ if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_UNSORTED_CHAIN) &&
+ cert_list_size <= DEFAULT_MAX_VERIFY_DEPTH) {
+ for (i = 0; i < cert_list_size; i++) {
+ sorted[i] = cert_list[i];
+ }
+ cert_list = sorted;
+ cert_list_size = _gnutls_sort_clist(cert_list, cert_list_size);
+ }
cert_list_size = shorten_clist(list, cert_list, cert_list_size);
if (cert_list_size <= 0)
diff --git a/lib/x509/x509.c b/lib/x509/x509.c
index c713f857a0..8cd8072e87 100644
--- a/lib/x509/x509.c
+++ b/lib/x509/x509.c
@@ -3873,20 +3873,17 @@ gnutls_x509_crt_list_import(gnutls_x509_crt_t * certs,
if (nocopy == 0) {
if (flags & GNUTLS_X509_CRT_LIST_SORT && *cert_max > 1) {
- gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH];
- gnutls_x509_crt_t *s;
-
- s = _gnutls_sort_clist(sorted, certs, cert_max, gnutls_x509_crt_deinit);
- if (s == certs) {
- gnutls_assert();
+ if (*cert_max > DEFAULT_MAX_VERIFY_DEPTH) {
ret = GNUTLS_E_UNIMPLEMENTED_FEATURE;
goto error;
}
-
- count = *cert_max;
- if (s == sorted) {
- memcpy(certs, s, (*cert_max)*sizeof(gnutls_x509_crt_t));
+ count = _gnutls_sort_clist(certs, *cert_max);
+ if (count < *cert_max) {
+ for (j = count; j < *cert_max; j++) {
+ gnutls_x509_crt_deinit(certs[j]);
+ }
}
+ *cert_max = count;
}
if (flags & GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED) {