/* * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, * Free Software Foundation, 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 3 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 * */ /* All functions which relate to X.509 certificate verification stuff are * included here */ #include #include #include #include #include /* MAX */ #include #include #include #include #include #include static int is_crl_issuer (gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer_cert); static int _gnutls_verify_crl2 (gnutls_x509_crl_t crl, const gnutls_x509_crt_t * trusted_cas, int tcas_size, unsigned int flags, unsigned int *output); /* Checks if two certs are identical. Return 0 on match. */ int check_if_same_cert (gnutls_x509_crt_t cert1, gnutls_x509_crt_t cert2) { gnutls_datum_t cert1bin = { NULL, 0 }, cert2bin = { NULL, 0}; int result; opaque serial1[128], serial2[128]; size_t serial1_size, serial2_size; serial1_size = sizeof (serial1); result = gnutls_x509_crt_get_serial (cert1, serial1, &serial1_size); if (result < 0) { gnutls_assert (); goto cmp; } serial2_size = sizeof (serial2); result = gnutls_x509_crt_get_serial (cert2, serial2, &serial2_size); if (result < 0) { gnutls_assert (); goto cmp; } if (serial2_size != serial1_size || memcmp (serial1, serial2, serial1_size) != 0) { return 1; } cmp: result = _gnutls_x509_der_encode (cert1->cert, "", &cert1bin, 0); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_der_encode (cert2->cert, "", &cert2bin, 0); if (result < 0) { gnutls_assert (); goto cleanup; } if ((cert1bin.size == cert2bin.size) && (memcmp (cert1bin.data, cert2bin.data, cert1bin.size) == 0)) result = 0; else result = 1; cleanup: _gnutls_free_datum (&cert1bin); _gnutls_free_datum (&cert2bin); return result; } /* 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 int check_if_ca (gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer, 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 result; /* Check if the issuer is the same with the * certificate. This is added in order for trusted * certificates to be able to verify themselves. */ result = _gnutls_x509_get_signed_data (issuer->cert, "tbsCertificate", &issuer_signed_data); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_get_signed_data (cert->cert, "tbsCertificate", &cert_signed_data); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_get_signature (issuer->cert, "signature", &issuer_signature); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_get_signature (cert->cert, "signature", &cert_signature); if (result < 0) { gnutls_assert (); goto cleanup; } /* 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; } } result = gnutls_x509_crt_get_ca_status (issuer, NULL); if (result == 1) { 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 ((result == 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) == 1)))) { gnutls_assert (); result = 1; goto cleanup; } else gnutls_assert (); 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 'certs' issuer is 'issuer_cert'. * This does a straight (DER) compare of the issuer/subject fields in * the given certificates. * * Returns 1 if they match and (0) if they don't match. Otherwise * a negative error code is returned to indicate error. */ static int is_issuer (gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer_cert) { gnutls_datum_t dn1 = { NULL, 0 }, dn2 = { NULL, 0}; int ret; ret = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn1); if (ret < 0) { gnutls_assert (); goto cleanup; } ret = gnutls_x509_crt_get_raw_dn (issuer_cert, &dn2); if (ret < 0) { gnutls_assert (); goto cleanup; } ret = _gnutls_x509_compare_raw_dn (&dn1, &dn2); cleanup: _gnutls_free_datum (&dn1); _gnutls_free_datum (&dn2); return ret; } /* 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. */ int _gnutls_is_same_dn (gnutls_x509_crt_t cert1, gnutls_x509_crt_t cert2) { gnutls_datum_t dn1 = { NULL, 0 }, dn2 = { NULL, 0}; int ret; ret = gnutls_x509_crt_get_raw_dn (cert1, &dn1); if (ret < 0) { gnutls_assert (); goto cleanup; } ret = gnutls_x509_crt_get_raw_dn (cert2, &dn2); if (ret < 0) { gnutls_assert (); goto cleanup; } ret = _gnutls_x509_compare_raw_dn (&dn1, &dn2); cleanup: _gnutls_free_datum (&dn1); _gnutls_free_datum (&dn2); return ret; } /* 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]) == 1) { 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 (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; } /* * Verifies the given certificate again a certificate list of * trusted CAs. * * Returns only 0 or 1. If 1 it means that the certificate * was successfuly verified. * * 'flags': an OR of the gnutls_certificate_verify_flags enumeration. * * Output will hold some extra information about the verification * procedure. Issuer will hold the actual issuer from the trusted list. */ static int _gnutls_verify_certificate2 (gnutls_x509_crt_t cert, const gnutls_x509_crt_t * trusted_cas, int tcas_size, unsigned int flags, unsigned int *output, gnutls_x509_crt_t * _issuer, time_t now, gnutls_verify_output_function func) { gnutls_datum_t cert_signed_data = { NULL, 0 }; gnutls_datum_t cert_signature = { NULL, 0 }; gnutls_x509_crt_t issuer = NULL; int issuer_version, result, hash_algo; unsigned int out = 0; if (output) *output = 0; if (tcas_size >= 1) issuer = find_issuer (cert, trusted_cas, tcas_size); else { gnutls_assert (); out = GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID; if (output) *output |= out; result = 0; goto cleanup; } /* issuer is not in trusted certificate * authorities. */ if (issuer == NULL) { out = GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID; if (output) *output |= out; gnutls_assert (); result = 0; goto cleanup; } if (_issuer != NULL) *_issuer = issuer; issuer_version = gnutls_x509_crt_get_version (issuer); if (issuer_version < 0) { gnutls_assert (); return issuer_version; } 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, flags) == 0) { gnutls_assert (); out = GNUTLS_CERT_SIGNER_NOT_CA | GNUTLS_CERT_INVALID; if (output) *output |= out; result = 0; goto cleanup; } } result = _gnutls_x509_get_signed_data (cert->cert, "tbsCertificate", &cert_signed_data); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_get_signature (cert->cert, "signature", &cert_signature); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_get_signature_algorithm(cert->cert, "signatureAlgorithm.algorithm"); if (result < 0) { gnutls_assert (); goto cleanup; } hash_algo = _gnutls_sign_get_hash_algorithm(result); result = _gnutls_x509_verify_data (hash_algo, &cert_signed_data, &cert_signature, issuer); if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) { gnutls_assert (); out |= GNUTLS_CERT_INVALID; /* error. ignore it */ if (output) *output |= out; result = 0; } else if (result < 0) { gnutls_assert(); goto cleanup; } /* 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 (is_issuer (cert, cert) == 0) { int sigalg; sigalg = gnutls_x509_crt_get_signature_algorithm (cert); 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))) { out = GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID; if (output) *output |= out; result = 0; } } /* Check activation/expiration times */ if (!(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) { /* check the time of the issuer first */ if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS)) { out |= check_time (issuer, now); if (out != 0) { result = 0; if (output) *output |= out; } } out |= check_time (cert, now); if (out != 0) { result = 0; if (output) *output |= out; } } cleanup: if (result >= 0 && func) 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. * * Returns: It will return true (1) if the given certificate is issued * by the given issuer, and false (0) if not. A negative error code is * returned in case of an error. **/ int gnutls_x509_crt_check_issuer (gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer) { return is_issuer (cert, issuer); } /* 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_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list, int clist_size, const gnutls_x509_crt_t * trusted_cas, int tcas_size, unsigned int flags, gnutls_verify_output_function func) { int i = 0, ret; unsigned int status = 0, output; time_t now = gnutls_time (0); gnutls_x509_crt_t issuer = NULL; 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++) { if (check_if_same_cert (certificate_list[i], trusted_cas[j]) == 0) { /* explicity 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 (trusted_cas[j], now); if (status != 0) { if (func) func(certificate_list[i], trusted_cas[j], NULL, status); return status; } } if (func) func(certificate_list[i], trusted_cas[j], NULL, 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; } /* 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 = _gnutls_verify_certificate2 (certificate_list[clist_size - 1], trusted_cas, tcas_size, flags, &output, &issuer, now, func); if (ret == 0) { /* if the last certificate in the certificate * list is invalid, then the certificate is not * trusted. */ gnutls_assert (); status |= output; status |= GNUTLS_CERT_INVALID; return status; } /* Verify the certificate path (chain) */ for (i = clist_size - 1; i > 0; i--) { output = 0; if (i - 1 < 0) break; /* 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_ALLOW_X509_V1_CA_CRT); if ((ret = _gnutls_verify_certificate2 (certificate_list[i - 1], &certificate_list[i], 1, flags, &output, NULL, now, func)) == 0) { status |= output; status |= GNUTLS_CERT_INVALID; return status; } } return 0; } /* This will return the appropriate hash to verify the given signature. * If signature is NULL it will return an (or the) appropriate hash for * the given parameters. */ int _gnutls_x509_verify_algorithm (gnutls_mac_algorithm_t * hash, const gnutls_datum_t * signature, gnutls_pk_algorithm_t pk, gnutls_pk_params_st * issuer_params) { opaque digest[MAX_HASH_SIZE]; gnutls_datum_t decrypted; int digest_size; int ret; switch (pk) { case GNUTLS_PK_DSA: case GNUTLS_PK_EC: if (hash) *hash = _gnutls_dsa_q_to_hash (pk, issuer_params, NULL); ret = 0; break; case GNUTLS_PK_RSA: if (signature == NULL) { /* return a sensible algorithm */ if (hash) *hash = GNUTLS_DIG_SHA256; return 0; } ret = _gnutls_pkcs1_rsa_decrypt (&decrypted, signature, issuer_params, 1); if (ret < 0) { gnutls_assert (); goto cleanup; } digest_size = sizeof (digest); if ((ret = decode_ber_digest_info (&decrypted, hash, digest, &digest_size)) != 0) { gnutls_assert (); _gnutls_free_datum (&decrypted); goto cleanup; } _gnutls_free_datum (&decrypted); if (digest_size != _gnutls_hash_get_algo_len (*hash)) { gnutls_assert (); ret = GNUTLS_E_ASN1_GENERIC_ERROR; goto cleanup; } ret = 0; break; default: gnutls_assert (); ret = GNUTLS_E_INTERNAL_ERROR; } cleanup: return ret; } /* 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! */ int _gnutls_x509_verify_data (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, const gnutls_datum_t * signature, gnutls_x509_crt_t issuer) { gnutls_pk_params_st issuer_params; int ret; /* Read the MPI parameters from the issuer's certificate. */ ret = _gnutls_x509_crt_get_mpis (issuer, &issuer_params); if (ret < 0) { gnutls_assert (); return ret; } ret = pubkey_verify_data (gnutls_x509_crt_get_pk_algorithm (issuer, NULL), algo, data, signature, &issuer_params); if (ret < 0) { gnutls_assert (); } /* release all allocated MPIs */ gnutls_pk_params_release(&issuer_params); return ret; } int _gnutls_x509_verify_hashed_data (const gnutls_datum_t * hash, const gnutls_datum_t * signature, gnutls_x509_crt_t issuer) { gnutls_pk_params_st issuer_params; int ret; /* Read the MPI parameters from the issuer's certificate. */ ret = _gnutls_x509_crt_get_mpis (issuer, &issuer_params); if (ret < 0) { gnutls_assert (); return ret; } ret = pubkey_verify_hashed_data (gnutls_x509_crt_get_pk_algorithm (issuer, NULL), hash, signature, &issuer_params); if (ret < 0) { gnutls_assert (); } /* release all allocated MPIs */ gnutls_pk_params_release(&issuer_params); 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. If no flags are specified (0), this function * will use the basicConstraints (2.5.29.19) PKIX extension. This * means that only a certificate authority is allowed to sign a * certificate. * * You must also 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, int cert_list_length, const gnutls_x509_crt_t * CA_list, int CA_list_length, const gnutls_x509_crl_t * CRL_list, int CRL_list_length, unsigned int flags, unsigned int *verify) { int i, ret; if (cert_list == NULL || cert_list_length == 0) return GNUTLS_E_NO_CERTIFICATE_FOUND; /* Verify certificate */ *verify = _gnutls_x509_verify_certificate (cert_list, cert_list_length, CA_list, CA_list_length, flags, NULL); /* Check for revoked certificates in the chain. */ #ifdef ENABLE_PKI 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; } } #endif 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. * * 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, int CA_list_length, unsigned int flags, unsigned int *verify) { /* Verify certificate */ *verify = _gnutls_x509_verify_certificate (&cert, 1, CA_list, CA_list_length, flags, NULL); return 0; } #ifdef ENABLE_PKI /** * 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. It will return true (1) if the given CRL was * issued by the given issuer, and false (0) if not. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crl_check_issuer (gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer) { return is_crl_issuer (crl, issuer); } /** * gnutls_x509_crl_verify: * @crl: is the crl to be verified * @CA_list: is a certificate list that is considered to be trusted one * @CA_list_length: 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 status. * See gnutls_x509_crt_list_verify() for a detailed description of * return values. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_crl_verify (gnutls_x509_crl_t crl, const gnutls_x509_crt_t * CA_list, int CA_list_length, unsigned int flags, unsigned int *verify) { int ret; /* Verify crl */ ret = _gnutls_verify_crl2 (crl, CA_list, CA_list_length, flags, verify); if (ret < 0) { gnutls_assert (); return ret; } return 0; } /* The same as above, but here we've got a CRL. */ static int is_crl_issuer (gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer_cert) { gnutls_datum_t dn1 = { NULL, 0 }, dn2 = { NULL, 0}; int ret; ret = gnutls_x509_crl_get_raw_issuer_dn (crl, &dn1); if (ret < 0) { gnutls_assert (); goto cleanup; } ret = gnutls_x509_crt_get_raw_dn (issuer_cert, &dn2); if (ret < 0) { gnutls_assert (); return ret; } ret = _gnutls_x509_compare_raw_dn (&dn1, &dn2); cleanup: _gnutls_free_datum (&dn1); _gnutls_free_datum (&dn2); return ret; } 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]) == 1) return trusted_cas[i]; } gnutls_assert (); return NULL; } /* * Returns only 0 or 1. If 1 it means that the CRL * was successfuly verified. * * 'flags': an OR of the gnutls_certificate_verify_flags enumeration. * * Output will hold information about the verification * procedure. */ static int _gnutls_verify_crl2 (gnutls_x509_crl_t crl, const gnutls_x509_crt_t * trusted_cas, int tcas_size, unsigned int flags, unsigned int *output) { /* 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; int result, hash_algo; if (output) *output = 0; if (tcas_size >= 1) issuer = find_crl_issuer (crl, trusted_cas, tcas_size); else { gnutls_assert (); if (output) *output |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID; return 0; } /* issuer is not in trusted certificate * authorities. */ if (issuer == NULL) { gnutls_assert (); if (output) *output |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID; return 0; } if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN)) { if (gnutls_x509_crt_get_ca_status (issuer, NULL) != 1) { gnutls_assert (); if (output) *output |= GNUTLS_CERT_SIGNER_NOT_CA | GNUTLS_CERT_INVALID; return 0; } } result = _gnutls_x509_get_signed_data (crl->crl, "tbsCertList", &crl_signed_data); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_get_signature (crl->crl, "signature", &crl_signature); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_get_signature_algorithm(crl->crl, "signatureAlgorithm.algorithm"); if (result < 0) { gnutls_assert (); goto cleanup; } hash_algo = _gnutls_sign_get_hash_algorithm(result); result = _gnutls_x509_verify_data (hash_algo, &crl_signed_data, &crl_signature, issuer); if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) { gnutls_assert (); /* error. ignore it */ if (output) *output |= GNUTLS_CERT_INVALID; result = 0; } else if (result < 0) { gnutls_assert (); goto cleanup; } { int sigalg; 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 (output) *output |= GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID; result = 0; } } cleanup: _gnutls_free_datum (&crl_signed_data); _gnutls_free_datum (&crl_signature); return result; } #endif