diff options
Diffstat (limited to 'lib/x509/verify-high.c')
-rw-r--r-- | lib/x509/verify-high.c | 1834 |
1 files changed, 1834 insertions, 0 deletions
diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c new file mode 100644 index 0000000..5698d4f --- /dev/null +++ b/lib/x509/verify-high.c @@ -0,0 +1,1834 @@ +/* + * Copyright (C) 2011-2016 Free Software Foundation, Inc. + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <libtasn1.h> +#include <global.h> +#include <num.h> /* MAX */ +#include <tls-sig.h> +#include <str.h> +#include <datum.h> +#include <hash-pjw-bare.h> +#include "x509_int.h" +#include <common.h> +#include <gnutls/x509-ext.h> +#include "verify-high.h" +#include "intprops.h" + +struct named_cert_st { + gnutls_x509_crt_t cert; + uint8_t name[MAX_SERVER_NAME_SIZE]; + unsigned int name_size; +}; + +struct node_st { + /* The trusted certificates */ + gnutls_x509_crt_t *trusted_cas; + unsigned int trusted_ca_size; + + struct named_cert_st *named_certs; + unsigned int named_cert_size; + + /* The trusted CRLs */ + gnutls_x509_crl_t *crls; + unsigned int crl_size; +}; + +struct gnutls_x509_trust_list_iter { + unsigned int node_index; + unsigned int ca_index; + +#ifdef ENABLE_PKCS11 + gnutls_pkcs11_obj_t* pkcs11_list; + unsigned int pkcs11_index; + unsigned int pkcs11_size; +#endif +}; + +#define DEFAULT_SIZE 127 + +struct cert_set_node_st { + gnutls_x509_crt_t *certs; + unsigned int size; +}; + +struct cert_set_st { + struct cert_set_node_st *node; + unsigned int size; +}; + +static int +cert_set_init(struct cert_set_st *set, unsigned int size) +{ + memset(set, 0, sizeof(*set)); + + set->size = size; + set->node = gnutls_calloc(size, sizeof(*set->node)); + if (!set->node) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + return 0; +} + +static void +cert_set_deinit(struct cert_set_st *set) +{ + size_t i; + + for (i = 0; i < set->size; i++) { + gnutls_free(set->node[i].certs); + } + + gnutls_free(set->node); +} + +static bool +cert_set_contains(struct cert_set_st *set, const gnutls_x509_crt_t cert) +{ + size_t hash, i; + + hash = hash_pjw_bare(cert->raw_dn.data, cert->raw_dn.size); + hash %= set->size; + + for (i = 0; i < set->node[hash].size; i++) { + if (unlikely(gnutls_x509_crt_equals(set->node[hash].certs[i], cert))) { + return true; + } + } + + return false; +} + +static int +cert_set_add(struct cert_set_st *set, const gnutls_x509_crt_t cert) +{ + size_t hash; + + hash = hash_pjw_bare(cert->raw_dn.data, cert->raw_dn.size); + hash %= set->size; + + if (unlikely(INT_ADD_OVERFLOW(set->node[hash].size, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + set->node[hash].certs = + _gnutls_reallocarray_fast(set->node[hash].certs, + set->node[hash].size + 1, + sizeof(*set->node[hash].certs)); + if (!set->node[hash].certs) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + set->node[hash].certs[set->node[hash].size] = cert; + set->node[hash].size++; + + return 0; +} + +/** + * gnutls_x509_trust_list_init: + * @list: A pointer to the type to be initialized + * @size: The size of the internal hash table. Use (0) for default size. + * + * This function will initialize an X.509 trust list structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0.0 + **/ +int +gnutls_x509_trust_list_init(gnutls_x509_trust_list_t * list, + unsigned int size) +{ + gnutls_x509_trust_list_t tmp; + + FAIL_IF_LIB_ERROR; + + tmp = + gnutls_calloc(1, sizeof(struct gnutls_x509_trust_list_st)); + + if (!tmp) + return GNUTLS_E_MEMORY_ERROR; + + if (size == 0) + size = DEFAULT_SIZE; + tmp->size = size; + + tmp->node = gnutls_calloc(1, tmp->size * sizeof(tmp->node[0])); + if (tmp->node == NULL) { + gnutls_assert(); + gnutls_free(tmp); + return GNUTLS_E_MEMORY_ERROR; + } + + *list = tmp; + + return 0; /* success */ +} + +/** + * gnutls_x509_trust_list_deinit: + * @list: The list to be deinitialized + * @all: if non-zero it will deinitialize all the certificates and CRLs contained in the structure. + * + * This function will deinitialize a trust list. Note that the + * @all flag should be typically non-zero unless you have specified + * your certificates using gnutls_x509_trust_list_add_cas() and you + * want to prevent them from being deinitialized by this function. + * + * Since: 3.0.0 + **/ +void +gnutls_x509_trust_list_deinit(gnutls_x509_trust_list_t list, + unsigned int all) +{ + unsigned int i, j; + + if (!list) + return; + + for (j = 0; j < list->blacklisted_size; j++) { + gnutls_x509_crt_deinit(list->blacklisted[j]); + } + gnutls_free(list->blacklisted); + + for (j = 0; j < list->keep_certs_size; j++) { + gnutls_x509_crt_deinit(list->keep_certs[j]); + } + gnutls_free(list->keep_certs); + + for (i = 0; i < list->size; i++) { + if (all) { + for (j = 0; j < list->node[i].trusted_ca_size; j++) { + gnutls_x509_crt_deinit(list->node[i]. + trusted_cas[j]); + } + } + gnutls_free(list->node[i].trusted_cas); + + + if (all) { + for (j = 0; j < list->node[i].crl_size; j++) { + gnutls_x509_crl_deinit(list->node[i]. + crls[j]); + } + } + gnutls_free(list->node[i].crls); + + if (all) { + for (j = 0; j < list->node[i].named_cert_size; j++) { + gnutls_x509_crt_deinit(list->node[i]. + named_certs[j]. + cert); + } + } + gnutls_free(list->node[i].named_certs); + } + + gnutls_free(list->x509_rdn_sequence.data); + gnutls_free(list->node); + gnutls_free(list->pkcs11_token); + gnutls_free(list); +} + +static int +add_new_ca_to_rdn_seq(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t ca) +{ + gnutls_datum_t tmp; + size_t newsize; + unsigned char *newdata, *p; + + /* Add DN of the last added CAs to the RDN sequence + * This will be sent to clients when a certificate + * request message is sent. + */ + tmp.data = ca->raw_dn.data; + tmp.size = ca->raw_dn.size; + + newsize = list->x509_rdn_sequence.size + 2 + tmp.size; + if (newsize < list->x509_rdn_sequence.size) { + gnutls_assert(); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + newdata = + gnutls_realloc_fast(list->x509_rdn_sequence.data, + newsize); + if (newdata == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + p = newdata + list->x509_rdn_sequence.size; + _gnutls_write_uint16(tmp.size, p); + if (tmp.data != NULL) + memcpy(p + 2, tmp.data, tmp.size); + + list->x509_rdn_sequence.size = newsize; + list->x509_rdn_sequence.data = newdata; + + return 0; +} + +#ifdef ENABLE_PKCS11 +/* Keeps the provided certificate in a structure that will be + * deallocated on deinit. This is to handle get_issuer() with + * pkcs11 trust modules when the GNUTLS_TL_GET_COPY flag isn't + * given. It is not thread safe. */ +static int +trust_list_add_compat(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert) +{ + if (unlikely(INT_ADD_OVERFLOW(list->keep_certs_size, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + list->keep_certs = + _gnutls_reallocarray_fast(list->keep_certs, + list->keep_certs_size + 1, + sizeof(list->keep_certs[0])); + if (list->keep_certs == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + list->keep_certs[list->keep_certs_size] = cert; + list->keep_certs_size++; + + return 0; +} +#endif + +/** + * gnutls_x509_trust_list_add_cas: + * @list: The list + * @clist: A list of CAs + * @clist_size: The length of the CA list + * @flags: flags from %gnutls_trust_list_flags_t + * + * This function will add the given certificate authorities + * to the trusted list. The CAs in @clist must not be deinitialized + * during the lifetime of @list. + * + * If the flag %GNUTLS_TL_NO_DUPLICATES is specified, then + * this function will ensure that no duplicates will be + * present in the final trust list. + * + * If the flag %GNUTLS_TL_NO_DUPLICATE_KEY is specified, then + * this function will ensure that no certificates with the + * same key are present in the final trust list. + * + * If either %GNUTLS_TL_NO_DUPLICATE_KEY or %GNUTLS_TL_NO_DUPLICATES + * are given, gnutls_x509_trust_list_deinit() must be called with parameter + * @all being 1. + * + * Returns: The number of added elements is returned; that includes + * duplicate entries. + * + * Since: 3.0.0 + **/ +int +gnutls_x509_trust_list_add_cas(gnutls_x509_trust_list_t list, + const gnutls_x509_crt_t * clist, + unsigned clist_size, unsigned int flags) +{ + unsigned i, j; + size_t hash; + int ret; + unsigned exists; + + for (i = 0; i < clist_size; i++) { + exists = 0; + hash = + hash_pjw_bare(clist[i]->raw_dn.data, + clist[i]->raw_dn.size); + hash %= list->size; + + /* avoid duplicates */ + if (flags & GNUTLS_TL_NO_DUPLICATES || flags & GNUTLS_TL_NO_DUPLICATE_KEY) { + for (j=0;j<list->node[hash].trusted_ca_size;j++) { + if (flags & GNUTLS_TL_NO_DUPLICATES) + ret = gnutls_x509_crt_equals(list->node[hash].trusted_cas[j], clist[i]); + else + ret = _gnutls_check_if_same_key(list->node[hash].trusted_cas[j], clist[i], 1); + if (ret != 0) { + exists = 1; + break; + } + } + + if (exists != 0) { + gnutls_x509_crt_deinit(list->node[hash].trusted_cas[j]); + list->node[hash].trusted_cas[j] = clist[i]; + continue; + } + } + + if (unlikely(INT_ADD_OVERFLOW(list->node[hash].trusted_ca_size, 1))) { + gnutls_assert(); + return i; + } + + list->node[hash].trusted_cas = + _gnutls_reallocarray_fast(list->node[hash].trusted_cas, + list->node[hash].trusted_ca_size + 1, + sizeof(list->node[hash].trusted_cas[0])); + if (list->node[hash].trusted_cas == NULL) { + gnutls_assert(); + return i; + } + + if (gnutls_x509_crt_get_version(clist[i]) >= 3 && + gnutls_x509_crt_get_ca_status(clist[i], NULL) <= 0) { + gnutls_datum_t dn; + gnutls_assert(); + if (gnutls_x509_crt_get_dn2(clist[i], &dn) >= 0) { + _gnutls_audit_log(NULL, + "There was a non-CA certificate in the trusted list: %s.\n", + dn.data); + gnutls_free(dn.data); + } + } + + list->node[hash].trusted_cas[list->node[hash]. + trusted_ca_size] = clist[i]; + list->node[hash].trusted_ca_size++; + + if (flags & GNUTLS_TL_USE_IN_TLS) { + ret = add_new_ca_to_rdn_seq(list, clist[i]); + if (ret < 0) { + gnutls_assert(); + return i+1; + } + } + } + + return i; +} + +static int +advance_iter(gnutls_x509_trust_list_t list, + gnutls_x509_trust_list_iter_t iter) +{ + if (iter->node_index < list->size) { + ++iter->ca_index; + + /* skip entries */ + while (iter->node_index < list->size && + iter->ca_index >= list->node[iter->node_index].trusted_ca_size) { + ++iter->node_index; + iter->ca_index = 0; + } + + if (iter->node_index < list->size) + return 0; + } + +#ifdef ENABLE_PKCS11 + if (list->pkcs11_token != NULL) { + if (iter->pkcs11_list == NULL) { + int ret = gnutls_pkcs11_obj_list_import_url2(&iter->pkcs11_list, &iter->pkcs11_size, + list->pkcs11_token, (GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|GNUTLS_PKCS11_OBJ_FLAG_CRT|GNUTLS_PKCS11_OBJ_FLAG_MARK_CA|GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED), 0); + if (ret < 0) + return gnutls_assert_val(ret); + + if (iter->pkcs11_size > 0) + return 0; + } else if (iter->pkcs11_index < iter->pkcs11_size) { + ++iter->pkcs11_index; + if (iter->pkcs11_index < iter->pkcs11_size) + return 0; + } + } +#endif + + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); +} + +/** + * gnutls_x509_trust_list_iter_get_ca: + * @list: The list + * @iter: A pointer to an iterator (initially the iterator should be %NULL) + * @crt: where the certificate will be copied + * + * This function obtains a certificate in the trust list and advances the + * iterator to the next certificate. The certificate returned in @crt must be + * deallocated with gnutls_x509_crt_deinit(). + * + * When past the last element is accessed %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * is returned and the iterator is reset. + * + * The iterator is deinitialized and reset to %NULL automatically by this + * function after iterating through all elements until + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. If the iteration is + * aborted early, it must be manually deinitialized using + * gnutls_x509_trust_list_iter_deinit(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.0 + **/ +int +gnutls_x509_trust_list_iter_get_ca(gnutls_x509_trust_list_t list, + gnutls_x509_trust_list_iter_t *iter, + gnutls_x509_crt_t *crt) +{ + int ret; + + /* initialize iterator */ + if (*iter == NULL) { + *iter = gnutls_malloc(sizeof (struct gnutls_x509_trust_list_iter)); + if (*iter == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + (*iter)->node_index = 0; + (*iter)->ca_index = 0; + +#ifdef ENABLE_PKCS11 + (*iter)->pkcs11_list = NULL; + (*iter)->pkcs11_size = 0; + (*iter)->pkcs11_index = 0; +#endif + + /* Advance iterator to the first valid entry */ + if (list->node[0].trusted_ca_size == 0) { + ret = advance_iter(list, *iter); + if (ret != 0) { + gnutls_x509_trust_list_iter_deinit(*iter); + *iter = NULL; + + *crt = NULL; + return gnutls_assert_val(ret); + } + } + } + + /* obtain the certificate at the current iterator position */ + if ((*iter)->node_index < list->size) { + ret = gnutls_x509_crt_init(crt); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_x509_crt_cpy(*crt, list->node[(*iter)->node_index].trusted_cas[(*iter)->ca_index]); + if (ret < 0) { + gnutls_x509_crt_deinit(*crt); + return gnutls_assert_val(ret); + } + } +#ifdef ENABLE_PKCS11 + else if ( (*iter)->pkcs11_index < (*iter)->pkcs11_size) { + ret = gnutls_x509_crt_init(crt); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_x509_crt_import_pkcs11(*crt, (*iter)->pkcs11_list[(*iter)->pkcs11_index]); + if (ret < 0) { + gnutls_x509_crt_deinit(*crt); + return gnutls_assert_val(ret); + } + } +#endif + + else { + /* iterator is at end */ + gnutls_x509_trust_list_iter_deinit(*iter); + *iter = NULL; + + *crt = NULL; + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + } + + /* Move iterator to the next position. + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned if the iterator + * has been moved to the end position. That is okay, we return the + * certificate that we read and when this function is called again we + * report GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE to our caller. */ + ret = advance_iter(list, *iter); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_x509_crt_deinit(*crt); + *crt = NULL; + + return gnutls_assert_val(ret); + } + + return 0; +} + +/** + * gnutls_x509_trust_list_iter_deinit: + * @iter: The iterator structure to be deinitialized + * + * This function will deinitialize an iterator structure. + * + * Since: 3.4.0 + **/ +void gnutls_x509_trust_list_iter_deinit(gnutls_x509_trust_list_iter_t iter) +{ + if (!iter) + return; + +#ifdef ENABLE_PKCS11 + if (iter->pkcs11_size > 0) { + unsigned i; + for (i = 0; i < iter->pkcs11_size; ++i) + gnutls_pkcs11_obj_deinit(iter->pkcs11_list[i]); + gnutls_free(iter->pkcs11_list); + } +#endif + + gnutls_free(iter); +} + +static gnutls_x509_crt_t crt_cpy(gnutls_x509_crt_t src) +{ +gnutls_x509_crt_t dst; +int ret; + + ret = gnutls_x509_crt_init(&dst); + if (ret < 0) { + gnutls_assert(); + return NULL; + } + + ret = _gnutls_x509_crt_cpy(dst, src); + if (ret < 0) { + gnutls_x509_crt_deinit(dst); + gnutls_assert(); + return NULL; + } + + return dst; +} + +/** + * gnutls_x509_trust_list_remove_cas: + * @list: The list + * @clist: A list of CAs + * @clist_size: The length of the CA list + * + * This function will remove the given certificate authorities + * from the trusted list. + * + * Note that this function can accept certificates and authorities + * not yet known. In that case they will be kept in a separate + * black list that will be used during certificate verification. + * Unlike gnutls_x509_trust_list_add_cas() there is no deinitialization + * restriction for certificate list provided in this function. + * + * Returns: The number of removed elements is returned. + * + * Since: 3.1.10 + **/ +int +gnutls_x509_trust_list_remove_cas(gnutls_x509_trust_list_t list, + const gnutls_x509_crt_t * clist, + unsigned clist_size) +{ + int r = 0; + unsigned j, i; + size_t hash; + + for (i = 0; i < clist_size; i++) { + hash = + hash_pjw_bare(clist[i]->raw_dn.data, + clist[i]->raw_dn.size); + hash %= list->size; + + for (j = 0; j < list->node[hash].trusted_ca_size; j++) { + if (gnutls_x509_crt_equals + (clist[i], + list->node[hash].trusted_cas[j]) != 0) { + + gnutls_x509_crt_deinit(list->node[hash]. + trusted_cas[j]); + list->node[hash].trusted_cas[j] = + list->node[hash].trusted_cas[list-> + node + [hash]. + trusted_ca_size + - 1]; + list->node[hash].trusted_ca_size--; + r++; + break; + } + } + + if (unlikely(INT_ADD_OVERFLOW(list->blacklisted_size, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + /* Add the CA (or plain) certificate to the black list as well. + * This will prevent a subordinate CA from being valid, and + * ensure that a server certificate will also get rejected. + */ + list->blacklisted = + _gnutls_reallocarray_fast(list->blacklisted, + list->blacklisted_size + 1, + sizeof(list->blacklisted[0])); + if (list->blacklisted == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + list->blacklisted[list->blacklisted_size] = crt_cpy(clist[i]); + if (list->blacklisted[list->blacklisted_size] != NULL) + list->blacklisted_size++; + } + + return r; +} + +/** + * gnutls_x509_trust_list_add_named_crt: + * @list: The list + * @cert: A certificate + * @name: An identifier for the certificate + * @name_size: The size of the identifier + * @flags: should be 0. + * + * This function will add the given certificate to the trusted + * list and associate it with a name. The certificate will not be + * be used for verification with gnutls_x509_trust_list_verify_crt() + * but with gnutls_x509_trust_list_verify_named_crt() or + * gnutls_x509_trust_list_verify_crt2() - the latter only since + * GnuTLS 3.4.0 and if a hostname is provided. + * + * In principle this function can be used to set individual "server" + * certificates that are trusted by the user for that specific server + * but for no other purposes. + * + * The certificate @cert must not be deinitialized during the lifetime + * of the @list. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0.0 + **/ +int +gnutls_x509_trust_list_add_named_crt(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert, + const void *name, size_t name_size, + unsigned int flags) +{ + size_t hash; + + if (name_size >= MAX_SERVER_NAME_SIZE) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + hash = + hash_pjw_bare(cert->raw_issuer_dn.data, + cert->raw_issuer_dn.size); + hash %= list->size; + + if (unlikely(INT_ADD_OVERFLOW(list->node[hash].named_cert_size, 1))) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + list->node[hash].named_certs = + _gnutls_reallocarray_fast(list->node[hash].named_certs, + list->node[hash].named_cert_size + 1, + sizeof(list->node[hash].named_certs[0])); + if (list->node[hash].named_certs == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + list->node[hash].named_certs[list->node[hash].named_cert_size]. + cert = cert; + memcpy(list->node[hash]. + named_certs[list->node[hash].named_cert_size].name, name, + name_size); + list->node[hash].named_certs[list->node[hash]. + named_cert_size].name_size = + name_size; + + list->node[hash].named_cert_size++; + + return 0; +} + +/** + * gnutls_x509_trust_list_add_crls: + * @list: The list + * @crl_list: A list of CRLs + * @crl_size: The length of the CRL list + * @flags: flags from %gnutls_trust_list_flags_t + * @verification_flags: gnutls_certificate_verify_flags if flags specifies GNUTLS_TL_VERIFY_CRL + * + * This function will add the given certificate revocation lists + * to the trusted list. The CRLs in @crl_list must not be deinitialized + * during the lifetime of @list. + * + * This function must be called after gnutls_x509_trust_list_add_cas() + * to allow verifying the CRLs for validity. If the flag %GNUTLS_TL_NO_DUPLICATES + * is given, then the final CRL list will not contain duplicate entries. + * + * If the flag %GNUTLS_TL_NO_DUPLICATES is given, gnutls_x509_trust_list_deinit() must be + * called with parameter @all being 1. + * + * If flag %GNUTLS_TL_VERIFY_CRL is given the CRLs will be verified before being added, + * and if verification fails, they will be skipped. + * + * Returns: The number of added elements is returned; that includes + * duplicate entries. + * + * Since: 3.0 + **/ +int +gnutls_x509_trust_list_add_crls(gnutls_x509_trust_list_t list, + const gnutls_x509_crl_t * crl_list, + unsigned crl_size, unsigned int flags, + unsigned int verification_flags) +{ + int ret; + unsigned x, i, j = 0; + unsigned int vret = 0; + size_t hash; + gnutls_x509_crl_t *tmp; + + /* Probably we can optimize things such as removing duplicates + * etc. + */ + if (crl_size == 0 || crl_list == NULL) + return 0; + + for (i = 0; i < crl_size; i++) { + hash = + hash_pjw_bare(crl_list[i]->raw_issuer_dn.data, + crl_list[i]->raw_issuer_dn.size); + hash %= list->size; + + if (flags & GNUTLS_TL_VERIFY_CRL) { + + ret = + gnutls_x509_crl_verify(crl_list[i], + list->node[hash]. + trusted_cas, + list->node[hash]. + trusted_ca_size, + verification_flags, + &vret); + if (ret < 0 || vret != 0) { + _gnutls_debug_log("CRL verification failed, not adding it\n"); + if (flags & GNUTLS_TL_NO_DUPLICATES) + gnutls_x509_crl_deinit(crl_list[i]); + if (flags & GNUTLS_TL_FAIL_ON_INVALID_CRL) + return gnutls_assert_val(GNUTLS_E_CRL_VERIFICATION_ERROR); + continue; + } + } + + /* If the CRL added overrides a previous one, then overwrite + * the old one */ + if (flags & GNUTLS_TL_NO_DUPLICATES) { + for (x=0;x<list->node[hash].crl_size;x++) { + if (crl_list[i]->raw_issuer_dn.size == list->node[hash].crls[x]->raw_issuer_dn.size && + memcmp(crl_list[i]->raw_issuer_dn.data, list->node[hash].crls[x]->raw_issuer_dn.data, crl_list[i]->raw_issuer_dn.size) == 0) { + if (gnutls_x509_crl_get_this_update(crl_list[i]) >= + gnutls_x509_crl_get_this_update(list->node[hash].crls[x])) { + + gnutls_x509_crl_deinit(list->node[hash].crls[x]); + list->node[hash].crls[x] = crl_list[i]; + goto next; + } else { + /* The new is older, discard it */ + gnutls_x509_crl_deinit(crl_list[i]); + goto next; + } + } + } + } + + if (unlikely(INT_ADD_OVERFLOW(list->node[hash].crl_size, 1))) { + gnutls_assert(); + goto error; + } + + tmp = _gnutls_reallocarray(list->node[hash].crls, + list->node[hash].crl_size + 1, + sizeof(list->node[hash].crls[0])); + if (tmp == NULL) { + gnutls_assert(); + goto error; + } + list->node[hash].crls = tmp; + + + list->node[hash].crls[list->node[hash].crl_size] = + crl_list[i]; + list->node[hash].crl_size++; + + next: + j++; + } + + return j; + + error: + ret = i; + if (flags & GNUTLS_TL_NO_DUPLICATES) + while (i < crl_size) + gnutls_x509_crl_deinit(crl_list[i++]); + return ret; +} + +/* Takes a certificate list and shortens it if there are + * intermedia certificates already trusted by us. + * + * Returns the new size of the list or a negative number on error. + */ +static int shorten_clist(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t * certificate_list, + unsigned int clist_size) +{ + unsigned int j, i; + size_t hash; + + if (clist_size > 1) { + /* Check if the last certificate in the path is self signed. + * In that case ignore it (a certificate is trusted only if it + * leads to a trusted party by us, not the server's). + * + * This prevents from verifying self signed certificates against + * themselves. This (although not bad) caused verification + * failures on some root self signed certificates that use the + * MD2 algorithm. + */ + if (gnutls_x509_crt_check_issuer + (certificate_list[clist_size - 1], + certificate_list[clist_size - 1]) != 0) { + clist_size--; + } + } + + /* We want to shorten the chain by removing the cert that matches + * one of the certs we trust and all the certs after that i.e. if + * cert chain is A signed-by B signed-by C signed-by D (signed-by + * self-signed E but already removed above), and we trust B, remove + * B, C and D. */ + for (i = 1; i < clist_size; i++) { + hash = + hash_pjw_bare(certificate_list[i]->raw_issuer_dn.data, + certificate_list[i]->raw_issuer_dn.size); + hash %= list->size; + + for (j = 0; j < list->node[hash].trusted_ca_size; j++) { + if (gnutls_x509_crt_equals + (certificate_list[i], + list->node[hash].trusted_cas[j]) != 0) { + /* cut the list at the point of first the trusted certificate */ + clist_size = i + 1; + break; + } + } + /* clist_size may have been changed which gets out of loop */ + } + + return clist_size; +} + +/* Takes a subject certificate, retrieves a chain from its issuers in + * @certificate_list, using the issuer callback set for @list. + * + * Returns the new size of the list or a negative number on error. + */ +static int +retrieve_issuers(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t subject, + gnutls_x509_crt_t *certificate_list, + unsigned int clist_size_max) +{ + gnutls_x509_crt_t *issuers; + unsigned int issuers_size; + unsigned int i; + int ret; + + if (!list->issuer_callback) { + return 0; + } + + _gnutls_cert_log("calling issuer callback on", subject); + + ret = list->issuer_callback(list, subject, &issuers, &issuers_size); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + /* Ignore empty list */ + if (!issuers_size) { + ret = 0; + goto cleanup; + } + + if (issuers_size > clist_size_max) { + _gnutls_debug_log("too many issuers returned; skipping\n"); + ret = 0; + goto cleanup; + } + + for (i = 0; i < issuers_size; i++) { + if (!gnutls_x509_crt_check_issuer(subject, issuers[i])) { + _gnutls_cert_log("unrelated certificate; skipping", + issuers[i]); + break; + } + subject = issuers[i]; + } + + ret = i; + + memcpy(certificate_list, issuers, ret * sizeof(gnutls_x509_crt_t)); + + cleanup: + for (i = ret; i < issuers_size; i++) { + gnutls_x509_crt_deinit(issuers[i]); + } + gnutls_free(issuers); + + return ret; +} + +int _gnutls_trust_list_get_issuer(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert, + gnutls_x509_crt_t * issuer, + unsigned int flags) +{ + int ret; + unsigned int i; + size_t hash; + + hash = + hash_pjw_bare(cert->raw_issuer_dn.data, + cert->raw_issuer_dn.size); + hash %= list->size; + + for (i = 0; i < list->node[hash].trusted_ca_size; i++) { + ret = + gnutls_x509_crt_check_issuer(cert, + list->node[hash]. + trusted_cas[i]); + if (ret != 0) { + if (flags & GNUTLS_TL_GET_COPY) { + *issuer = crt_cpy(list->node[hash].trusted_cas[i]); + } else { + *issuer = list->node[hash].trusted_cas[i]; + } + return 0; + } + } + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; +} + +static +int trust_list_get_issuer_by_dn(gnutls_x509_trust_list_t list, + const gnutls_datum_t *dn, + const gnutls_datum_t *spki, + gnutls_x509_crt_t * issuer, + unsigned int flags) +{ + int ret; + unsigned int i, j; + size_t hash; + uint8_t tmp[256]; + size_t tmp_size; + + if (dn) { + hash = + hash_pjw_bare(dn->data, + dn->size); + hash %= list->size; + + for (i = 0; i < list->node[hash].trusted_ca_size; i++) { + ret = _gnutls_x509_compare_raw_dn(dn, &list->node[hash].trusted_cas[i]->raw_dn); + if (ret != 0) { + if (spki && spki->size > 0) { + tmp_size = sizeof(tmp); + + ret = gnutls_x509_crt_get_subject_key_id(list->node[hash].trusted_cas[i], tmp, &tmp_size, NULL); + if (ret < 0) + continue; + if (spki->size != tmp_size || memcmp(spki->data, tmp, spki->size) != 0) + continue; + } + *issuer = crt_cpy(list->node[hash].trusted_cas[i]); + return 0; + } + } + } else if (spki) { + /* search everything! */ + for (i = 0; i < list->size; i++) { + for (j = 0; j < list->node[i].trusted_ca_size; j++) { + tmp_size = sizeof(tmp); + + ret = gnutls_x509_crt_get_subject_key_id(list->node[i].trusted_cas[j], tmp, &tmp_size, NULL); + if (ret < 0) + continue; + + if (spki->size != tmp_size || memcmp(spki->data, tmp, spki->size) != 0) + continue; + + *issuer = crt_cpy(list->node[i].trusted_cas[j]); + return 0; + } + } + } + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; +} + +/** + * gnutls_x509_trust_list_get_issuer: + * @list: The list + * @cert: is the certificate to find issuer for + * @issuer: Will hold the issuer if any. Should be treated as constant + * unless %GNUTLS_TL_GET_COPY is set in @flags. + * @flags: flags from %gnutls_trust_list_flags_t (%GNUTLS_TL_GET_COPY is applicable) + * + * This function will find the issuer of the given certificate. + * If the flag %GNUTLS_TL_GET_COPY is specified a copy of the issuer + * will be returned which must be freed using gnutls_x509_crt_deinit(). + * In that case the provided @issuer must not be initialized. + * + * Note that the flag %GNUTLS_TL_GET_COPY is required for this function + * to work with PKCS#11 trust lists in a thread-safe way. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0 + **/ +int gnutls_x509_trust_list_get_issuer(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert, + gnutls_x509_crt_t * issuer, + unsigned int flags) +{ + int ret; + + ret = _gnutls_trust_list_get_issuer(list, cert, issuer, flags); + if (ret == 0) { + return 0; + } + +#ifdef ENABLE_PKCS11 + if (ret < 0 && list->pkcs11_token) { + gnutls_x509_crt_t crt; + gnutls_datum_t der = {NULL, 0}; + /* use the token for verification */ + ret = gnutls_pkcs11_get_raw_issuer(list->pkcs11_token, cert, &der, + GNUTLS_X509_FMT_DER, GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) { + gnutls_free(der.data); + return gnutls_assert_val(ret); + } + + ret = gnutls_x509_crt_import(crt, &der, GNUTLS_X509_FMT_DER); + gnutls_free(der.data); + if (ret < 0) { + gnutls_x509_crt_deinit(crt); + return gnutls_assert_val(ret); + } + + if (flags & GNUTLS_TL_GET_COPY) { + *issuer = crt; + return 0; + } else { + /* we add this CA to the keep_cert list in order to make it + * persistent. It will be deallocated when the trust list is. + */ + ret = trust_list_add_compat(list, crt); + if (ret < 0) { + gnutls_x509_crt_deinit(crt); + return gnutls_assert_val(ret); + } + *issuer = crt; + return ret; + } + } +#endif + return ret; +} + +/** + * gnutls_x509_trust_list_get_issuer_by_dn: + * @list: The list + * @dn: is the issuer's DN + * @issuer: Will hold the issuer if any. Should be deallocated after use. + * @flags: Use zero + * + * This function will find the issuer with the given name, and + * return a copy of the issuer, which must be freed using gnutls_x509_crt_deinit(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.0 + **/ +int gnutls_x509_trust_list_get_issuer_by_dn(gnutls_x509_trust_list_t list, + const gnutls_datum_t *dn, + gnutls_x509_crt_t *issuer, + unsigned int flags) +{ + int ret; + + ret = trust_list_get_issuer_by_dn(list, dn, NULL, issuer, flags); + if (ret == 0) { + return 0; + } + +#ifdef ENABLE_PKCS11 + if (ret < 0 && list->pkcs11_token) { + gnutls_x509_crt_t crt; + gnutls_datum_t der = {NULL, 0}; + /* use the token for verification */ + ret = gnutls_pkcs11_get_raw_issuer_by_dn(list->pkcs11_token, dn, &der, + GNUTLS_X509_FMT_DER, GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) { + gnutls_free(der.data); + return gnutls_assert_val(ret); + } + + ret = gnutls_x509_crt_import(crt, &der, GNUTLS_X509_FMT_DER); + gnutls_free(der.data); + if (ret < 0) { + gnutls_x509_crt_deinit(crt); + return gnutls_assert_val(ret); + } + + *issuer = crt; + return 0; + } +#endif + return ret; +} + +/** + * gnutls_x509_trust_list_get_issuer_by_subject_key_id: + * @list: The list + * @dn: is the issuer's DN (may be %NULL) + * @spki: is the subject key ID + * @issuer: Will hold the issuer if any. Should be deallocated after use. + * @flags: Use zero + * + * This function will find the issuer with the given name and subject key ID, and + * return a copy of the issuer, which must be freed using gnutls_x509_crt_deinit(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.2 + **/ +int gnutls_x509_trust_list_get_issuer_by_subject_key_id(gnutls_x509_trust_list_t list, + const gnutls_datum_t *dn, + const gnutls_datum_t *spki, + gnutls_x509_crt_t *issuer, + unsigned int flags) +{ + int ret; + + ret = trust_list_get_issuer_by_dn(list, dn, spki, issuer, flags); + if (ret == 0) { + return 0; + } + +#ifdef ENABLE_PKCS11 + if (ret < 0 && list->pkcs11_token) { + gnutls_x509_crt_t crt; + gnutls_datum_t der = {NULL, 0}; + /* use the token for verification */ + ret = gnutls_pkcs11_get_raw_issuer_by_subject_key_id(list->pkcs11_token, dn, spki, &der, + GNUTLS_X509_FMT_DER, GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) { + gnutls_free(der.data); + return gnutls_assert_val(ret); + } + + ret = gnutls_x509_crt_import(crt, &der, GNUTLS_X509_FMT_DER); + gnutls_free(der.data); + if (ret < 0) { + gnutls_x509_crt_deinit(crt); + return gnutls_assert_val(ret); + } + + *issuer = crt; + return 0; + } +#endif + return ret; +} + +static +int check_if_in_blacklist(gnutls_x509_crt_t * cert_list, unsigned int cert_list_size, + gnutls_x509_crt_t * blacklist, unsigned int blacklist_size) +{ +unsigned i, j; + + if (blacklist_size == 0) + return 0; + + for (i=0;i<cert_list_size;i++) { + for (j=0;j<blacklist_size;j++) { + if (gnutls_x509_crt_equals(cert_list[i], blacklist[j]) != 0) { + return 1; + } + } + } + + return 0; +} + +/** + * gnutls_x509_trust_list_verify_crt: + * @list: The list + * @cert_list: is the certificate list to be verified + * @cert_list_size: is the certificate list size + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @voutput: will hold the certificate verification output. + * @func: If non-null will be called on each chain element verification with the output. + * + * This function will try to verify the given certificate and return + * its status. The @voutput parameter will hold an OR'ed sequence of + * %gnutls_certificate_status_t flags. + * + * The details of the verification are the same as in gnutls_x509_trust_list_verify_crt2(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0 + **/ +int +gnutls_x509_trust_list_verify_crt(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t * cert_list, + unsigned int cert_list_size, + unsigned int flags, + unsigned int *voutput, + gnutls_verify_output_function func) +{ + return gnutls_x509_trust_list_verify_crt2(list, cert_list, cert_list_size, + NULL, 0, flags, voutput, func); +} + +#define LAST_DN cert_list[cert_list_size-1]->raw_dn +#define LAST_IDN cert_list[cert_list_size-1]->raw_issuer_dn +/* This macro is introduced to detect a verification output which + * indicates an unknown signer, a signer which uses an insecure + * algorithm (e.g., sha1), a signer has expired, or something that + * indicates a superseded signer */ +#define SIGNER_OLD_OR_UNKNOWN(output) ((output & GNUTLS_CERT_SIGNER_NOT_FOUND) || \ + (output & GNUTLS_CERT_EXPIRED) || \ + (output & GNUTLS_CERT_INSECURE_ALGORITHM)) +#define SIGNER_WAS_KNOWN(output) (!(output & GNUTLS_CERT_SIGNER_NOT_FOUND)) + +/** + * gnutls_x509_trust_list_verify_crt2: + * @list: The list + * @cert_list: is the certificate list to be verified + * @cert_list_size: is the certificate list size + * @data: an array of typed data + * @elements: the number of data elements + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @voutput: will hold the certificate verification output. + * @func: If non-null will be called on each chain element verification with the output. + * + * This function will attempt to verify the given certificate chain and return + * its status. The @voutput parameter will hold an OR'ed sequence of + * %gnutls_certificate_status_t flags. + * + * When a certificate chain of @cert_list_size with more than one certificates is + * provided, the verification status will apply to the first certificate in the chain + * that failed verification. The verification process starts from the end of the chain + * (from CA to end certificate). The first certificate in the chain must be the end-certificate + * while the rest of the members may be sorted or not. + * + * Additionally a certificate verification profile can be specified + * from the ones in %gnutls_certificate_verification_profiles_t by + * ORing the result of GNUTLS_PROFILE_TO_VFLAGS() to the verification + * flags. + * + * Additional verification parameters are possible via the @data types; the + * acceptable types are %GNUTLS_DT_DNS_HOSTNAME, %GNUTLS_DT_IP_ADDRESS and %GNUTLS_DT_KEY_PURPOSE_OID. + * The former accepts as data a null-terminated hostname, and the latter a null-terminated + * object identifier (e.g., %GNUTLS_KP_TLS_WWW_SERVER). + * If a DNS hostname is provided then this function will compare + * the hostname in the end certificate against the given. If names do not match the + * %GNUTLS_CERT_UNEXPECTED_OWNER status flag will be set. In addition it + * will consider certificates provided with gnutls_x509_trust_list_add_named_crt(). + * + * If a key purpose OID is provided and the end-certificate contains the extended key + * usage PKIX extension, it will be required to match the provided OID + * or be marked for any purpose, otherwise verification will fail with + * %GNUTLS_CERT_PURPOSE_MISMATCH status. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. Note that verification failure will not result to an + * error code, only @voutput will be updated. + * + * Since: 3.3.8 + **/ +int +gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t * cert_list, + unsigned int cert_list_size, + gnutls_typed_vdata_st *data, + unsigned int elements, + unsigned int flags, + unsigned int *voutput, + gnutls_verify_output_function func) +{ + int ret = 0; + unsigned int i; + size_t hash; + gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH]; + gnutls_x509_crt_t retrieved[DEFAULT_MAX_VERIFY_DEPTH]; + unsigned int retrieved_size = 0; + const char *hostname = NULL, *purpose = NULL, *email = NULL; + unsigned hostname_size = 0; + unsigned have_set_name = 0; + unsigned saved_output; + gnutls_datum_t ip = {NULL, 0}; + struct cert_set_st cert_set = { NULL, 0 }; + + if (cert_list == NULL || cert_list_size < 1) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + for (i=0;i<elements;i++) { + if (data[i].type == GNUTLS_DT_DNS_HOSTNAME) { + hostname = (void*)data[i].data; + if (data[i].size > 0) { + hostname_size = data[i].size; + } + + if (have_set_name != 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + have_set_name = 1; + } else if (data[i].type == GNUTLS_DT_IP_ADDRESS) { + if (data[i].size > 0) { + ip.data = data[i].data; + ip.size = data[i].size; + } + + if (have_set_name != 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + have_set_name = 1; + } else if (data[i].type == GNUTLS_DT_RFC822NAME) { + email = (void*)data[i].data; + + if (have_set_name != 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + have_set_name = 1; + } else if (data[i].type == GNUTLS_DT_KEY_PURPOSE_OID) { + purpose = (void*)data[i].data; + } + } + + if (hostname) { /* shortcut using the named certs - if any */ + unsigned vtmp = 0; + if (hostname_size == 0) + hostname_size = strlen(hostname); + + ret = gnutls_x509_trust_list_verify_named_crt(list, + cert_list[0], hostname, hostname_size, + flags, &vtmp, func); + if (ret == 0 && vtmp == 0) { + *voutput = vtmp; + return 0; + } + } + + memcpy(sorted, cert_list, cert_list_size * sizeof(gnutls_x509_crt_t)); + cert_list = sorted; + + ret = cert_set_init(&cert_set, DEFAULT_MAX_VERIFY_DEPTH); + if (ret < 0) { + return ret; + } + + for (i = 0; i < cert_list_size && + cert_list_size <= DEFAULT_MAX_VERIFY_DEPTH; ) { + unsigned int sorted_size = 1; + unsigned int j; + gnutls_x509_crt_t issuer; + + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_UNSORTED_CHAIN)) { + sorted_size = _gnutls_sort_clist(&cert_list[i], + cert_list_size - i); + } + + /* Remove duplicates. Start with index 1, as the first element + * may be re-checked after issuer retrieval. */ + for (j = 1; j < sorted_size; j++) { + if (cert_set_contains(&cert_set, cert_list[i + j])) { + if (i + j < cert_list_size - 1) { + memmove(&cert_list[i + j], + &cert_list[i + j + 1], + sizeof(cert_list[i])); + } + cert_list_size--; + break; + } + } + /* Found a duplicate, try again with the same index. */ + if (j < sorted_size) { + continue; + } + + /* Record the certificates seen. */ + for (j = 0; j < sorted_size; j++, i++) { + ret = cert_set_add(&cert_set, cert_list[i]); + if (ret < 0) { + goto cleanup; + } + } + + /* If the issuer of the certificate is known, no need + * for further processing. */ + if (gnutls_x509_trust_list_get_issuer(list, + cert_list[i - 1], + &issuer, + GNUTLS_TL_GET_COPY) == 0) { + gnutls_x509_crt_deinit(issuer); + cert_list_size = i; + break; + } + + /* If there is no gap between this and the next certificate, + * proceed with the next certificate. */ + if (i < cert_list_size && + gnutls_x509_crt_check_issuer(cert_list[i - 1], + cert_list[i])) { + continue; + } + + ret = retrieve_issuers(list, + cert_list[i - 1], + &retrieved[retrieved_size], + DEFAULT_MAX_VERIFY_DEPTH - + MAX(retrieved_size, + cert_list_size)); + if (ret < 0) { + break; + } else if (ret > 0) { + assert((unsigned int)ret <= + DEFAULT_MAX_VERIFY_DEPTH - cert_list_size); + memmove(&cert_list[i + ret], + &cert_list[i], + (cert_list_size - i) * + sizeof(gnutls_x509_crt_t)); + memcpy(&cert_list[i], + &retrieved[retrieved_size], + ret * sizeof(gnutls_x509_crt_t)); + retrieved_size += ret; + cert_list_size += ret; + + /* Start again from the end of the previous segment. */ + i--; + } + } + + cert_list_size = shorten_clist(list, cert_list, cert_list_size); + if (cert_list_size <= 0) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + hash = + hash_pjw_bare(cert_list[cert_list_size - 1]->raw_issuer_dn. + data, + cert_list[cert_list_size - + 1]->raw_issuer_dn.size); + hash %= list->size; + + ret = check_if_in_blacklist(cert_list, cert_list_size, + list->blacklisted, list->blacklisted_size); + if (ret != 0) { + *voutput = 0; + *voutput |= GNUTLS_CERT_REVOKED; + *voutput |= GNUTLS_CERT_INVALID; + ret = 0; + goto cleanup; + } + + *voutput = + _gnutls_verify_crt_status(list, cert_list, cert_list_size, + list->node[hash].trusted_cas, + list->node[hash].trusted_ca_size, + flags, purpose, func); + saved_output = *voutput; + + if (SIGNER_OLD_OR_UNKNOWN(*voutput) && + (LAST_DN.size != LAST_IDN.size || + memcmp(LAST_DN.data, LAST_IDN.data, LAST_IDN.size) != 0)) { + + /* if we couldn't find the issuer, try to see if the last + * certificate is in the trusted list and try to verify against + * (if it is not self signed) */ + hash = + hash_pjw_bare(cert_list[cert_list_size - 1]->raw_dn. + data, cert_list[cert_list_size - 1]->raw_dn.size); + hash %= list->size; + + _gnutls_debug_log("issuer in verification was not found or insecure; trying against trust list\n"); + + *voutput = + _gnutls_verify_crt_status(list, cert_list, cert_list_size, + list->node[hash].trusted_cas, + list->node[hash].trusted_ca_size, + flags, purpose, func); + if (*voutput != 0) { + if (SIGNER_WAS_KNOWN(saved_output)) + *voutput = saved_output; + gnutls_assert(); + } + } + + saved_output = *voutput; + +#ifdef ENABLE_PKCS11 + if (SIGNER_OLD_OR_UNKNOWN(*voutput) && list->pkcs11_token) { + /* use the token for verification */ + + *voutput = _gnutls_pkcs11_verify_crt_status(list, list->pkcs11_token, + cert_list, cert_list_size, + purpose, + flags, func); + if (*voutput != 0) { + if (SIGNER_WAS_KNOWN(saved_output)) + *voutput = saved_output; + gnutls_assert(); + } + } +#endif + + /* End-certificate, key purpose and hostname checks. */ + if (purpose) { + ret = _gnutls_check_key_purpose(cert_list[0], purpose, 0); + if (ret != 1) { + gnutls_assert(); + *voutput |= GNUTLS_CERT_PURPOSE_MISMATCH|GNUTLS_CERT_INVALID; + } + } + + if (hostname) { + ret = + gnutls_x509_crt_check_hostname2(cert_list[0], hostname, flags); + if (ret == 0) { + gnutls_assert(); + *voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID; + } + } + + if (ip.data) { + ret = + gnutls_x509_crt_check_ip(cert_list[0], ip.data, ip.size, flags); + if (ret == 0) { + gnutls_assert(); + *voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID; + } + } + + if (email) { + ret = + gnutls_x509_crt_check_email(cert_list[0], email, 0); + if (ret == 0) { + gnutls_assert(); + *voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID; + } + } + + /* CRL checks follow */ + + if (*voutput != 0 || (flags & GNUTLS_VERIFY_DISABLE_CRL_CHECKS)) { + ret = 0; + goto cleanup; + } + + /* Check revocation of individual certificates. + * start with the last one that we already have its hash + */ + ret = + _gnutls_x509_crt_check_revocation(cert_list + [cert_list_size - 1], + list->node[hash].crls, + list->node[hash].crl_size, + func); + if (ret == 1) { /* revoked */ + *voutput |= GNUTLS_CERT_REVOKED; + *voutput |= GNUTLS_CERT_INVALID; + ret = 0; + goto cleanup; + } + + for (i = 0; i < cert_list_size - 1; i++) { + hash = + hash_pjw_bare(cert_list[i]->raw_issuer_dn.data, + cert_list[i]->raw_issuer_dn.size); + hash %= list->size; + + ret = _gnutls_x509_crt_check_revocation(cert_list[i], + list->node[hash]. + crls, + list->node[hash]. + crl_size, func); + if (ret < 0) { + gnutls_assert(); + } else if (ret == 1) { /* revoked */ + *voutput |= GNUTLS_CERT_REVOKED; + *voutput |= GNUTLS_CERT_INVALID; + ret = 0; + goto cleanup; + } + } + + cleanup: + for (i = 0; i < retrieved_size; i++) { + gnutls_x509_crt_deinit(retrieved[i]); + } + cert_set_deinit(&cert_set); + return ret; +} + +/** + * gnutls_x509_trust_list_verify_named_crt: + * @list: The list + * @cert: is the certificate to be verified + * @name: is the certificate's name + * @name_size: is the certificate's name size + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @voutput: will hold the certificate verification output. + * @func: If non-null will be called on each chain element verification with the output. + * + * This function will try to find a certificate that is associated with the provided + * name --see gnutls_x509_trust_list_add_named_crt(). If a match is found the + * certificate is considered valid. In addition to that this function will also + * check CRLs. The @voutput parameter will hold an OR'ed sequence of + * %gnutls_certificate_status_t flags. + * + * Additionally a certificate verification profile can be specified + * from the ones in %gnutls_certificate_verification_profiles_t by + * ORing the result of GNUTLS_PROFILE_TO_VFLAGS() to the verification + * flags. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0.0 + **/ +int +gnutls_x509_trust_list_verify_named_crt(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert, + const void *name, + size_t name_size, + unsigned int flags, + unsigned int *voutput, + gnutls_verify_output_function func) +{ + int ret; + unsigned int i; + size_t hash; + + + hash = + hash_pjw_bare(cert->raw_issuer_dn.data, + cert->raw_issuer_dn.size); + hash %= list->size; + + ret = check_if_in_blacklist(&cert, 1, + list->blacklisted, list->blacklisted_size); + if (ret != 0) { + *voutput = 0; + *voutput |= GNUTLS_CERT_REVOKED; + *voutput |= GNUTLS_CERT_INVALID; + return 0; + } + + *voutput = GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND; + + for (i = 0; i < list->node[hash].named_cert_size; i++) { + if (gnutls_x509_crt_equals(cert, list->node[hash].named_certs[i].cert) != 0) { /* check if name matches */ + if (list->node[hash].named_certs[i].name_size == + name_size + && memcmp(list->node[hash].named_certs[i].name, + name, name_size) == 0) { + *voutput = 0; + break; + } + } + } + + if (*voutput != 0 || (flags & GNUTLS_VERIFY_DISABLE_CRL_CHECKS)) + return 0; + + /* Check revocation of individual certificates. + * start with the last one that we already have its hash + */ + ret = _gnutls_x509_crt_check_revocation(cert, + list->node[hash].crls, + list->node[hash].crl_size, + func); + if (ret == 1) { /* revoked */ + *voutput |= GNUTLS_CERT_REVOKED; + *voutput |= GNUTLS_CERT_INVALID; + return 0; + } + + return 0; +} + +/* return 1 if @cert is in @list, 0 if not */ +int +_gnutls_trustlist_inlist(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert) +{ + int ret; + unsigned int i; + size_t hash; + + hash = hash_pjw_bare(cert->raw_dn.data, cert->raw_dn.size); + hash %= list->size; + + for (i = 0; i < list->node[hash].trusted_ca_size; i++) { + ret = + gnutls_x509_crt_equals(cert, + list->node[hash]. + trusted_cas[i]); + if (ret != 0) + return 1; + } + + return 0; +} |