Re: [Exim] CRL support for exim 4.30 with gnutls 1.0.4 (patc…

Αρχική Σελίδα
Delete this message
Reply to this message
Συντάκτης: Vivek
Ημερομηνία:  
Προς: exim-users
Αντικείμενο: Re: [Exim] CRL support for exim 4.30 with gnutls 1.0.4 (patch)
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 */
--