Some months ago people requested support for Certificate Revokation Lists
(CRL) in mod_ssl and I've now found a little bit of extra time to port some
old code from Douglas E. Engert and the GLOBUS project (which was posted to
the SSLeay mailing lists one year ago) to mod_ssl+OpenSSL. The appended patch
adds CRL support to the certificate verification process of mod_ssl and should
do whatwhat people requested. 

Just apply it to mod_ssl 2.2.7's src/modules/ssl/ directory and add your CRLs
to the SSLCACertificatePath dir and make sure a hash symlink exists (use the
"openssl crl -noout -hash" command manually until I add support for this to
the ssl.crt/Makefile).

Feedback is welcome!
                                       Ralf S. Engelschall
                                       [EMAIL PROTECTED]
                                       www.engelschall.com

Index: mod_ssl.h
===================================================================
RCS file: /e/modssl/REPOS/mod_ssl/pkg.apache/src/modules/ssl/mod_ssl.h,v
retrieving revision 1.85
diff -u -r1.85 mod_ssl.h
--- mod_ssl.h   1999/03/29 10:33:39     1.85
+++ mod_ssl.h   1999/03/30 10:41:40
@@ -575,6 +575,7 @@
 RSA         *ssl_callback_TmpRSA(SSL *, int);
 #endif
 int          ssl_callback_SSLVerify(int, X509_STORE_CTX *);
+int          ssl_callback_SSLVerify_CRL(int, X509_STORE_CTX *, SSL *, conn_rec *, 
+request_rec *);
 int          ssl_callback_NewSessionCacheEntry(SSL *, SSL_SESSION *);
 SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *, unsigned char *, int, int *);
 void         ssl_callback_DelSessionCacheEntry(SSL_CTX *, SSL_SESSION *);
Index: ssl_engine_kernel.c
===================================================================
RCS file: /e/modssl/REPOS/mod_ssl/pkg.apache/src/modules/ssl/ssl_engine_kernel.c,v
retrieving revision 1.77
diff -u -r1.77 ssl_engine_kernel.c
--- ssl_engine_kernel.c 1999/03/29 10:33:39     1.77
+++ ssl_engine_kernel.c 1999/03/30 13:37:37
@@ -1200,6 +1200,12 @@
     }
 
     /*
+     * Additionally perform CRL-based revokation checks
+     */
+    if (ok)
+        ok = ssl_callback_SSLVerify_CRL(ok, ctx, ssl, conn, r); 
+
+    /*
      * If we already know it's not ok, log the real reason
      */
     if (!ok) {
@@ -1231,6 +1237,152 @@
      * And finally signal OpenSSL the (perhaps changed) state
      */
     return (ok);
+}
+
+int ssl_callback_SSLVerify_CRL(
+    int ok, X509_STORE_CTX *ctx, SSL *ssl, conn_rec *conn, request_rec *r)
+{
+#ifdef SSL_EXPERIMENTAL
+    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;
+
+    /* 
+     * OpenSSL provides the mechanism to handle CRLs but does not use them
+     * implicitly when verifying certificates, so we do it explicity 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. At each step we've to both verify
+     * the signature on the CRL (to make sure it's a valid CRL) and it's
+     * revokation list (to make sure the current certificate it's revoked).
+     * But because to check the signature on the CRL we need the public key of
+     * the issueing 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 revokation 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.
+     */
+
+    /*
+     * 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);
+
+    /*
+     * 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  = X509_STORE_get_by_subject(ctx, X509_LU_CRL, subject, &obj);
+    crl = obj.data.crl;
+    if (rc > 0 && crl != NULL) {
+
+        /*
+         * Log information about CRL
+         */
+        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] = NUL;
+        BIO_free(bio);
+        cp2 = X509_NAME_oneline(subject, NULL, 0);
+        ssl_log(conn->server, SSL_LOG_TRACE, "Found CRL: %s, DN: %s", cp, cp2);
+        free(cp);
+        free(cp2);
+
+        /* 
+         * Verify the signature on this CRL
+         */
+        if (X509_CRL_verify(crl, X509_get_pubkey(xs)) <= 0) {
+            ssl_log(conn->server, SSL_LOG_ERROR, "Invalid signature on CRL");
+            ctx->error = X509_V_ERR_CRL_SIGNATURE_FAILURE;
+            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) {
+            ssl_log(conn->server, SSL_LOG_ERROR, "Invalid nextUpdate field in CRL");
+            ctx->error = X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD;
+            return FALSE;
+        }
+        if (i < 0) {
+            ssl_log(conn->server, SSL_LOG_ERROR, 
+                    "Found CRL is expired - "
+                    "revoking all certificates until you get updated CRL");
+            ctx->error = X509_V_ERR_CRL_HAS_EXPIRED;
+            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 revokation.
+     */
+    memset((char *)&obj, 0, sizeof(obj));
+    rc  = X509_STORE_get_by_subject(ctx, X509_LU_CRL, issuer, &obj);
+    crl = obj.data.crl;
+    if (rc > 0 && crl != NULL) {
+
+        /* 
+         * Check if the current certificate is revoked by this CRL
+         */
+        n = sk_num(X509_CRL_get_REVOKED(crl));
+        for (i = 0; i < n; i++) {
+            revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i);
+            if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(xs)) == 
+0) {
+
+                serial = ASN1_INTEGER_get(revoked->serialNumber);
+                cp     = X509_NAME_oneline(issuer, NULL, 0);
+                ssl_log(conn->server, SSL_LOG_ERROR,
+                        "Certificate with serial number %ld (0x%lX) "
+                        "revoked per CRL (Issuer: %s)",
+                        serial, serial, cp);
+                free(cp);
+
+                ctx->error = X509_V_ERR_CERT_REVOKED;
+                return FALSE;
+            }
+        }
+
+        X509_OBJECT_free_contents(&obj);
+    }
+#endif /* SSL_EXPERIMENTAL */
+
+   return ok;
 }
 
 /*
______________________________________________________________________
Apache Interface to OpenSSL (mod_ssl)  www.engelschall.com/sw/mod_ssl/
Official Support Mailing List               [EMAIL PROTECTED]
Automated List Manager                       [EMAIL PROTECTED]

Reply via email to