diff options
Diffstat (limited to '')
-rw-r--r-- | lib/x509/verify.c | 1752 |
1 files changed, 1752 insertions, 0 deletions
diff --git a/lib/x509/verify.c b/lib/x509/verify.c new file mode 100644 index 0000000..c7e35f7 --- /dev/null +++ b/lib/x509/verify.c @@ -0,0 +1,1752 @@ +/* + * Copyright (C) 2003-2014 Free Software Foundation, Inc. + * Copyright (C) 2013 Nikos Mavrogiannopoulos + * Copyright (C) 2014 Red Hat + * + * 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/> + * + */ + +/* All functions which relate to X.509 certificate verification stuff are + * included here + */ + +#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 <pkcs11_int.h> +#include <x509_int.h> +#include <common.h> +#include <pk.h> +#include <x509/verify-high.h> +#include "supported_exts.h" +#include "profiles.h" + +/* Checks if two certs have the same name and the same key. Return 1 on match. + * If @is_ca is zero then this function is identical to gnutls_x509_crt_equals() + */ +unsigned +_gnutls_check_if_same_key(gnutls_x509_crt_t cert1, + gnutls_x509_crt_t cert2, + unsigned is_ca) +{ + int ret; + unsigned result; + + if (is_ca == 0) + return gnutls_x509_crt_equals(cert1, cert2); + + ret = _gnutls_is_same_dn(cert1, cert2); + if (ret == 0) + return 0; + + if (cert1->raw_spki.size > 0 && (cert1->raw_spki.size == cert2->raw_spki.size) && + (memcmp(cert1->raw_spki.data, cert2->raw_spki.data, cert1->raw_spki.size) == 0)) + result = 1; + else + result = 0; + + return result; +} + +unsigned +_gnutls_check_if_same_key2(gnutls_x509_crt_t cert1, + gnutls_datum_t * cert2bin) +{ + int ret; + gnutls_x509_crt_t cert2; + + ret = gnutls_x509_crt_init(&cert2); + if (ret < 0) + return gnutls_assert_val(0); + + ret = gnutls_x509_crt_import(cert2, cert2bin, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_x509_crt_deinit(cert2); + return gnutls_assert_val(0); + } + + ret = _gnutls_check_if_same_key(cert1, cert2, 1); + + gnutls_x509_crt_deinit(cert2); + return ret; +} + +/* checks whether there are present unknown/unsupported critical extensions. + * + * Returns true if they are present. + */ +static unsigned check_for_unknown_exts(gnutls_x509_crt_t cert) +{ + unsigned i; + char oid[MAX_OID_SIZE]; + size_t oid_size; + unsigned critical; + int ret; + + for (i=0;;i++) { + oid_size = sizeof(oid); + oid[0] = 0; + critical = 0; + + ret = gnutls_x509_crt_get_extension_info(cert, i, oid, &oid_size, &critical); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + return 0; + } else if (ret < 0) { + gnutls_assert(); + /* could not decode? */ + _gnutls_debug_log("Could not decode extension %d\n", i); + return 1; + } + + if (critical == 0) + continue; + + if (is_ext_oid_supported(oid, oid_size) == NULL) { + gnutls_assert(); + _gnutls_debug_log("Unsupported critical extension: %s\n", oid); + return 1; + } + } + + return 0; +} + +/* Checks if the issuer of a certificate is a + * Certificate Authority, or if the certificate is the same + * as the issuer (and therefore it doesn't need to be a CA). + * + * Returns true or false, if the issuer is a CA, + * or not. + */ +static unsigned +check_if_ca(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer, + unsigned int *max_path, unsigned int flags) +{ + gnutls_datum_t cert_signed_data = { NULL, 0 }; + gnutls_datum_t issuer_signed_data = { NULL, 0 }; + gnutls_datum_t cert_signature = { NULL, 0 }; + gnutls_datum_t issuer_signature = { NULL, 0 }; + int pathlen = -1, ret; + unsigned result; + unsigned int ca_status = 0; + + /* Check if the issuer is the same with the + * certificate. This is added in order for trusted + * certificates to be able to verify themselves. + */ + + ret = + _gnutls_x509_get_signed_data(issuer->cert, &issuer->der, "tbsCertificate", + &issuer_signed_data); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = + _gnutls_x509_get_signed_data(cert->cert, &cert->der, "tbsCertificate", + &cert_signed_data); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = + _gnutls_x509_get_signature(issuer->cert, "signature", + &issuer_signature); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = + _gnutls_x509_get_signature(cert->cert, "signature", + &cert_signature); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + /* If the subject certificate is the same as the issuer + * return true. + */ + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME)) + if (cert_signed_data.size == issuer_signed_data.size) { + if ((memcmp + (cert_signed_data.data, + issuer_signed_data.data, + cert_signed_data.size) == 0) + && (cert_signature.size == + issuer_signature.size) + && + (memcmp + (cert_signature.data, issuer_signature.data, + cert_signature.size) == 0)) { + result = 1; + goto cleanup; + } + } + + ret = + gnutls_x509_crt_get_basic_constraints(issuer, NULL, &ca_status, + &pathlen); + if (ret < 0) { + ca_status = 0; + pathlen = -1; + } + + if (ca_status != 0 && pathlen != -1) { + if ((unsigned) pathlen < *max_path) + *max_path = pathlen; + } + + if (ca_status != 0) { + result = 1; + goto cleanup; + } + /* Handle V1 CAs that do not have a basicConstraint, but accept + these certs only if the appropriate flags are set. */ + else if ((ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) && + ((flags & GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT) || + (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT) && + (gnutls_x509_crt_check_issuer(issuer, issuer) != 0)))) { + gnutls_assert(); + result = 1; + goto cleanup; + } else { + gnutls_assert(); + } + + fail: + result = 0; + + cleanup: + _gnutls_free_datum(&cert_signed_data); + _gnutls_free_datum(&issuer_signed_data); + _gnutls_free_datum(&cert_signature); + _gnutls_free_datum(&issuer_signature); + return result; +} + + +/* This function checks if cert's issuer is issuer. + * This does a straight (DER) compare of the issuer/subject DN fields in + * the given certificates, as well as check the authority key ID. + * + * Returns 1 if they match and (0) if they don't match. + */ +static unsigned is_issuer(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer) +{ + uint8_t id1[MAX_KEY_ID_SIZE]; + uint8_t id2[MAX_KEY_ID_SIZE]; + size_t id1_size; + size_t id2_size; + int ret; + unsigned result; + + if (_gnutls_x509_compare_raw_dn + (&cert->raw_issuer_dn, &issuer->raw_dn) != 0) + result = 1; + else + result = 0; + + if (result != 0) { + /* check if the authority key identifier matches the subject key identifier + * of the issuer */ + id1_size = sizeof(id1); + + ret = + gnutls_x509_crt_get_authority_key_id(cert, id1, + &id1_size, NULL); + if (ret < 0) { + /* If there is no authority key identifier in the + * certificate, assume they match */ + result = 1; + goto cleanup; + } + + id2_size = sizeof(id2); + ret = + gnutls_x509_crt_get_subject_key_id(issuer, id2, + &id2_size, NULL); + if (ret < 0) { + /* If there is no subject key identifier in the + * issuer certificate, assume they match */ + result = 1; + gnutls_assert(); + goto cleanup; + } + + if (id1_size == id2_size + && memcmp(id1, id2, id1_size) == 0) + result = 1; + else + result = 0; + } + + cleanup: + return result; +} + +/* Check if the given certificate is the issuer of the CRL. + * Returns 1 on success and 0 otherwise. + */ +static unsigned is_crl_issuer(gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer) +{ + if (_gnutls_x509_compare_raw_dn + (&crl->raw_issuer_dn, &issuer->raw_dn) != 0) + return 1; + else + return 0; +} + +/* Checks if the DN of two certificates is the same. + * Returns 1 if they match and (0) if they don't match. Otherwise + * a negative error code is returned to indicate error. + */ +unsigned _gnutls_is_same_dn(gnutls_x509_crt_t cert1, gnutls_x509_crt_t cert2) +{ + if (_gnutls_x509_compare_raw_dn(&cert1->raw_dn, &cert2->raw_dn) != + 0) + return 1; + else + return 0; +} + +/* Finds an issuer of the certificate. If multiple issuers + * are present, returns one that is activated and not expired. + */ +static inline gnutls_x509_crt_t +find_issuer(gnutls_x509_crt_t cert, + const gnutls_x509_crt_t * trusted_cas, int tcas_size) +{ + int i; + gnutls_x509_crt_t issuer = NULL; + + /* this is serial search. + */ + for (i = 0; i < tcas_size; i++) { + if (is_issuer(cert, trusted_cas[i]) != 0) { + if (issuer == NULL) { + issuer = trusted_cas[i]; + } else { + time_t now = gnutls_time(0); + + if (now < + gnutls_x509_crt_get_expiration_time + (trusted_cas[i]) + && now >= + gnutls_x509_crt_get_activation_time + (trusted_cas[i])) { + issuer = trusted_cas[i]; + } + } + } + } + + return issuer; +} + +static unsigned int check_time_status(gnutls_x509_crt_t crt, time_t now) +{ + int status = 0; + time_t t; + + t = gnutls_x509_crt_get_activation_time(crt); + if (t == (time_t) - 1 || now < t) { + status |= GNUTLS_CERT_NOT_ACTIVATED; + status |= GNUTLS_CERT_INVALID; + return status; + } + + t = gnutls_x509_crt_get_expiration_time(crt); + if (t == (time_t) - 1 || now > t) { + status |= GNUTLS_CERT_EXPIRED; + status |= GNUTLS_CERT_INVALID; + return status; + } + + return 0; +} + +unsigned _gnutls_is_broken_sig_allowed(const gnutls_sign_entry_st *se, unsigned int flags) +{ + gnutls_digest_algorithm_t hash; + + /* we have a catch all */ + if ((flags & GNUTLS_VERIFY_ALLOW_BROKEN) == GNUTLS_VERIFY_ALLOW_BROKEN) + return 1; + + /* the first two are for backwards compatibility */ + if ((se->id == GNUTLS_SIGN_RSA_MD2) + && (flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) + return 1; + if ((se->id == GNUTLS_SIGN_RSA_MD5) + && (flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5)) + return 1; + + hash = se->hash; + if (hash == GNUTLS_DIG_SHA1 && (flags & GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1)) + return 1; + + return 0; +} + +#define CASE_SEC_PARAM(profile, level) \ + case profile: \ + sym_bits = gnutls_sec_param_to_symmetric_bits(level); \ + se = _gnutls_sign_to_entry(sigalg); \ + if (unlikely(se == NULL)) { \ + _gnutls_cert_log("cert", crt); \ + _gnutls_debug_log(#level": certificate's signature algorithm is unknown\n"); \ + return gnutls_assert_val(0); \ + } \ + if (unlikely(se->hash == GNUTLS_DIG_UNKNOWN)) { \ + _gnutls_cert_log("cert", crt); \ + _gnutls_debug_log(#level": certificate's signature hash is unknown\n"); \ + return gnutls_assert_val(0); \ + } \ + if (!trusted && \ + _gnutls_sign_get_hash_strength(sigalg) < sym_bits) { \ + _gnutls_cert_log("cert", crt); \ + _gnutls_debug_log(#level": certificate's signature hash strength is unacceptable (is %u bits, needed %u)\n", _gnutls_sign_get_hash_strength(sigalg), sym_bits); \ + return gnutls_assert_val(0); \ + } \ + sp = gnutls_pk_bits_to_sec_param(pkalg, bits); \ + if (sp < level) { \ + _gnutls_cert_log("cert", crt); \ + _gnutls_debug_log(#level": certificate's security level is unacceptable\n"); \ + return gnutls_assert_val(0); \ + } \ + if (issuer) { \ + sp = gnutls_pk_bits_to_sec_param(issuer_pkalg, issuer_bits); \ + if (sp < level) { \ + _gnutls_cert_log("issuer", issuer); \ + _gnutls_debug_log(#level": certificate's issuer security level is unacceptable\n"); \ + return gnutls_assert_val(0); \ + } \ + } \ + break; + +/* Checks whether the provided certificates are acceptable + * according to verification profile specified. + * + * @crt: a certificate + * @issuer: the certificates issuer (allowed to be NULL) + * @sigalg: the signature algorithm used + * @trusted: whether @crt is treated as trusted (e.g., present in the system + * trust list); if it is true, the check on signature algorithm will + * be skipped + * @flags: the specified verification flags + */ +static unsigned is_level_acceptable( + gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, + gnutls_sign_algorithm_t sigalg, bool trusted, + unsigned flags) +{ + gnutls_certificate_verification_profiles_t profile = GNUTLS_VFLAGS_TO_PROFILE(flags); + int issuer_pkalg = 0, pkalg, ret; + unsigned bits = 0, issuer_bits = 0, sym_bits = 0; + gnutls_pk_params_st params; + gnutls_sec_param_t sp; + const gnutls_sign_entry_st *se; + gnutls_certificate_verification_profiles_t min_profile; + + min_profile = _gnutls_get_system_wide_verification_profile(); + + if (min_profile) { + if (profile < min_profile) { + gnutls_assert(); + profile = min_profile; + } + } + + if (profile == GNUTLS_PROFILE_UNKNOWN) { + return 1; + } + + pkalg = gnutls_x509_crt_get_pk_algorithm(crt, &bits); + if (pkalg < 0) + return gnutls_assert_val(0); + + if (issuer) { + issuer_pkalg = gnutls_x509_crt_get_pk_algorithm(issuer, &issuer_bits); + if (issuer_pkalg < 0) + return gnutls_assert_val(0); + } + + switch (profile) { + CASE_SEC_PARAM(GNUTLS_PROFILE_VERY_WEAK, GNUTLS_SEC_PARAM_VERY_WEAK); + CASE_SEC_PARAM(GNUTLS_PROFILE_LOW, GNUTLS_SEC_PARAM_LOW); + CASE_SEC_PARAM(GNUTLS_PROFILE_LEGACY, GNUTLS_SEC_PARAM_LEGACY); + CASE_SEC_PARAM(GNUTLS_PROFILE_MEDIUM, GNUTLS_SEC_PARAM_MEDIUM); + CASE_SEC_PARAM(GNUTLS_PROFILE_HIGH, GNUTLS_SEC_PARAM_HIGH); + CASE_SEC_PARAM(GNUTLS_PROFILE_ULTRA, GNUTLS_SEC_PARAM_ULTRA); + CASE_SEC_PARAM(GNUTLS_PROFILE_FUTURE, GNUTLS_SEC_PARAM_FUTURE); + case GNUTLS_PROFILE_SUITEB128: + case GNUTLS_PROFILE_SUITEB192: { + unsigned curve, issuer_curve; + + /* check suiteB params validity: rfc5759 */ + + if (gnutls_x509_crt_get_version(crt) != 3) { + _gnutls_debug_log("SUITEB: certificate uses an unacceptable version number\n"); + return gnutls_assert_val(0); + } + + if (sigalg != GNUTLS_SIGN_ECDSA_SHA256 && sigalg != GNUTLS_SIGN_ECDSA_SHA384) { + _gnutls_debug_log("SUITEB: certificate is not signed using ECDSA-SHA256 or ECDSA-SHA384\n"); + return gnutls_assert_val(0); + } + + if (pkalg != GNUTLS_PK_EC) { + _gnutls_debug_log("SUITEB: certificate does not contain ECC parameters\n"); + return gnutls_assert_val(0); + } + + if (issuer_pkalg != GNUTLS_PK_EC) { + _gnutls_debug_log("SUITEB: certificate's issuer does not have ECC parameters\n"); + return gnutls_assert_val(0); + } + + ret = _gnutls_x509_crt_get_mpis(crt, ¶ms); + if (ret < 0) { + _gnutls_debug_log("SUITEB: cannot read certificate params\n"); + return gnutls_assert_val(0); + } + + curve = params.curve; + gnutls_pk_params_release(¶ms); + + if (curve != GNUTLS_ECC_CURVE_SECP256R1 && + curve != GNUTLS_ECC_CURVE_SECP384R1) { + _gnutls_debug_log("SUITEB: certificate's ECC params do not contain SECP256R1 or SECP384R1\n"); + return gnutls_assert_val(0); + } + + if (profile == GNUTLS_PROFILE_SUITEB192) { + if (curve != GNUTLS_ECC_CURVE_SECP384R1) { + _gnutls_debug_log("SUITEB192: certificate does not use SECP384R1\n"); + return gnutls_assert_val(0); + } + } + + if (issuer != NULL) { + if (gnutls_x509_crt_get_version(issuer) != 3) { + _gnutls_debug_log("SUITEB: certificate's issuer uses an unacceptable version number\n"); + return gnutls_assert_val(0); + } + + ret = _gnutls_x509_crt_get_mpis(issuer, ¶ms); + if (ret < 0) { + _gnutls_debug_log("SUITEB: cannot read certificate params\n"); + return gnutls_assert_val(0); + } + + issuer_curve = params.curve; + gnutls_pk_params_release(¶ms); + + if (issuer_curve != GNUTLS_ECC_CURVE_SECP256R1 && + issuer_curve != GNUTLS_ECC_CURVE_SECP384R1) { + _gnutls_debug_log("SUITEB: certificate's issuer ECC params do not contain SECP256R1 or SECP384R1\n"); + return gnutls_assert_val(0); + } + + if (issuer_curve < curve) { + _gnutls_debug_log("SUITEB: certificate's issuer ECC params are weaker than the certificate's\n"); + return gnutls_assert_val(0); + } + + if (sigalg == GNUTLS_SIGN_ECDSA_SHA256 && + issuer_curve == GNUTLS_ECC_CURVE_SECP384R1) { + _gnutls_debug_log("SUITEB: certificate is signed with ECDSA-SHA256 when using SECP384R1\n"); + return gnutls_assert_val(0); + } + } + + break; + case GNUTLS_PROFILE_UNKNOWN: /* already checked; avoid compiler warnings */ + _gnutls_debug_log("An unknown profile (%d) was encountered\n", (int)profile); + } + } + + return 1; +} + +typedef struct verify_state_st { + time_t now; + unsigned int max_path; + gnutls_x509_name_constraints_t nc; + gnutls_x509_tlsfeatures_t tls_feat; + gnutls_verify_output_function *func; +} verify_state_st; + +#define MARK_INVALID(x) { gnutls_assert(); \ + out |= (x|GNUTLS_CERT_INVALID); \ + result = 0; } + +static int _gnutls_x509_verify_data(gnutls_sign_algorithm_t sign, + const gnutls_datum_t * data, + const gnutls_datum_t * signature, + gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer, + unsigned vflags); + +/* + * Verifies the given certificate against a certificate list of + * trusted CAs. + * + * Returns only 0 or 1. If 1 it means that the certificate + * was successfully verified. + * + * 'flags': an OR of the gnutls_certificate_verify_flags enumeration. + * + * Output will hold some extra information about the verification + * procedure. + */ +static unsigned verify_crt(gnutls_x509_trust_list_t tlist, + gnutls_x509_crt_t cert, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, unsigned int flags, + unsigned int *output, + verify_state_st *vparams, + unsigned end_cert) +{ + gnutls_datum_t cert_signed_data = { NULL, 0 }; + gnutls_datum_t cert_signature = { NULL, 0 }; + gnutls_x509_crt_t issuer = NULL; + int issuer_version; + unsigned result = 1; + unsigned int out = 0, usage; + int sigalg, ret; + const gnutls_sign_entry_st *se; + + if (output) + *output = 0; + + if (vparams->max_path == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + /* bail immediately, to avoid inconistency */ + goto cleanup; + } + vparams->max_path--; + + if (tcas_size >= 1) + issuer = find_issuer(cert, trusted_cas, tcas_size); + + ret = + _gnutls_x509_get_signed_data(cert->cert, &cert->der, "tbsCertificate", + &cert_signed_data); + if (ret < 0) { + MARK_INVALID(0); + cert_signed_data.data = NULL; + } + + ret = + _gnutls_x509_get_signature(cert->cert, "signature", + &cert_signature); + if (ret < 0) { + MARK_INVALID(0); + cert_signature.data = NULL; + } + + ret = + _gnutls_x509_get_signature_algorithm(cert->cert, + "signatureAlgorithm"); + if (ret < 0) { + MARK_INVALID(0); + } + sigalg = ret; + + se = _gnutls_sign_to_entry(sigalg); + + /* issuer is not in trusted certificate + * authorities. + */ + if (issuer == NULL) { + MARK_INVALID(GNUTLS_CERT_SIGNER_NOT_FOUND); + } else { + if (vparams->nc != NULL) { + /* append the issuer's constraints */ + ret = gnutls_x509_crt_get_name_constraints(issuer, vparams->nc, + GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, NULL); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto nc_done; + } + + /* only check name constraints in server certificates, not CAs */ + if (end_cert != 0) { + ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_DNSNAME, cert); + if (ret == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto nc_done; + } + + ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_RFC822NAME, cert); + if (ret == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto nc_done; + } + + ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_DN, cert); + if (ret == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto nc_done; + } + + ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_URI, cert); + if (ret == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto nc_done; + } + + ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_IPADDRESS, cert); + if (ret == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto nc_done; + } + } + } + + nc_done: + if (vparams->tls_feat != NULL) { + /* append the issuer's constraints */ + ret = gnutls_x509_crt_get_tlsfeatures(issuer, vparams->tls_feat, GNUTLS_EXT_FLAG_APPEND, NULL); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto feat_done; + } + + ret = gnutls_x509_tlsfeatures_check_crt(vparams->tls_feat, cert); + if (ret == 0) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + goto feat_done; + } + } + + feat_done: + issuer_version = gnutls_x509_crt_get_version(issuer); + + if (issuer_version < 0) { + MARK_INVALID(0); + } else if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN) && + ((flags & GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT) + || issuer_version != 1)) { + if (check_if_ca(cert, issuer, &vparams->max_path, flags) != 1) { + MARK_INVALID(GNUTLS_CERT_SIGNER_NOT_CA); + } + + ret = + gnutls_x509_crt_get_key_usage(issuer, &usage, NULL); + if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + if (ret < 0) { + MARK_INVALID(0); + } else if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + } + } + } + + if (sigalg < 0) { + MARK_INVALID(0); + } else if (cert_signed_data.data != NULL && + cert_signature.data != NULL) { + ret = + _gnutls_x509_verify_data(sigalg, + &cert_signed_data, + &cert_signature, + cert, + issuer, flags); + + if (ret == GNUTLS_E_PK_SIG_VERIFY_FAILED) { + MARK_INVALID(GNUTLS_CERT_SIGNATURE_FAILURE); + } else if (ret == GNUTLS_E_CONSTRAINT_ERROR) { + MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + } else if (ret < 0) { + MARK_INVALID(0); + } + } + } + + /* we always check the issuer for unsupported critical extensions */ + if (issuer && check_for_unknown_exts(issuer) != 0) { + if (!(flags & GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS)) { + MARK_INVALID(GNUTLS_CERT_UNKNOWN_CRIT_EXTENSIONS); + } + } + + /* we only check the end-certificate for critical extensions; that + * way do not perform this check twice on the certificates when + * verifying a large list */ + if (end_cert && check_for_unknown_exts(cert) != 0) { + if (!(flags & GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS)) { + MARK_INVALID(GNUTLS_CERT_UNKNOWN_CRIT_EXTENSIONS); + } + } + + if (sigalg >= 0 && se) { + if (is_level_acceptable(cert, issuer, sigalg, false, flags) == 0) { + MARK_INVALID(GNUTLS_CERT_INSECURE_ALGORITHM); + } + + /* If the certificate is not self signed check if the algorithms + * used are secure. If the certificate is self signed it doesn't + * really matter. + */ + if (_gnutls_sign_is_secure2(se, GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS) == 0 && + _gnutls_is_broken_sig_allowed(se, flags) == 0 && + is_issuer(cert, cert) == 0) { + MARK_INVALID(GNUTLS_CERT_INSECURE_ALGORITHM); + } + } + + /* Check activation/expiration times + */ + if (!(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) { + /* check the time of the issuer first */ + if (issuer != NULL && + !(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS)) { + out |= check_time_status(issuer, vparams->now); + if (out != 0) { + gnutls_assert(); + result = 0; + } + } + + out |= check_time_status(cert, vparams->now); + if (out != 0) { + gnutls_assert(); + result = 0; + } + } + + cleanup: + if (output) + *output |= out; + + if (vparams->func) { + if (result == 0) { + out |= GNUTLS_CERT_INVALID; + } + vparams->func(cert, issuer, NULL, out); + } + _gnutls_free_datum(&cert_signed_data); + _gnutls_free_datum(&cert_signature); + + return result; +} + +/** + * gnutls_x509_crt_check_issuer: + * @cert: is the certificate to be checked + * @issuer: is the certificate of a possible issuer + * + * This function will check if the given certificate was issued by the + * given issuer. It checks the DN fields and the authority + * key identifier and subject key identifier fields match. + * + * If the same certificate is provided at the @cert and @issuer fields, + * it will check whether the certificate is self-signed. + * + * Returns: It will return true (1) if the given certificate is issued + * by the given issuer, and false (0) if not. + **/ +unsigned +gnutls_x509_crt_check_issuer(gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer) +{ + return is_issuer(cert, issuer); +} + +static +unsigned check_ca_sanity(const gnutls_x509_crt_t issuer, + time_t now, unsigned int flags) +{ + unsigned int status = 0; + unsigned sigalg; + int ret; + + /* explicit time check for trusted CA that we remove from + * list. GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS + */ + if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS) && + !(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) { + status |= check_time_status(issuer, now); + } + + ret = + _gnutls_x509_get_signature_algorithm(issuer->cert, "signatureAlgorithm"); + sigalg = ret; + + /* we explicitly allow CAs which we do not support their self-algorithms + * to pass. */ + if (ret >= 0 && !is_level_acceptable(issuer, NULL, sigalg, true, flags)) { + status |= GNUTLS_CERT_INSECURE_ALGORITHM|GNUTLS_CERT_INVALID; + } + + return status; + +} + +/* Verify X.509 certificate chain. + * + * Note that the return value is an OR of GNUTLS_CERT_* elements. + * + * This function verifies a X.509 certificate list. The certificate + * list should lead to a trusted certificate in order to be trusted. + */ +unsigned int +_gnutls_verify_crt_status(gnutls_x509_trust_list_t tlist, + const gnutls_x509_crt_t * certificate_list, + int clist_size, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, + unsigned int flags, + const char *purpose, + gnutls_verify_output_function func) +{ + int i = 0, ret; + unsigned int status = 0, output; + time_t now = gnutls_time(0); + verify_state_st vparams; + + 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. */ + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME)) + i = 0; /* also replace the first one */ + else + i = 1; /* do not replace the first one */ + + for (; i < clist_size; i++) { + int j; + + for (j = 0; j < tcas_size; j++) { + /* we check for a certificate that may not be identical with the one + * sent by the client, but will have the same name and key. That is + * because it can happen that a CA certificate is upgraded from intermediate + * CA to self-signed CA at some point. */ + if (_gnutls_check_if_same_key + (certificate_list[i], trusted_cas[j], i) != 0) { + + status |= check_ca_sanity(trusted_cas[j], now, flags); + + if (func) + func(certificate_list[i], + trusted_cas[j], NULL, status); + + if (status != 0) { + return gnutls_assert_val(status); + } + + clist_size = i; + break; + } + } + /* clist_size may have been changed which gets out of loop */ + } + + if (clist_size == 0) { + /* The certificate is already present in the trusted certificate list. + * Nothing to verify. */ + return status; + } + + memset(&vparams, 0, sizeof(vparams)); + vparams.now = now; + vparams.max_path = MAX_VERIFY_DEPTH; + vparams.func = func; + + ret = gnutls_x509_name_constraints_init(&vparams.nc); + if (ret < 0) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + return status; + } + + ret = gnutls_x509_tlsfeatures_init(&vparams.tls_feat); + if (ret < 0) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + goto cleanup; + } + + /* Verify the last certificate in the certificate path + * against the trusted CA certificate list. + * + * If no CAs are present returns CERT_INVALID. Thus works + * in self signed etc certificates. + */ + output = 0; + + ret = verify_crt(tlist, + certificate_list[clist_size - 1], + trusted_cas, tcas_size, flags, + &output, + &vparams, + clist_size==1?1:0); + if (ret != 1) { + /* if the last certificate in the certificate + * list is invalid, then the certificate is not + * trusted. + */ + gnutls_assert(); + status |= output; + status |= GNUTLS_CERT_INVALID; + goto cleanup; + } + + /* Verify the certificate path (chain) + */ + for (i = clist_size - 1; i > 0; i--) { + output = 0; + + if (purpose != NULL) { + ret = _gnutls_check_key_purpose(certificate_list[i], purpose, 1); + if (ret != 1) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_PURPOSE_MISMATCH; + + if (func) + func(certificate_list[i-1], + certificate_list[i], NULL, status); + goto cleanup; + } + } + + /* note that here we disable this V1 CA flag. So that no version 1 + * certificates can exist in a supplied chain. + */ + if (!(flags & GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT)) { + flags |= GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT; + } + + if (!verify_crt(tlist, + certificate_list[i - 1], + &certificate_list[i], 1, + flags, &output, + &vparams, + i==1?1:0)) { + gnutls_assert(); + status |= output; + status |= GNUTLS_CERT_INVALID; + goto cleanup; + } + } + +cleanup: + gnutls_x509_name_constraints_deinit(vparams.nc); + gnutls_x509_tlsfeatures_deinit(vparams.tls_feat); + return status; +} + + +#define PURPOSE_NSSGC "2.16.840.1.113730.4.1" +#define PURPOSE_VSGC "2.16.840.1.113733.1.8.1" + +/* Returns true if the provided purpose is in accordance with the certificate. + */ +unsigned _gnutls_check_key_purpose(gnutls_x509_crt_t cert, const char *purpose, unsigned no_any) +{ + char oid[MAX_OID_SIZE]; + size_t oid_size; + int ret; + unsigned critical = 0; + unsigned check_obsolete_oids = 0; + unsigned i; + + /* The check_obsolete_oids hack is because of certain very old CA certificates + * around which instead of having the GNUTLS_KP_TLS_WWW_SERVER have some old + * OIDs for that purpose. Assume these OIDs equal GNUTLS_KP_TLS_WWW_SERVER in + * CA certs */ + if (strcmp(purpose, GNUTLS_KP_TLS_WWW_SERVER) == 0) { + unsigned ca_status; + ret = + gnutls_x509_crt_get_basic_constraints(cert, NULL, &ca_status, + NULL); + if (ret < 0) + ca_status = 0; + + if (ca_status) + check_obsolete_oids = 1; + } + + for (i=0;;i++) { + oid_size = sizeof(oid); + ret = gnutls_x509_crt_get_key_purpose_oid(cert, i, oid, &oid_size, &critical); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + if (i==0) { + /* no key purpose in certificate, assume ANY */ + return 1; + } else { + gnutls_assert(); + break; + } + } else if (ret < 0) { + gnutls_assert(); + break; + } + + if (check_obsolete_oids) { + if (strcmp(oid, PURPOSE_NSSGC) == 0) { + return 1; + } else if (strcmp(oid, PURPOSE_VSGC) == 0) { + return 1; + } + } + + if (strcmp(oid, purpose) == 0 || (no_any == 0 && strcmp(oid, GNUTLS_KP_ANY) == 0)) { + return 1; + } + _gnutls_debug_log("looking for key purpose '%s', but have '%s'\n", purpose, oid); + } + return 0; +} + +#ifdef ENABLE_PKCS11 +/* Verify X.509 certificate chain using a PKCS #11 token. + * + * Note that the return value is an OR of GNUTLS_CERT_* elements. + * + * Unlike the non-PKCS#11 version, this function accepts a key purpose + * (from GNUTLS_KP_...). That is because in the p11-kit trust modules + * anchors are mixed and get assigned a purpose. + * + * This function verifies a X.509 certificate list. The certificate + * list should lead to a trusted certificate in order to be trusted. + */ +unsigned int +_gnutls_pkcs11_verify_crt_status(gnutls_x509_trust_list_t tlist, + const char* url, + const gnutls_x509_crt_t * certificate_list, + unsigned clist_size, + const char *purpose, + unsigned int flags, + gnutls_verify_output_function func) +{ + int ret; + unsigned int status = 0, i; + gnutls_x509_crt_t issuer = NULL; + gnutls_datum_t raw_issuer = {NULL, 0}; + time_t now = gnutls_time(0); + + 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. */ + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME)) + i = 0; /* also replace the first one */ + else + i = 1; /* do not replace the first one */ + + for (; i < clist_size; i++) { + unsigned vflags; + gnutls_x509_crt_t trusted_cert; + + if (i == 0) /* in the end certificate do full comparison */ + vflags = GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE| + GNUTLS_PKCS11_OBJ_FLAG_COMPARE|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED; + else + vflags = GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE| + GNUTLS_PKCS11_OBJ_FLAG_COMPARE_KEY|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED; + + if (_gnutls_pkcs11_crt_is_known (url, certificate_list[i], vflags, &trusted_cert) != 0) { + + status |= check_ca_sanity(trusted_cert, now, flags); + + if (func) + func(trusted_cert, + certificate_list[i], NULL, status); + + gnutls_x509_crt_deinit(trusted_cert); + + if (status != 0) { + return gnutls_assert_val(status); + } + + clist_size = i; + break; + } + /* clist_size may have been changed which gets out of loop */ + } + + if (clist_size == 0) { + /* The certificate is already present in the trusted certificate list. + * Nothing to verify. */ + return status; + } + + /* check for blacklists */ + for (i = 0; i < clist_size; i++) { + if (gnutls_pkcs11_crt_is_known (url, certificate_list[i], + GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE| + GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) != 0) { + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_REVOKED; + if (func) + func(certificate_list[i], certificate_list[i], NULL, status); + goto cleanup; + } + } + + /* check against issuer */ + ret = gnutls_pkcs11_get_raw_issuer(url, certificate_list[clist_size - 1], + &raw_issuer, GNUTLS_X509_FMT_DER, + GNUTLS_PKCS11_OBJ_FLAG_OVERWRITE_TRUSTMOD_EXT|GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE); + if (ret < 0) { + gnutls_assert(); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE && clist_size > 2) { + + /* check if the last certificate in the chain is present + * in our trusted list, and if yes, verify against it. */ + ret = gnutls_pkcs11_crt_is_known(url, certificate_list[clist_size - 1], + GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED|GNUTLS_PKCS11_OBJ_FLAG_COMPARE); + if (ret != 0) { + return _gnutls_verify_crt_status(tlist, + certificate_list, clist_size, + &certificate_list[clist_size - 1], + 1, flags, purpose, func); + } + } + + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_SIGNER_NOT_FOUND; + /* verify the certificate list against 0 trusted CAs in order + * to get, any additional flags from the certificate list (e.g., + * insecure algorithms or expired */ + status |= _gnutls_verify_crt_status(tlist, certificate_list, clist_size, + NULL, 0, flags, purpose, func); + goto cleanup; + } + + ret = gnutls_x509_crt_init(&issuer); + if (ret < 0) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_SIGNER_NOT_FOUND; + goto cleanup; + } + + ret = gnutls_x509_crt_import(issuer, &raw_issuer, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_SIGNER_NOT_FOUND; + goto cleanup; + } + + /* check if the raw issuer is blacklisted (it can happen if + * the issuer is both in the trusted list and the blacklisted) + */ + if (gnutls_pkcs11_crt_is_known (url, issuer, + GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE| + GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) != 0) { + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_SIGNER_NOT_FOUND; /* if the signer is revoked - it is as if it doesn't exist */ + goto cleanup; + } + + /* security modules that provide trust, bundle all certificates (of all purposes) + * together. In software that doesn't specify any purpose assume the default to + * be www-server. */ + ret = _gnutls_check_key_purpose(issuer, purpose==NULL?GNUTLS_KP_TLS_WWW_SERVER:purpose, 0); + if (ret != 1) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + status |= GNUTLS_CERT_SIGNER_NOT_FOUND; + goto cleanup; + } + + status = _gnutls_verify_crt_status(tlist, certificate_list, clist_size, + &issuer, 1, flags, purpose, func); + +cleanup: + gnutls_free(raw_issuer.data); + if (issuer != NULL) + gnutls_x509_crt_deinit(issuer); + + return status; +} +#endif + +static int +_gnutls_x509_validate_sign_params(gnutls_pk_algorithm_t pk_algorithm, + asn1_node cert, + const char *name, + gnutls_x509_spki_st *sig_params) +{ + /* The signature parameter validation is only needed for RSA-PSS */ + if (pk_algorithm == GNUTLS_PK_RSA_PSS) { + int result; + gnutls_x509_spki_st params; + + result = _gnutls_x509_read_sign_params(cert, name, ¶ms); + if (result < 0) { + /* If parameters field is absent, no parameter + * validation is needed */ + if (result != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND && + result != GNUTLS_E_ASN1_VALUE_NOT_FOUND) { + gnutls_assert(); + return result; + } + } else { + /* Check if the underlying hash algorithms are same. */ + if (sig_params->rsa_pss_dig != params.rsa_pss_dig) { + gnutls_assert(); + return GNUTLS_E_CONSTRAINT_ERROR; + } + + /* The salt length used to generate the + * signature must be equal to or larger than + * the one in the key parameter. */ + if (sig_params->salt_size < params.salt_size) { + gnutls_assert(); + return GNUTLS_E_CONSTRAINT_ERROR; + } + } + } + return 0; +} + +/* verifies if the certificate is properly signed. + * returns GNUTLS_E_PK_VERIFY_SIG_FAILED on failure and 1 on success. + * + * 'data' is the signed data + * 'signature' is the signature! + */ +static int +_gnutls_x509_verify_data(gnutls_sign_algorithm_t sign, + const gnutls_datum_t * data, + const gnutls_datum_t * signature, + gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer, + unsigned vflags) +{ + gnutls_pk_params_st params; + gnutls_pk_algorithm_t issuer_pk; + int ret; + gnutls_x509_spki_st sign_params; + const gnutls_sign_entry_st *se; + + /* Read the MPI parameters from the issuer's certificate. + */ + ret = _gnutls_x509_crt_get_mpis(issuer, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + issuer_pk = gnutls_x509_crt_get_pk_algorithm(issuer, NULL); + + se = _gnutls_sign_to_entry(sign); + if (se == NULL) + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM); + + if (cert != NULL) { + ret = _gnutls_x509_read_sign_params(cert->cert, + "signatureAlgorithm", + &sign_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_validate_sign_params(issuer_pk, + issuer->cert, + "tbsCertificate." + "subjectPublicKeyInfo." + "algorithm", + &sign_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else { + memcpy(&sign_params, ¶ms.spki, + sizeof(gnutls_x509_spki_st)); + + sign_params.pk = se->pk; + if (sign_params.pk == GNUTLS_PK_RSA_PSS) + sign_params.rsa_pss_dig = se->hash; + } + + ret = pubkey_verify_data(se, hash_to_entry(se->hash), data, signature, ¶ms, + &sign_params, vflags); + if (ret < 0) { + gnutls_assert(); + } + + cleanup: + /* release all allocated MPIs + */ + gnutls_pk_params_release(¶ms); + + return ret; +} + +/** + * gnutls_x509_crt_list_verify: + * @cert_list: is the certificate list to be verified + * @cert_list_length: holds the number of certificate in cert_list + * @CA_list: is the CA list which will be used in verification + * @CA_list_length: holds the number of CA certificate in CA_list + * @CRL_list: holds a list of CRLs. + * @CRL_list_length: the length of CRL list. + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @verify: will hold the certificate verification output. + * + * + * This function will try to verify the given certificate list and + * return its status. The details of the verification are the same + * as in gnutls_x509_trust_list_verify_crt2(). + * + * You must check the peer's name in order to check if the verified + * certificate belongs to the actual peer. + * + * The certificate verification output will be put in @verify and will + * be one or more of the gnutls_certificate_status_t enumerated + * elements bitwise or'd. For a more detailed verification status use + * gnutls_x509_crt_verify() per list element. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_list_verify(const gnutls_x509_crt_t * cert_list, + unsigned cert_list_length, + const gnutls_x509_crt_t * CA_list, + unsigned CA_list_length, + const gnutls_x509_crl_t * CRL_list, + unsigned CRL_list_length, unsigned int flags, + unsigned int *verify) +{ + unsigned i; + int ret; + gnutls_x509_trust_list_t tlist; + + if (cert_list == NULL || cert_list_length == 0) + return GNUTLS_E_NO_CERTIFICATE_FOUND; + + gnutls_x509_trust_list_init(&tlist, 0); + + /* Verify certificate + */ + *verify = _gnutls_verify_crt_status(tlist, cert_list, cert_list_length, + CA_list, CA_list_length, + flags, NULL, NULL); + + /* Check for revoked certificates in the chain. + */ + for (i = 0; i < cert_list_length; i++) { + ret = gnutls_x509_crt_check_revocation(cert_list[i], + CRL_list, + CRL_list_length); + if (ret == 1) { /* revoked */ + *verify |= GNUTLS_CERT_REVOKED; + *verify |= GNUTLS_CERT_INVALID; + } + } + + gnutls_x509_trust_list_deinit(tlist, 0); + return 0; +} + +/** + * gnutls_x509_crt_verify: + * @cert: is the certificate to be verified + * @CA_list: is one certificate that is considered to be trusted one + * @CA_list_length: holds the number of CA certificate in CA_list + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @verify: will hold the certificate verification output. + * + * This function will try to verify the given certificate and return + * its status. Note that a verification error does not imply a negative + * return status. In that case the @verify status is set. + * + * 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. + **/ +int +gnutls_x509_crt_verify(gnutls_x509_crt_t cert, + const gnutls_x509_crt_t * CA_list, + unsigned CA_list_length, unsigned int flags, + unsigned int *verify) +{ + gnutls_x509_trust_list_t tlist; + + gnutls_x509_trust_list_init(&tlist, 0); + + /* Verify certificate + */ + *verify = _gnutls_verify_crt_status(tlist, &cert, 1, + CA_list, CA_list_length, + flags, NULL, NULL); + + gnutls_x509_trust_list_deinit(tlist, 0); + return 0; +} + +/** + * gnutls_x509_crl_check_issuer: + * @crl: is the CRL to be checked + * @issuer: is the certificate of a possible issuer + * + * This function will check if the given CRL was issued by the given + * issuer certificate. + * + * Returns: true (1) if the given CRL was issued by the given issuer, + * and false (0) if not. + **/ +unsigned +gnutls_x509_crl_check_issuer(gnutls_x509_crl_t crl, + gnutls_x509_crt_t issuer) +{ + return is_crl_issuer(crl, issuer); +} + +static inline gnutls_x509_crt_t +find_crl_issuer(gnutls_x509_crl_t crl, + const gnutls_x509_crt_t * trusted_cas, int tcas_size) +{ + int i; + + /* this is serial search. + */ + + for (i = 0; i < tcas_size; i++) { + if (is_crl_issuer(crl, trusted_cas[i]) != 0) + return trusted_cas[i]; + } + + gnutls_assert(); + return NULL; +} + +/** + * gnutls_x509_crl_verify: + * @crl: is the crl to be verified + * @trusted_cas: is a certificate list that is considered to be trusted one + * @tcas_size: holds the number of CA certificates in CA_list + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @verify: will hold the crl verification output. + * + * This function will try to verify the given crl and return its verification status. + * See gnutls_x509_crt_list_verify() for a detailed description of + * return values. Note that since GnuTLS 3.1.4 this function includes + * the time checks. + * + * Note that value in @verify is set only when the return value of this + * function is success (i.e, failure to trust a CRL a certificate does not imply + * a negative return value). + * + * Before GnuTLS 3.5.7 this function would return zero or a positive + * number on success. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a + * negative error value. + **/ +int +gnutls_x509_crl_verify(gnutls_x509_crl_t crl, + const gnutls_x509_crt_t * trusted_cas, + unsigned tcas_size, unsigned int flags, + unsigned int *verify) +{ +/* CRL is ignored for now */ + gnutls_datum_t crl_signed_data = { NULL, 0 }; + gnutls_datum_t crl_signature = { NULL, 0 }; + gnutls_x509_crt_t issuer = NULL; + int result, sigalg; + time_t now = gnutls_time(0); + time_t nextu; + unsigned int usage; + + if (verify) + *verify = 0; + + if (tcas_size >= 1) + issuer = find_crl_issuer(crl, trusted_cas, tcas_size); + + result = + _gnutls_x509_get_signed_data(crl->crl, &crl->der, "tbsCertList", + &crl_signed_data); + if (result < 0) { + gnutls_assert(); + if (verify) + *verify |= GNUTLS_CERT_INVALID; + goto cleanup; + } + + result = + _gnutls_x509_get_signature(crl->crl, "signature", + &crl_signature); + if (result < 0) { + gnutls_assert(); + if (verify) + *verify |= GNUTLS_CERT_INVALID; + goto cleanup; + } + + sigalg = + _gnutls_x509_get_signature_algorithm(crl->crl, + "signatureAlgorithm"); + if (sigalg < 0) { + gnutls_assert(); + if (verify) + *verify |= GNUTLS_CERT_INVALID; + goto cleanup; + } + + /* issuer is not in trusted certificate + * authorities. + */ + if (issuer == NULL) { + gnutls_assert(); + if (verify) + *verify |= + GNUTLS_CERT_SIGNER_NOT_FOUND | + GNUTLS_CERT_INVALID; + } else { + if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN)) { + if (gnutls_x509_crt_get_ca_status(issuer, NULL) != 1) { + gnutls_assert(); + if (verify) + *verify |= + GNUTLS_CERT_SIGNER_NOT_CA | + GNUTLS_CERT_INVALID; + } + + result = + gnutls_x509_crt_get_key_usage(issuer, &usage, NULL); + if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + if (result < 0) { + gnutls_assert(); + if (verify) + *verify |= GNUTLS_CERT_INVALID; + } else if (!(usage & GNUTLS_KEY_CRL_SIGN)) { + gnutls_assert(); + if (verify) + *verify |= + GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE + | GNUTLS_CERT_INVALID; + } + } + } + + result = + _gnutls_x509_verify_data(sigalg, + &crl_signed_data, &crl_signature, + NULL, + issuer, flags); + if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) { + gnutls_assert(); + /* error. ignore it */ + if (verify) + *verify |= GNUTLS_CERT_SIGNATURE_FAILURE; + result = 0; + } else if (result == GNUTLS_E_CONSTRAINT_ERROR) { + if (verify) + *verify |= GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE; + result = 0; + } else if (result < 0) { + gnutls_assert(); + if (verify) + *verify |= GNUTLS_CERT_INVALID; + goto cleanup; + } else { + result = 0; /* everything ok */ + } + } + + { + sigalg = gnutls_x509_crl_get_signature_algorithm(crl); + + if (((sigalg == GNUTLS_SIGN_RSA_MD2) && + !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) || + ((sigalg == GNUTLS_SIGN_RSA_MD5) && + !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5))) { + if (verify) + *verify |= GNUTLS_CERT_INSECURE_ALGORITHM; + result = 0; + } + } + + if (gnutls_x509_crl_get_this_update(crl) > now && verify) + *verify |= GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE; + + nextu = gnutls_x509_crl_get_next_update(crl); + if (nextu != -1 && nextu < now && verify) + *verify |= GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED; + + + cleanup: + if (verify && *verify != 0) + *verify |= GNUTLS_CERT_INVALID; + + _gnutls_free_datum(&crl_signed_data); + _gnutls_free_datum(&crl_signature); + + return result; +} |