/* * 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 * */ #include "gnutls_int.h" #include "errors.h" #include #include #include /* MAX */ #include #include #include #include #include "x509_int.h" #include #include #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;jnode[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;xnode[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;iraw_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 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; }