diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2014-11-27 15:06:11 +0100 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2014-11-27 15:51:04 +0100 |
commit | 0349b3c4c2d33b13fc0757d363714214135c9eff (patch) | |
tree | eb1cb14d7171fd5474c6ad0f246783900db233f8 /lib | |
parent | ed3913fdbc8ffe3b552c0d0f593f17c4e1a273d5 (diff) | |
download | gnutls-0349b3c4c2d33b13fc0757d363714214135c9eff.tar.gz |
Added flag GNUTLS_X509_CRT_LIST_SORT for gnutls_x509_crt_list_import*
That also allows automatically sorting input chains to the
gnutls_certificate_credentials_t structure.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gnutls_pcert.c | 56 | ||||
-rw-r--r-- | lib/gnutls_x509.c | 123 | ||||
-rw-r--r-- | lib/includes/gnutls/abstract.h | 4 | ||||
-rw-r--r-- | lib/includes/gnutls/x509.h | 4 | ||||
-rw-r--r-- | lib/libgnutls.map | 1 | ||||
-rw-r--r-- | lib/x509/common.c | 70 | ||||
-rw-r--r-- | lib/x509/common.h | 9 | ||||
-rw-r--r-- | lib/x509/verify-high.c | 62 | ||||
-rw-r--r-- | lib/x509/x509.c | 17 |
9 files changed, 216 insertions, 130 deletions
diff --git a/lib/gnutls_pcert.c b/lib/gnutls_pcert.c index 1d81cbbedc..0ce2989831 100644 --- a/lib/gnutls_pcert.c +++ b/lib/gnutls_pcert.c @@ -23,6 +23,7 @@ #include <gnutls_int.h> #include <gnutls_errors.h> #include <auth/cert.h> +#include <x509/common.h> #include <gnutls_x509.h> #include "x509/x509_int.h" #ifdef ENABLE_OPENPGP @@ -32,7 +33,7 @@ /** * gnutls_pcert_import_x509: * @pcert: The pcert structure - * @crt: The raw certificate to be imported + * @crt: The certificate to be imported * @flags: zero for now * * This convenience function will import the given certificate to a @@ -85,6 +86,59 @@ int gnutls_pcert_import_x509(gnutls_pcert_st * pcert, } /** + * gnutls_pcert_import_x509_list: + * @pcert: The pcert structure + * @crt: The certificates to be imported + * @ncrt: The number of certificates + * @flags: zero or %GNUTLS_X509_CRT_LIST_SORT + * + * This convenience function will import the given certificate to a + * #gnutls_pcert_st structure. The structure must be deinitialized + * afterwards using gnutls_pcert_deinit(); + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.0 + **/ +int gnutls_pcert_import_x509_list(gnutls_pcert_st * pcert, + gnutls_x509_crt_t *crt, unsigned *ncrt, + unsigned int flags) +{ + int ret; + unsigned i; + unsigned current = 0; + gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH]; + gnutls_x509_crt_t *s; + + s = crt; + if (flags & GNUTLS_X509_CRT_LIST_SORT && *ncrt > 1) { + s = _gnutls_sort_clist(sorted, crt, ncrt, NULL); + if (s == crt) { + gnutls_assert(); + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + } + } + + for (i=0;i<*ncrt;i++) { + ret = gnutls_pcert_import_x509(&pcert[i], s[i], 0); + if (ret < 0) { + current = i; + goto cleanup; + } + } + + return 0; + + cleanup: + for (i=0;i<current;i++) { + gnutls_pcert_deinit(&pcert[i]); + } + return ret; + +} + +/** * gnutls_pcert_list_import_x509_raw: * @pcerts: The structures to store the parsed certificate. Must not be initialized. * @pcert_max: Initially must hold the maximum number of certs. It will be updated with the number of certs available. diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c index b2cf632493..c9b099b55c 100644 --- a/lib/gnutls_x509.c +++ b/lib/gnutls_x509.c @@ -355,31 +355,6 @@ static int get_x509_name(gnutls_x509_crt_t crt, gnutls_str_array_t * names) return 0; } -static int get_x509_name_raw(gnutls_datum_t * raw, - gnutls_x509_crt_fmt_t type, - gnutls_str_array_t * names) -{ - int ret; - gnutls_x509_crt_t crt; - - ret = gnutls_x509_crt_init(&crt); - if (ret < 0) { - gnutls_assert(); - return ret; - } - - ret = gnutls_x509_crt_import(crt, raw, type); - if (ret < 0) { - gnutls_assert(); - gnutls_x509_crt_deinit(crt); - return ret; - } - - ret = get_x509_name(crt, names); - gnutls_x509_crt_deinit(crt); - return ret; -} - /* Reads a DER encoded certificate list from memory and stores it to a * gnutls_cert structure. Returns the number of certificates parsed. */ @@ -457,8 +432,10 @@ parse_pem_cert_mem(gnutls_certificate_credentials_t res, const char *ptr; gnutls_datum_t tmp; int ret, count, i; - gnutls_pcert_st *certs = NULL; + unsigned ncerts = 0; + gnutls_pcert_st *pcerts = NULL; gnutls_str_array_t names; + gnutls_x509_crt_t unsorted[DEFAULT_MAX_VERIFY_DEPTH]; _gnutls_str_array_init(&names); @@ -479,37 +456,21 @@ parse_pem_cert_mem(gnutls_certificate_credentials_t res, count = 0; do { - certs = - gnutls_realloc_fast(certs, - (count + - 1) * sizeof(gnutls_pcert_st)); - - if (certs == NULL) { - gnutls_assert(); - ret = GNUTLS_E_MEMORY_ERROR; - goto cleanup; - } - tmp.data = (void *) ptr; tmp.size = size; - if (count == 0) { - ret = - get_x509_name_raw(&tmp, GNUTLS_X509_FMT_PEM, - &names); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } + ret = gnutls_x509_crt_init(&unsorted[count]); + if (ret < 0) { + gnutls_assert(); + goto cleanup; } - ret = - gnutls_pcert_import_x509_raw(&certs[count], &tmp, - GNUTLS_X509_FMT_PEM, 0); + ret = gnutls_x509_crt_import(unsorted[count], &tmp, GNUTLS_X509_FMT_PEM); if (ret < 0) { gnutls_assert(); goto cleanup; } + count++; /* now we move ptr after the pem header */ @@ -532,27 +493,53 @@ parse_pem_cert_mem(gnutls_certificate_credentials_t res, } else ptr = NULL; - count++; + } + while (ptr != NULL && count < DEFAULT_MAX_VERIFY_DEPTH); + ret = + get_x509_name(unsorted[0], &names); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + pcerts = gnutls_malloc(sizeof(gnutls_pcert_st) * count); + if (pcerts == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; } - while (ptr != NULL); + ncerts = count; ret = - certificate_credential_append_crt_list(res, names, certs, - count); + gnutls_pcert_import_x509_list(pcerts, unsorted, &ncerts, GNUTLS_X509_CRT_LIST_SORT); if (ret < 0) { + gnutls_free(pcerts); + pcerts = NULL; gnutls_assert(); goto cleanup; } - return count; + ret = + certificate_credential_append_crt_list(res, names, pcerts, + ncerts); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + for (i = 0; i < count; i++) + gnutls_x509_crt_deinit(unsorted[i]); + + return ncerts; cleanup: _gnutls_str_array_clear(&names); - if (certs) { + for (i = 0; i < count; i++) + gnutls_x509_crt_deinit(unsorted[i]); + if (pcerts) { for (i = 0; i < count; i++) - gnutls_pcert_deinit(&certs[i]); - gnutls_free(certs); + gnutls_pcert_deinit(&pcerts[i]); + gnutls_free(pcerts); } return ret; } @@ -1080,7 +1067,7 @@ gnutls_certificate_set_x509_key(gnutls_certificate_credentials_t res, int cert_list_size, gnutls_x509_privkey_t key) { - int ret, i; + int ret; gnutls_privkey_t pkey; gnutls_pcert_st *pcerts = NULL; gnutls_str_array_t names; @@ -1121,16 +1108,17 @@ gnutls_certificate_set_x509_key(gnutls_certificate_credentials_t res, } ret = get_x509_name(cert_list[0], &names); - if (ret < 0) - return gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } - for (i = 0; i < cert_list_size; i++) { - ret = - gnutls_pcert_import_x509(&pcerts[i], cert_list[i], 0); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } + ret = + gnutls_pcert_import_x509_list(pcerts, cert_list, (unsigned int*)&cert_list_size, + GNUTLS_X509_CRT_LIST_SORT); + if (ret < 0) { + gnutls_assert(); + goto cleanup; } ret = @@ -1145,12 +1133,13 @@ gnutls_certificate_set_x509_key(gnutls_certificate_credentials_t res, if ((ret = _gnutls_check_key_cert_match(res)) < 0) { gnutls_assert(); - return ret; + goto cleanup; } return 0; cleanup: + gnutls_free(pcerts); _gnutls_str_array_clear(&names); return ret; } diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h index 5382336325..ab59e57aec 100644 --- a/lib/includes/gnutls/abstract.h +++ b/lib/includes/gnutls/abstract.h @@ -462,6 +462,10 @@ typedef struct gnutls_pcert_st { int gnutls_pcert_import_x509(gnutls_pcert_st * pcert, gnutls_x509_crt_t crt, unsigned int flags); +int gnutls_pcert_import_x509_list(gnutls_pcert_st * pcert, + gnutls_x509_crt_t *crt, unsigned *ncrt, + unsigned int flags); + int gnutls_pcert_export_x509(gnutls_pcert_st * pcert, gnutls_x509_crt_t * crt); diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h index eb606c8615..87b948d588 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -112,12 +112,14 @@ extern "C" { * @GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED: Fail if the certificates * in the buffer are not ordered starting from subject to issuer. * The error code will be %GNUTLS_E_CERTIFICATE_LIST_UNSORTED. + * @GNUTLS_X509_CRT_LIST_SORT: Sort the certificate chain if unsorted. * * Enumeration of different certificate import flags. */ typedef enum gnutls_certificate_import_flags { GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED = 1, - GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED = 2 + GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED = 1<<1, + GNUTLS_X509_CRT_LIST_SORT = 1<<2 } gnutls_certificate_import_flags; int gnutls_x509_crt_init(gnutls_x509_crt_t * cert); diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 2b0a32dd9a..7e91ad4ba6 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1065,6 +1065,7 @@ GNUTLS_3_1_0 { gnutls_system_key_delete; gnutls_system_key_add_x509; gnutls_register_custom_url; + gnutls_pcert_import_x509_list; } GNUTLS_3_0_0; GNUTLS_FIPS140 { diff --git a/lib/x509/common.c b/lib/x509/common.c index f85c6de85e..a34cdd5582 100644 --- a/lib/x509/common.c +++ b/lib/x509/common.c @@ -1978,3 +1978,73 @@ _gnutls_check_valid_key_id(gnutls_datum_t *key_id, out: return result; } + +/* Takes a certificate list and orders it with subject, issuer order. + * + * *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 sorted list which may be the original clist. + */ +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) +{ + int prev; + unsigned int j, i; + int issuer[DEFAULT_MAX_VERIFY_DEPTH]; /* contain the index of the issuers */ + + /* Do not bother sorting if too many certificates are given. + * Prevent any DoS attacks. + */ + if (*clist_size > DEFAULT_MAX_VERIFY_DEPTH) + return clist; + + for (i = 0; i < DEFAULT_MAX_VERIFY_DEPTH; i++) + issuer[i] = -1; + + /* Find the issuer of each certificate and store it + * in issuer array. + */ + 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) { + issuer[i] = j; + break; + } + } + } + + if (issuer[0] == -1) { + *clist_size = 1; + return clist; + } + + prev = 0; + sorted[0] = clist[0]; + for (i = 1; i < *clist_size; i++) { + prev = issuer[prev]; + if (prev == -1) { /* no issuer */ + *clist_size = i; + break; + } + sorted[i] = clist[prev]; + } + + if (func) { + for (i = 1; i < *clist_size; i++) { + if (issuer[i] == -1) { + func(clist[i]); + } + } + } + + return sorted; +} diff --git a/lib/x509/common.h b/lib/x509/common.h index d39a87c24f..f319b5be1e 100644 --- a/lib/x509/common.h +++ b/lib/x509/common.h @@ -231,4 +231,13 @@ int x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert, int x509_crt_to_raw_pubkey(gnutls_x509_crt_t crt, gnutls_datum_t * rpubkey); + +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); + #endif diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c index 98d5af18f5..b8e8be4606 100644 --- a/lib/x509/verify-high.c +++ b/lib/x509/verify-high.c @@ -790,66 +790,6 @@ static int shorten_clist(gnutls_x509_trust_list_t list, return clist_size; } -/* Takes a certificate list and orders it with subject, issuer order. - * - * *clist_size contains the size of the ordered list (which is always less or - * equal to the original). - * - * Returns the sorted list which may be the original clist. - */ -static gnutls_x509_crt_t *sort_clist(gnutls_x509_crt_t - sorted[DEFAULT_MAX_VERIFY_DEPTH], - gnutls_x509_crt_t * clist, - unsigned int *clist_size) -{ - int prev; - unsigned int j, i; - int issuer[DEFAULT_MAX_VERIFY_DEPTH]; /* contain the index of the issuers */ - - /* Do not bother sorting if too many certificates are given. - * Prevent any DoS attacks. - */ - if (*clist_size > DEFAULT_MAX_VERIFY_DEPTH) - return clist; - - for (i = 0; i < DEFAULT_MAX_VERIFY_DEPTH; i++) - issuer[i] = -1; - - /* Find the issuer of each certificate and store it - * in issuer array. - */ - 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) { - issuer[i] = j; - break; - } - } - } - - if (issuer[0] == -1) { - *clist_size = 1; - return clist; - } - - prev = 0; - sorted[0] = clist[0]; - for (i = 1; i < *clist_size; i++) { - prev = issuer[prev]; - if (prev == -1) { /* no issuer */ - *clist_size = i; - break; - } - sorted[i] = clist[prev]; - } - - return sorted; -} - static int trust_list_get_issuer(gnutls_x509_trust_list_t list, gnutls_x509_crt_t cert, @@ -1163,7 +1103,7 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, } if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_UNSORTED_CHAIN)) - cert_list = sort_clist(sorted, cert_list, &cert_list_size); + cert_list = _gnutls_sort_clist(sorted, cert_list, &cert_list_size, NULL); 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 5c8ee0fca0..d2b594876c 100644 --- a/lib/x509/x509.c +++ b/lib/x509/x509.c @@ -3445,6 +3445,23 @@ gnutls_x509_crt_list_import(gnutls_x509_crt_t * certs, *cert_max = count; + 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(); + ret = GNUTLS_E_UNIMPLEMENTED_FEATURE; + goto error; + } + + count = *cert_max; + if (s == sorted) { + memcpy(certs, s, (*cert_max)*sizeof(gnutls_x509_crt_t)); + } + } + if (flags & GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED) { ret = check_if_sorted(certs, *cert_max); if (ret < 0) { |