]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
sslBump: Send intermediate CA
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Thu, 27 Oct 2011 15:27:25 +0000 (18:27 +0300)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Thu, 27 Oct 2011 15:27:25 +0000 (18:27 +0300)
SslBump code assumed that it is signing generated certificates with a root CA
certificate. Root certificates are usually not sent along with the server
certificates because clients must have them independently installed or
built-in. Squid was not sending the signing certificate.

In many environments, Squid signing certificate is intermediate (i.e., it
belongs to a non-root CA). If Squid does not send that intermediate signing
certificate with the generated one, the client will not be able to establish a
complete chain of trust from the generated fake to the root CA certificate,
leading to errors.

With this change, Squid may send the signing certificate (along with the
generated one) using the following rules:

   * If the configured signing certificate is self-signed,
     then just send the generated certificate alone.
     Note that root CA certificates are self-signed (by root CA).

   * Otherwise (i.e., if the configured signing certificate is an intermediate
     CA certificate), send both the intermediate CA and the generated fake
     certificate.

   * If Squid sends the intermediate CA certificate, Squid also sends
     all other certificates from the "cert=" file, Sending a chain with
     multiple intermediate CA certificates may be required when the Squid
     signing certificate was signed by another intermediate CA.

This is a Measurement Factory Project

src/ProtoPort.h
src/cache_cf.cc
src/client_side.cc
src/ssl/gadgets.cc
src/ssl/gadgets.h
src/ssl/support.cc
src/ssl/support.h

index 6b17579a840acfb6ad0d8fdb0b8d574bbb9da30a..d80fa0089168df2863f103cea80867e2044d43ad 100644 (file)
@@ -72,6 +72,7 @@ struct http_port_list {
     Ssl::SSL_CTX_Pointer staticSslContext; ///< for HTTPS accelerator or static sslBump
     Ssl::X509_Pointer signingCert; ///< x509 certificate for signing generated certificates
     Ssl::EVP_PKEY_Pointer signPkey; ///< private key for sighing generated certificates
+    Ssl::X509_STACK_Pointer certsToChain; ///<  x509 certificates to send with the generated cert
 #endif
 
     CBDATA_CLASS2(http_port_list);
index 24079dd32c3fee2db0285de0163a42f676199c8b..4710020de9d0b6e7ce60c0e7b65e40c09acac5ac 100644 (file)
@@ -909,7 +909,7 @@ configDoConfigure(void)
                                        s->cafile, s->capath, s->crlfile, s->dhfile,
                                        s->sslContextSessionId));
 
-            Ssl::readCertAndPrivateKeyFromFiles(s->signingCert, s->signPkey, s->cert, s->key);
+            Ssl::readCertChainAndPrivateKeyFromFiles(s->signingCert, s->signPkey, s->certsToChain, s->cert, s->key);
         }
     }
 
index da3d5cae9f511b6e5235bd404b65b0bd11fb40eb..14918836e94a5a539d39ea050eb1c1d8fc513a49 100644 (file)
@@ -3469,7 +3469,8 @@ ConnStateData::sslCrtdHandleReply(const char * reply)
                 debugs(33, 5, HERE << "Certificate for " << sslHostName << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
             } else {
                 debugs(33, 5, HERE << "Certificate for " << sslHostName << " was successfully recieved from ssl_crtd");
-                getSslContextDone(Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str()), true);
+                SSL_CTX *ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str());
+                getSslContextDone(ctx, true);
                 return;
             }
         }
