This patch modify squid cert validation subsystem to sent to cert
validator helper the complete certificates chain, not only the
certificates sent by web server. This is may not be possible in all
cases, for example  in cases where the root certificate is not stored
locally.

This is a Measurement Factory project

Sending root certificate for validation

This patch modify squid cert validation subsystem to sent to cert validator
helper the complete certificates chain, not only the certificates sent by
web server. This is may not be possible in all cases, for example  in cases
where the root certificate is not stored localy.

Also this patch includes a small optimization, it checks for domain mismatch
error only when the checked (current) certificate is the server certificate.

This is a Measurement Factory project

=== modified file 'src/globals.h'
--- src/globals.h	2013-05-13 22:48:23 +0000
+++ src/globals.h	2013-06-07 09:39:53 +0000
@@ -119,31 +119,32 @@
 extern unsigned int WIN32_Socks_initialized;	/* 0 */
 #endif
 #if _SQUID_WINDOWS_
 extern unsigned int WIN32_OS_version;	/* 0 */
 extern char *WIN32_OS_string;           /* NULL */
 extern char *WIN32_Service_name;        /* NULL */
 extern char *WIN32_Command_Line;        /* NULL */
 extern char *WIN32_Service_Command_Line; /* NULL */
 extern unsigned int WIN32_run_mode;     /* _WIN_SQUID_RUN_MODE_INTERACTIVE */
 #endif
 #if HAVE_SBRK
 extern void *sbrk_start;	/* 0 */
 #endif
 
 extern int ssl_ex_index_server;	/* -1 */
 extern int ssl_ctx_ex_index_dont_verify_domain; /* -1 */
 extern int ssl_ex_index_cert_error_check;	/* -1 */
 extern int ssl_ex_index_ssl_error_detail;      /* -1 */
 extern int ssl_ex_index_ssl_peeked_cert;      /* -1 */
 extern int ssl_ex_index_ssl_errors;   /* -1 */
+extern int ssl_ex_index_ssl_cert_chain;  /* -1 */
 
 extern const char *external_acl_message;      /* NULL */
 extern int opt_send_signal;	/* -1 */
 extern int opt_no_daemon; /* 0 */
 extern int opt_parse_cfg_only; /* 0 */
 
 /// current Squid process number (e.g., 4).
 /// Zero for SMP-unaware code and in no-SMP mode.
 extern int KidIdentifier; /* 0 */
 
 #endif /* SQUID_GLOBALS_H */

=== modified file 'src/ssl/cert_validate_message.cc'
--- src/ssl/cert_validate_message.cc	2013-06-07 00:18:11 +0000
+++ src/ssl/cert_validate_message.cc	2013-06-07 09:42:18 +0000
@@ -1,33 +1,38 @@
 #include "squid.h"
 #include "acl/FilledChecklist.h"
