From: Simon Josefsson Date: Mon, 2 Feb 2009 16:21:17 +0000 (+0100) Subject: Make it possible to trust intermediary certificates. X-Git-Tag: gnutls_2_7_5~5 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0ca6c0eeb67e3be4f6d79d775f23c3fccba97444;p=thirdparty%2Fgnutls.git Make it possible to trust intermediary certificates. Based on tiny patch from "Douglas E. Engert" in . --- diff --git a/NEWS b/NEWS index a78281d119..6854cca35b 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,19 @@ See the end for copying conditions. * Version 2.7.5 (unreleased) +** libgnutls: Accept chains where intermediary certs are trusted. +Before GnuTLS needed to validate the entire chain back to a +self-signed certificate. GnuTLS will now stop looking when it has +found an intermediary trusted certificate. The new behaviour is +useful when chains, for example, contains a top-level CA, an +intermediary CA signed using RSA-MD5, and an end-entity certificate. +To avoid chain validation errors due to the RSA-MD5 cert, you can +explicitly add the intermediary RSA-MD5 cert to your trusted certs. +The signature on trusted certificates are not checked, so the chain +has a chance to validate correctly. Reported by "Douglas E. Engert" + in +. + ** libgnutls: result_size in gnutls_hex_encode now holds the size of the result. Report by John Brooks . diff --git a/doc/TODO b/doc/TODO index d9c900a023..beec4237d8 100644 --- a/doc/TODO +++ b/doc/TODO @@ -48,8 +48,6 @@ Current list: - Support non-blocking gnutls_{dh,rsa}_params_generate2 for when there is not enough entropy available. - Chain verifications. - - Short-cut the certificate verification algorithm before the - root if a middle-CA is trusted. - Check path length constraints. - Check keyCertSign key usages. - Reject extensions in v1 certificates. diff --git a/lib/x509/verify.c b/lib/x509/verify.c index c4fab1df3e..ee66060227 100644 --- a/lib/x509/verify.c +++ b/lib/x509/verify.c @@ -51,6 +51,38 @@ static int _gnutls_verify_crl2 (gnutls_x509_crl_t crl, int tcas_size, unsigned int flags, unsigned int *output); +/* Checks if two certs are identical. Return 0 onn match. */ +static 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; + + 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 @@ -365,16 +397,12 @@ gnutls_x509_crt_check_issuer (gnutls_x509_crt_t cert, } -/* The algorithm used is: - * 1. Check last certificate in the chain. If it is not verified return. - * 2. Check if any certificates in the chain are revoked. If yes return. - * 3. Try to verify the rest of certificates in the chain. If not verified return. - * 4. Return 0. +/* 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 CA in order to be trusted. + * This function verifies a X.509 certificate list. The certificate + * list should lead to a trusted certificate in order to be trusted. */ static unsigned int _gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list, @@ -387,16 +415,32 @@ _gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list, int i = 0, ret; unsigned int status = 0, output; + /* Check for revoked certificates in the chain + */ +#ifdef ENABLE_PKI + for (i = 0; i < clist_size; i++) + { + ret = gnutls_x509_crt_check_revocation (certificate_list[i], + CRLs, crls_size); + if (ret == 1) + { /* revoked */ + status |= GNUTLS_CERT_REVOKED; + status |= GNUTLS_CERT_INVALID; + return status; + } + } +#endif + 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 in addition 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. + * 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) @@ -405,16 +449,38 @@ _gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list, } } + /* 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. We must leave the first cert on chain. */ + if (clist_size > 1 && !(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME)) + { + for (i = 1; 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) + { + clist_size = i; + break; + } + } + /* clist_size may have been changed which gets out of loop */ + } + } + /* 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. */ - ret = - _gnutls_verify_certificate2 (certificate_list[clist_size - 1], - trusted_cas, tcas_size, flags, &output); - + ret = _gnutls_verify_certificate2 (certificate_list[clist_size - 1], + trusted_cas, tcas_size, flags, &output); if (ret == 0) { /* if the last certificate in the certificate @@ -427,23 +493,7 @@ _gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list, return status; } - /* Check for revoked certificates in the chain - */ -#ifdef ENABLE_PKI - for (i = 0; i < clist_size; i++) - { - ret = gnutls_x509_crt_check_revocation (certificate_list[i], - CRLs, crls_size); - if (ret == 1) - { /* revoked */ - status |= GNUTLS_CERT_REVOKED; - status |= GNUTLS_CERT_INVALID; - return status; - } - } -#endif - - /* Verify the certificate path (chain) + /* Verify the certificate path (chain) */ for (i = clist_size - 1; i > 0; i--) { diff --git a/tests/chainverify.c b/tests/chainverify.c index 7647d59f52..2da18ff163 100644 --- a/tests/chainverify.c +++ b/tests/chainverify.c @@ -703,6 +703,11 @@ static struct 0, GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID }, { "cacertrsamd5 ok", cacertrsamd5, &cacertrsamd5[2], GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5, 0 }, + { "cacertrsamd5 short-cut not ok", cacertrsamd5, &cacertrsamd5[1], + GNUTLS_VERIFY_DO_NOT_ALLOW_SAME, + GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID }, + { "cacertrsamd5 short-cut ok", cacertrsamd5, &cacertrsamd5[1], + 0, 0 }, { NULL, NULL, NULL, 0} }; /* *INDENT-ON* */