summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Josefsson <simon@josefsson.org>2009-02-02 17:38:53 +0100
committerSimon Josefsson <simon@josefsson.org>2009-02-02 17:38:53 +0100
commit8770b1cf409811decc278f63f3cf634d0f30027a (patch)
tree85d45e707830c8cd878524ffd75f748f15df4e9e
parent1a341a09c2c14fc80e9ca1c542ebfcb24a5fc44f (diff)
downloadgnutls-8770b1cf409811decc278f63f3cf634d0f30027a.tar.gz
Make it possible to trust intermediary certificates.
Based on tiny patch from "Douglas E. Engert" <deengert@anl.gov> in <http://thread.gmane.org/gmane.comp.encryption.gpg.gnutls.devel/3351/focus=3376>.
-rw-r--r--NEWS13
-rw-r--r--lib/x509/verify.c116
2 files changed, 96 insertions, 33 deletions
diff --git a/NEWS b/NEWS
index 0706b9dd2e..7f44ce316b 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,19 @@ See the end for copying conditions.
* Version 2.6.4 (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"
+<deengert@anl.gov> in
+<http://thread.gmane.org/gmane.comp.encryption.gpg.gnutls.devel/3351>.
+
** libgnutls: result_size in gnutls_hex_encode now holds
the size of the result. Report by John Brooks <special@dereferenced.net>.
diff --git a/lib/x509/verify.c b/lib/x509/verify.c
index ffc7704d7d..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,34 +415,72 @@ _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)
+ 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. 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--)
{