+#include "globals.h"
 #include "helper.h"
 #include "ssl/support.h"
 #include "ssl/cert_validate_message.h"
 #include "ssl/ErrorDetail.h"
 
 void
 Ssl::CertValidationMsg::composeRequest(CertValidationRequest const &vcert)
 {
     body.clear();
     body += Ssl::CertValidationMsg::param_host + "=" + vcert.domainName;
-    STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(vcert.ssl);
+    STACK_OF(X509) *peerCerts = static_cast<STACK_OF(X509) *>(SSL_get_ex_data(vcert.ssl, ssl_ex_index_ssl_cert_chain));
+
+    if (!peerCerts)
+        peerCerts = SSL_get_peer_cert_chain(vcert.ssl);
+
     if (peerCerts) {
         Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
         for (int i = 0; i < sk_X509_num(peerCerts); ++i) {
             X509 *cert = sk_X509_value(peerCerts, i);
             PEM_write_bio_X509(bio.get(), cert);
             body = body + "\n" + param_cert + xitoa(i) + "=";
             char *ptr;
             long len = BIO_get_mem_data(bio.get(), &ptr);
             body.append(ptr, (ptr[len-1] == '\n' ? len - 1 : len));
             if (!BIO_reset(bio.get())) {
                 // print an error?
             }
         }
     }
 
     if (vcert.errors) {
         int i = 0;
         for (const Ssl::CertErrors *err = vcert.errors; err; err = err->next, ++i) {
             body +="\n";
             body = body + param_error_name + xitoa(i) + "=" + GetErrorName(err->element.code) + "\n";

=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc	2013-06-07 00:18:11 +0000
+++ src/ssl/support.cc	2013-06-07 10:34:37 +0000
@@ -225,41 +225,42 @@
 ssl_verify_cb(int ok, X509_STORE_CTX * ctx)
 {
     // preserve original ctx->error before SSL_ calls can overwrite it
     Ssl::ssl_error_t error_no = ok ? SSL_ERROR_NONE : ctx->error;
 
     char buffer[256] = "";
     SSL *ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
     SSL_CTX *sslctx = SSL_get_SSL_CTX(ssl);
     const char *server = (const char *)SSL_get_ex_data(ssl, ssl_ex_index_server);
     void *dont_verify_domain = SSL_CTX_get_ex_data(sslctx, ssl_ctx_ex_index_dont_verify_domain);
     ACLChecklist *check = (ACLChecklist*)SSL_get_ex_data(ssl, ssl_ex_index_cert_error_check);
     X509 *peeked_cert = (X509 *)SSL_get_ex_data(ssl, ssl_ex_index_ssl_peeked_cert);
     X509 *peer_cert = ctx->cert;
 
     X509_NAME_oneline(X509_get_subject_name(peer_cert), buffer,
                       sizeof(buffer));
 
     if (ok) {
         debugs(83, 5, "SSL Certificate signature OK: " << buffer);
 
-        if (server) {
+        // Check for domain mismatch only if the current certificate is the peer certificate.
+        if (server && peer_cert == X509_STORE_CTX_get_current_cert(ctx)) {
             if (!Ssl::checkX509ServerValidity(peer_cert, server)) {
                 debugs(83, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " << buffer << " does not match domainname " << server);
                 ok = 0;
                 error_no = SQUID_X509_V_ERR_DOMAIN_MISMATCH;
             }
         }
     }
 
     if (ok && peeked_cert) {
         // Check whether the already peeked certificate matches the new one.
         if (X509_cmp(peer_cert, peeked_cert) != 0) {
             debugs(83, 2, "SQUID_X509_V_ERR_CERT_CHANGE: Certificate " << buffer << " does not match peeked certificate");
             ok = 0;
             error_no =  SQUID_X509_V_ERR_CERT_CHANGE;
         }
     }
 
     if (!ok) {
         X509 *broken_cert =  X509_STORE_CTX_get_current_cert(ctx);
         if (!broken_cert)
@@ -281,42 +282,49 @@
         else
             debugs(83, DBG_IMPORTANT, "SSL unknown certificate error " << error_no << " in " << buffer);
 
         if (check) {
             ACLFilledChecklist *filledCheck = Filled(check);
             assert(!filledCheck->sslErrors);
             filledCheck->sslErrors = new Ssl::CertErrors(Ssl::CertError(error_no, broken_cert));
             filledCheck->serverCert.resetAndLock(peer_cert);
             if (check->fastCheck() == ACCESS_ALLOWED) {
                 debugs(83, 3, "bypassing SSL error " << error_no << " in " << buffer);
                 ok = 1;
             } else {
                 debugs(83, 5, "confirming SSL error " << error_no);
             }
             delete filledCheck->sslErrors;
             filledCheck->sslErrors = NULL;
             filledCheck->serverCert.reset(NULL);
         }
         // If the certificate validator is used then we need to allow all errors and
         // pass them to certficate validator for more processing
-        else if (Ssl::TheConfig.ssl_crt_validator)
+        else if (Ssl::TheConfig.ssl_crt_validator) {
             ok = 1;
+            // Check if we have stored certificates chain. Store if not.
+            if (!SSL_get_ex_data(ssl, ssl_ex_index_ssl_cert_chain)) {
+                STACK_OF(X509) *certStack = X509_STORE_CTX_get1_chain(ctx);
+                if (certStack && !SSL_set_ex_data(ssl, ssl_ex_index_ssl_cert_chain, certStack))
+                    sk_X509_pop_free(certStack, X509_free);
+            }
+        }
     }
 
     if (!dont_verify_domain && server) {}
 
     if (!ok && !SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail) ) {
 
         // Find the broken certificate. It may be intermediate.
         X509 *broken_cert = peer_cert; // reasonable default if search fails
         // Our SQUID_X509_V_ERR_DOMAIN_MISMATCH implies peer_cert is at fault.
         if (error_no != SQUID_X509_V_ERR_DOMAIN_MISMATCH) {
             if (X509 *last_used_cert = X509_STORE_CTX_get_current_cert(ctx))
                 broken_cert = last_used_cert;
         }
 
         Ssl::ErrorDetail *errDetail =
             new Ssl::ErrorDetail(error_no, peer_cert, broken_cert);
 
         if (!SSL_set_ex_data(ssl, ssl_ex_index_ssl_error_detail,  errDetail)) {
             debugs(83, 2, "Failed to set Ssl::ErrorDetail in ssl_verify_cb: Certificate " << buffer);
             delete errDetail;
@@ -626,40 +634,51 @@
     delete static_cast<ACLChecklist *>(ptr); // may be NULL
 }
 
 // "free" function for SSL_get_ex_new_index("ssl_error_detail")
 static void
 ssl_free_ErrorDetail(void *, void *ptr, CRYPTO_EX_DATA *,
                      int, long, void *)
 {
     Ssl::ErrorDetail  *errDetail = static_cast <Ssl::ErrorDetail *>(ptr);
     delete errDetail;
 }
 
 static void
 ssl_free_SslErrors(void *, void *ptr, CRYPTO_EX_DATA *,
                    int, long, void *)
 {
     Ssl::CertErrors *errs = static_cast <Ssl::CertErrors*>(ptr);
     delete errs;
 }
 
+/// \ingroup ServerProtocolSSLInternal
+/// Callback handler function to release STACK_OF(X509) "ex" data stored
+/// in an SSL object.
+static void
+ssl_free_CertChain(void *, void *ptr, CRYPTO_EX_DATA *,
+                   int, long, void *)
+{
+    STACK_OF(X509) *certsChain = static_cast <STACK_OF(X509) *>(ptr);
+    sk_X509_pop_free(certsChain,X509_free);
+}
+
 // "free" function for X509 certificates
 static void
 ssl_free_X509(void *, void *ptr, CRYPTO_EX_DATA *,
               int, long, void *)
 {
     X509  *cert = static_cast <X509 *>(ptr);
     X509_free(cert);
 }
 
 /// \ingroup ServerProtocolSSLInternal
 static void
 ssl_initialize(void)
 {
     static int ssl_initialized = 0;
 
     if (!ssl_initialized) {
         ssl_initialized = 1;
         SSL_load_error_strings();
         SSLeay_add_ssl_algorithms();
 #if HAVE_OPENSSL_ENGINE_H
@@ -676,40 +695,41 @@
                 fatalf("Failed to initialise SSL engine: %s\n",
                        ERR_error_string(ssl_error, NULL));
             }
         }
 
 #else
         if (Config.SSL.ssl_engine) {
             fatalf("Your OpenSSL has no SSL engine support\n");
         }
 
 #endif
 
     }
 
     ssl_ex_index_server = SSL_get_ex_new_index(0, (void *) "server", NULL, NULL, NULL);
     ssl_ctx_ex_index_dont_verify_domain = SSL_CTX_get_ex_new_index(0, (void *) "dont_verify_domain", NULL, NULL, NULL);
     ssl_ex_index_cert_error_check = SSL_get_ex_new_index(0, (void *) "cert_error_check", NULL, &ssl_dupAclChecklist, &ssl_freeAclChecklist);
     ssl_ex_index_ssl_error_detail = SSL_get_ex_new_index(0, (void *) "ssl_error_detail", NULL, NULL, &ssl_free_ErrorDetail);
     ssl_ex_index_ssl_peeked_cert  = SSL_get_ex_new_index(0, (void *) "ssl_peeked_cert", NULL, NULL, &ssl_free_X509);
     ssl_ex_index_ssl_errors =  SSL_get_ex_new_index(0, (void *) "ssl_errors", NULL, NULL, &ssl_free_SslErrors);
+    ssl_ex_index_ssl_cert_chain = SSL_get_ex_new_index(0, (void *) "ssl_cert_chain", NULL, NULL, &ssl_free_CertChain);
 }
 
 /// \ingroup ServerProtocolSSLInternal
 static int
 ssl_load_crl(SSL_CTX *sslContext, const char *CRLfile)
 {
     X509_STORE *st = SSL_CTX_get_cert_store(sslContext);
     X509_CRL *crl;
     BIO *in = BIO_new_file(CRLfile, "r");
     int count = 0;
 
     if (!in) {
         debugs(83, 2, "WARNING: Failed to open CRL file '" << CRLfile << "'");
         return 0;
     }
 
     while ((crl = PEM_read_bio_X509_CRL(in,NULL,NULL,NULL))) {
         if (!X509_STORE_add_crl(st, crl))
             debugs(83, 2, "WARNING: Failed to add CRL from file '" << CRLfile << "'");
         else

Reply via email to