This message is in MIME format. The first part should be readable text,
while the remaining parts are likely unreadable without MIME-aware tools.
Send mail to mime@??? for more info.
--
> > I also have a patch against openssl that I ported from one that someone
> > at work did, but it's more complicated and I haven't tested that one yet,
> > and it needs a little work anyway to make it work in a similar manner
> > to the gnutls patch.
>
> That would also be useful.
Ok. A big chunk of this patch is from the apache mod_ssl and is by
Ralf S Engelschall, released under a BSD with advertising style licence,
so it may not be possible to include directly - It is cumulative with
the previous patch for gnutls, in that it uses the new config parameter
added by that patch (but is otherwise independent of it).
--
Content-Description:
--- src/tls-openssl.c Fri Jan 23 18:33:13 2004
+++ src/tls-openssl.c Fri Jan 23 19:41:52 2004
@@ -41,8 +41,11 @@
static int ssl_session_timeout = 200;
static BOOL verify_optional = FALSE;
-
-
+/* +CRL support */
+static X509_STORE *Revocation_store=NULL;
+X509_STORE *SSL_X509_STORE_create(char *cpFile, char *cpPath);
+int ssl_callback_SSLVerify_CRL(int ok, X509_STORE_CTX *ctx);
+/* -CRL support */
/*************************************************
@@ -182,6 +185,23 @@
if (!verify_callback_called) tls_certificate_verified = TRUE;
verify_callback_called = TRUE;
+/* +CRL support */
+/* Additionally perform CRL-based revocation checks as per Apache's mod_ssl */
+if (state == 1)
+ {
+ DEBUG(D_tls) debug_printf("About to call ssl_callback_SSLVerify_CRL\n");
+ state = ssl_callback_SSLVerify_CRL(state, x509ctx);
+ if (state == 0)
+ {
+ log_write(1, LOG_MAIN, "SSL CRL error: depth=%d error=%s cert=%s",
+ x509ctx->error_depth,
+ X509_verify_cert_error_string(x509ctx->error),
+ txt);
+ return 0; /* reject */
+ }
+ }
+/* -CRL support */
+
return 1; /* accept */
}
@@ -455,13 +475,17 @@
*/
static int
-setup_certs(uschar *certs, host_item *host, BOOL optional)
+setup_certs(uschar *certs, uschar *crls, host_item *host, BOOL optional)
{
uschar *expcerts;
-
+uschar *expcrl = NULL;
+
if (!expand_check(certs, US"tls_verify_certificates", &expcerts))
return DEFER;
+if (crls && !expand_check(crls, US"tls_crl", &expcrl))
+ return DEFER;
+
if (expcerts != NULL)
{
struct stat statbuf;
@@ -483,6 +507,36 @@
return tls_error(US"SSL_CTX_load_verify_locations", host);
if (file != NULL)
SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CS file));
+/* +CRL support */
+/* Init CRL stuff from tls_crl */
+ if ( expcrl != NULL )
+ {
+ /* Free up the old store, as we've been called again */
+ if (Revocation_store != NULL)
+ {
+ X509_STORE_free(Revocation_store);
+ Revocation_store = NULL;
+ }
+
+ /* stat the CRLs to see if they're a dir or a file */
+ if (Ustat(expcrl, &statbuf) < 0)
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "failed to stat %s for CRLs", expcrl);
+ }
+ else
+ {
+ if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
+ { file = NULL; dir = expcrl; }
+ else
+ { file = expcrl; dir = NULL; }
+ DEBUG(D_tls)
+ debug_printf("About to call SSL_X509_STORE_create(%s, %s)\n",
+ file, dir);
+ if ((Revocation_store = SSL_X509_STORE_create(file, dir)) == NULL)
+ return tls_error("SSL_X509_STORE_create", host);
+ }
+ }
+/* -CRL support */
}
/* If verification is optional, don't fail if no certificate */
@@ -543,13 +597,13 @@
if (verify_check_host(&tls_verify_hosts) == OK)
{
- rc = setup_certs(tls_verify_certificates, NULL, FALSE);
+ rc = setup_certs(tls_verify_certificates, tls_crl, NULL, FALSE);
if (rc != OK) return rc;
verify_optional = FALSE;
}
else if (verify_check_host(&tls_try_verify_hosts) == OK)
{
- rc = setup_certs(tls_verify_certificates, NULL, TRUE);
+ rc = setup_certs(tls_verify_certificates, tls_crl, NULL, TRUE);
if (rc != OK) return rc;
verify_optional = TRUE;
}
@@ -676,7 +730,7 @@
return tls_error(US"SSL_CTX_set_cipher_list", host);
}
-rc = setup_certs(verify_certs, host, FALSE);
+rc = setup_certs(verify_certs, NULL, host, FALSE);
if (rc != OK) return rc;
if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", host);
@@ -912,5 +966,257 @@
tls_active = -1;
}
+
+/* +CRL support */
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@???> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@???.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@???> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/* _________________________________________________________________
+**
+** Certificate Revocation List (CRL) Storage
+** _________________________________________________________________
+*/
+
+X509_STORE *SSL_X509_STORE_create(char *cpFile, char *cpPath)
+{
+ X509_STORE *pStore;
+ X509_LOOKUP *pLookup;
+
+ if (cpFile == NULL && cpPath == NULL)
+ return NULL;
+ if ((pStore = X509_STORE_new()) == NULL)
+ return NULL;
+ if (cpFile != NULL) {
+ if ((pLookup = X509_STORE_add_lookup(pStore, X509_LOOKUP_file())) == NULL) {
+ X509_STORE_free(pStore);
+ return NULL;
+ }
+ X509_LOOKUP_load_file(pLookup, cpFile, X509_FILETYPE_PEM);
+ }
+ if (cpPath != NULL) {
+ if ((pLookup = X509_STORE_add_lookup(pStore, X509_LOOKUP_hash_dir())) == NULL) {
+ X509_STORE_free(pStore);
+ return NULL;
+ }
+ X509_LOOKUP_add_dir(pLookup, cpPath, X509_FILETYPE_PEM);
+ }
+ return pStore;
+}
+
+int SSL_X509_STORE_lookup(X509_STORE *pStore, int nType,
+ X509_NAME *pName, X509_OBJECT *pObj)
+{
+ X509_STORE_CTX pStoreCtx;
+ int rc;
+
+ X509_STORE_CTX_init(&pStoreCtx, pStore, NULL, NULL);
+ rc = X509_STORE_get_by_subject(&pStoreCtx, nType, pName, pObj);
+ X509_STORE_CTX_cleanup(&pStoreCtx);
+ return rc;
+}
+
+int ssl_callback_SSLVerify_CRL(
+ int ok, X509_STORE_CTX *ctx)
+{
+ X509_OBJECT obj;
+ X509_NAME *subject;
+ X509_NAME *issuer;
+ X509 *xs;
+ X509_CRL *crl;
+ X509_REVOKED *revoked;
+ long serial;
+ BIO *bio;
+ int i, n, rc;
+ char *cp;
+ char *cp2;
+
+ /*
+ * Determine certificate ingredients in advance
+ */
+ xs = X509_STORE_CTX_get_current_cert(ctx);
+ subject = X509_get_subject_name(xs);
+ issuer = X509_get_issuer_name(xs);
+
+ /*
+ * OpenSSL provides the general mechanism to deal with CRLs but does not
+ * use them automatically when verifying certificates, so we do it
+ * explicitly here. We will check the CRL for the currently checked
+ * certificate, if there is such a CRL in the store.
+ *
+ * We come through this procedure for each certificate in the certificate
+ * chain, starting with the root-CA's certificate. At each step we've to
+ * both verify the signature on the CRL (to make sure it's a valid CRL)
+ * and it's revocation list (to make sure the current certificate isn't
+ * revoked). But because to check the signature on the CRL we need the
+ * public key of the issuing CA certificate (which was already processed
+ * one round before), we've a little problem. But we can both solve it and
+ * at the same time optimize the processing by using the following
+ * verification scheme (idea and code snippets borrowed from the GLOBUS
+ * project):
+ *
+ * 1. We'll check the signature of a CRL in each step when we find a CRL
+ * through the _subject_ name of the current certificate. This CRL
+ * itself will be needed the first time in the next round, of course.
+ * But we do the signature processing one round before this where the
+ * public key of the CA is available.
+ *
+ * 2. We'll check the revocation list of a CRL in each step when
+ * we find a CRL through the _issuer_ name of the current certificate.
+ * This CRLs signature was then already verified one round before.
+ *
+ * This verification scheme allows a CA to revoke its own certificate as
+ * well, of course.
+ */
+
+ /*
+ * Try to retrieve a CRL corresponding to the _subject_ of
+ * the current certificate in order to verify it's integrity.
+ */
+ memset((char *)&obj, 0, sizeof(obj));
+ rc = SSL_X509_STORE_lookup(Revocation_store, X509_LU_CRL, subject, &obj);
+ crl = obj.data.crl;
+ if (rc > 0 && crl != NULL) {
+ /*
+ * Log information about CRL
+ * (A little bit complicated because of ASN.1 and BIOs...)
+ */
+ DEBUG(D_tls) {
+ bio = BIO_new(BIO_s_mem());
+ BIO_printf(bio, "lastUpdate: ");
+ ASN1_UTCTIME_print(bio, X509_CRL_get_lastUpdate(crl));
+ BIO_printf(bio, ", nextUpdate: ");
+ ASN1_UTCTIME_print(bio, X509_CRL_get_nextUpdate(crl));
+ n = BIO_pending(bio);
+ cp = malloc(n+1);
+ n = BIO_read(bio, cp, n);
+ cp[n] = '\0';
+ BIO_free(bio);
+ cp2 = X509_NAME_oneline(subject, NULL, 0);
+ debug_printf("CA CRL: Issuer: %s, %s\n", cp2, cp);
+ free(cp2);
+ free(cp);
+ }
+
+ /*
+ * Verify the signature on this CRL
+ */
+ if (X509_CRL_verify(crl, X509_get_pubkey(xs)) <= 0) {
+ log_write(0, LOG_MAIN, "Invalid signature on CRL");
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
+ X509_OBJECT_free_contents(&obj);
+ return FALSE;
+ }
+
+ /*
+ * Check date of CRL to make sure it's not expired
+ */
+ i = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl));
+ if (i == 0) {
+ log_write(0, LOG_MAIN, "Found CRL has invalid nextUpdate field");
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
+ X509_OBJECT_free_contents(&obj);
+ return FALSE;
+ }
+ if (i < 0) {
+ log_write(0, LOG_MAIN,
+ "Found CRL is expired - "
+ "revoking all certificates until you get updated CRL");
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED);
+ X509_OBJECT_free_contents(&obj);
+ return FALSE;
+ }
+ X509_OBJECT_free_contents(&obj);
+ }
+
+ /*
+ * Try to retrieve a CRL corresponding to the _issuer_ of
+ * the current certificate in order to check for revocation.
+ */
+ memset((char *)&obj, 0, sizeof(obj));
+ rc = SSL_X509_STORE_lookup(Revocation_store, X509_LU_CRL, issuer, &obj);
+ crl = obj.data.crl;
+ if (rc > 0 && crl != NULL) {
+ /*
+ * Check if the current certificate is revoked by this CRL
+ */
+#if SSL_LIBRARY_VERSION < 0x00904000
+ n = sk_num(X509_CRL_get_REVOKED(crl));
+#else
+ n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
+#endif
+ for (i = 0; i < n; i++) {
+#if SSL_LIBRARY_VERSION < 0x00904000
+ revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i);
+#else
+ revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
+#endif
+ if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(xs)) == 0) {
+
+ serial = ASN1_INTEGER_get(revoked->serialNumber);
+ cp = X509_NAME_oneline(issuer, NULL, 0);
+ log_write(0, LOG_MAIN,
+ "Certificate with serial %ld (0x%lX) "
+ "revoked per CRL from issuer %s",
+ serial, serial, cp);
+ free(cp);
+
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED);
+ X509_OBJECT_free_contents(&obj);
+ return FALSE;
+ }
+ }
+ X509_OBJECT_free_contents(&obj);
+ }
+ return ok;
+ }
+/* -CRL support */
/* End of tls-openssl.c */
--