@@ -3523,6 +3524,9 @@ ConnStateData::getSslContextDone(SSL_CTX * sslContext, bool isNew)
 {
     // Try to add generated ssl context to storage.
     if (port->generateHostCertificates && isNew) {
+
+        Ssl::addChainToSslContext(sslContext, port->certsToChain.get());
+
         Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
         if (sslContext && sslHostName != "") {
             if (!ssl_ctx_cache.add(sslHostName.termedBuf(), sslContext)) {
index 057d3c766cbdaa0296e89a16759ce511fede27a9..35ddf90148ec6d163f9a1851ef1abbd35c7396e4 100644 (file)
@@ -4,6 +4,9 @@
 
 #include "config.h"
 #include "ssl/gadgets.h"
+#if HAVE_OPENSSL_X509V3_H
+#include <openssl/x509v3.h>
+#endif
 
 /**
  \ingroup ServerProtocolSSLInternal
@@ -231,11 +234,7 @@ static X509 * readSslX509Certificate(char const * certFilename)
     return certificate;
 }
 
-/**
- \ingroup ServerProtocolSSLInternal
- * Read private key from file. Make sure that this is not encrypted file.
- */
-static EVP_PKEY * readSslPrivateKey(char const * keyFilename)
+EVP_PKEY * Ssl::readSslPrivateKey(char const * keyFilename)
 {
     if (!keyFilename)
         return NULL;
index 11f2286307ab6c5bd191c3d50323581257f74b36..f19a2170a37e0e8d5628840a855f8909034e64b4 100644 (file)
@@ -39,6 +39,9 @@ namespace Ssl
 CtoCpp1(X509_free, X509 *)
 typedef TidyPointer<X509, X509_free_cpp> X509_Pointer;
 
+CtoCpp1(sk_X509_free, STACK_OF(X509) *)
+typedef TidyPointer<STACK_OF(X509), sk_X509_free_cpp> X509_STACK_Pointer;
+
 CtoCpp1(EVP_PKEY_free, EVP_PKEY *)
 typedef TidyPointer<EVP_PKEY, EVP_PKEY_free_cpp> EVP_PKEY_Pointer;
 
@@ -116,6 +119,12 @@ X509 * signRequest(X509_REQ_Pointer const & request, X509_Pointer const & x509,
  */
 bool generateSslCertificateAndPrivateKey(char const *host, X509_Pointer const & signedX509, EVP_PKEY_Pointer const & signedPkey, X509_Pointer & cert, EVP_PKEY_Pointer & pkey, BIGNUM const* serial);
 
+/**
+ \ingroup SslCrtdSslAPI
+ * Read private key from file. Make sure that this is not encrypted file.
+ */
+EVP_PKEY * readSslPrivateKey(char const * keyFilename);
+
 /**
  \ingroup SslCrtdSslAPI
  *  Read certificate and private key from files.
index cfdb05b85114681bf179d830afe1c30da76ea40c..6953459e5d08075640c01c7b3d1ac31271d7a6b2 100644 (file)
@@ -1248,4 +1248,73 @@ Ssl::setClientSNI(SSL *ssl, const char *fqdn)
 #endif
 }
 
+void Ssl::addChainToSslContext(SSL_CTX *sslContext, STACK_OF(X509) *chain)
+{
+    if (!chain)
+        return;
+
+    for (int i = 0; i < sk_X509_num(chain); i++) {
+        X509 *cert = sk_X509_value(chain, i);
+        if (SSL_CTX_add_extra_chain_cert(sslContext, cert)) {
+            // increase the certificate lock
+            CRYPTO_add(&(cert->references),1,CRYPTO_LOCK_X509);
+        } else {
+            const int ssl_error = ERR_get_error();
+            debugs(83, DBG_IMPORTANT, "WARNING: can not add certificate to SSL context chain: " << ERR_error_string(ssl_error, NULL));
+        }
+    }
+}
+
+/**
+ \ingroup ServerProtocolSSLInternal
+ * Read certificate from file.
+ * See also: static readSslX509Certificate function, gadgets.cc file
+ */
+static X509 * readSslX509CertificatesChain(char const * certFilename,  STACK_OF(X509)* chain)
+{
+    if (!certFilename)
+        return NULL;
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
+    if (!bio)
+        return NULL;
+    if (!BIO_read_filename(bio.get(), certFilename))
+        return NULL;
+    X509 *certificate = PEM_read_bio_X509(bio.get(), NULL, NULL, NULL);
+
+    if (certificate && chain) {
+
+        if (X509_check_issued(certificate, certificate) == X509_V_OK) 
+            debugs(83, 5, "Certificate is self-signed, will not be chained");
+        else {
+            if(sk_X509_push(chain, certificate))
+                CRYPTO_add(&(certificate->references), 1, CRYPTO_LOCK_X509);
+            else
+                debugs(83, DBG_IMPORTANT, "WARNING: unable to add signing certificate to cert chain");
+            // and add to the chain any certificate loaded from the file
+            while (X509 *ca = PEM_read_bio_X509(bio.get(), NULL, NULL, NULL)) {
+                if (!sk_X509_push(chain, ca))
+                    debugs(83, DBG_IMPORTANT, "WARNING: unable to add CA certificate to cert chain");
+            }
+        }
+    }
+    
+    return certificate;
+}
+
+void Ssl::readCertChainAndPrivateKeyFromFiles(X509_Pointer & cert, EVP_PKEY_Pointer & pkey, X509_STACK_Pointer & chain, char const * certFilename, char const * keyFilename)
+{
+    if (keyFilename == NULL)
+        keyFilename = certFilename;
+    if (!chain)
+        chain.reset(sk_X509_new_null());
+    if (!chain)
+        debugs(83, DBG_IMPORTANT, "WARNING: unable to allocate memory for cert chain");
+    pkey.reset(readSslPrivateKey(keyFilename));
+    cert.reset(readSslX509CertificatesChain(certFilename, chain.get()));
+    if (!pkey || !cert || !X509_check_private_key(cert.get(), pkey.get())) {
+        pkey.reset(NULL);
+        cert.reset(NULL);
+    }
+}
+
 #endif /* USE_SSL */
index 6ac0fd440677236e2747c0b3cb2a7de764749808..e2df0bdddeb5f755f640fa593d44408dd9c59a78 100644 (file)
@@ -123,6 +123,21 @@ bool verifySslCertificateDate(SSL_CTX * sslContext);
  */
 SSL_CTX * generateSslContextUsingPkeyAndCertFromMemory(const char * data);
 
+/**
+  \ingroup ServerProtocolSSLAPI
+  * Adds the certificates in certList to the certificate chain of the SSL context
+ */
+void addChainToSslContext(SSL_CTX *sslContext, STACK_OF(X509) *certList);
+
+/**
+ \ingroup ServerProtocolSSLAPI
+ *  Read certificate, private key and any certificates which must be chained from files.
+ * See also: Ssl::readCertAndPrivateKeyFromFiles function,  defined in gadgets.h
+ * \param certFilename name of file with certificate and certificates which must be chainned.
+ * \param keyFilename name of file with private key.
+ */
+void readCertChainAndPrivateKeyFromFiles(X509_Pointer & cert, EVP_PKEY_Pointer & pkey, X509_STACK_Pointer & chain, char const * certFilename, char const * keyFilename);
+
 /**
    \ingroup ServerProtocolSSLAPI
    * Iterates over the X509 common and alternate names and to see if  matches with given data