From: Christos Tsantilas Date: Thu, 27 Oct 2011 15:27:25 +0000 (+0300) Subject: sslBump: Send intermediate CA X-Git-Tag: BumpSslServerFirst.take01~70 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a594dbfa3c8960e7fc2a3c9a9fcd848c5242dbac;p=thirdparty%2Fsquid.git sslBump: Send intermediate CA 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 --- diff --git a/src/ProtoPort.h b/src/ProtoPort.h index 6b17579a84..d80fa00891 100644 --- a/src/ProtoPort.h +++ b/src/ProtoPort.h @@ -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); diff --git a/src/cache_cf.cc b/src/cache_cf.cc index 24079dd32c..4710020de9 100644 --- a/src/cache_cf.cc +++ b/src/cache_cf.cc @@ -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); } } diff --git a/src/client_side.cc b/src/client_side.cc index da3d5cae9f..14918836e9 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -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)) { diff --git a/src/ssl/gadgets.cc b/src/ssl/gadgets.cc index 057d3c766c..35ddf90148 100644 --- a/src/ssl/gadgets.cc +++ b/src/ssl/gadgets.cc @@ -4,6 +4,9 @@ #include "config.h" #include "ssl/gadgets.h" +#if HAVE_OPENSSL_X509V3_H +#include +#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; diff --git a/src/ssl/gadgets.h b/src/ssl/gadgets.h index 11f2286307..f19a2170a3 100644 --- a/src/ssl/gadgets.h +++ b/src/ssl/gadgets.h @@ -39,6 +39,9 @@ namespace Ssl CtoCpp1(X509_free, X509 *) typedef TidyPointer X509_Pointer; +CtoCpp1(sk_X509_free, STACK_OF(X509) *) +typedef TidyPointer X509_STACK_Pointer; + CtoCpp1(EVP_PKEY_free, EVP_PKEY *) typedef TidyPointer 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. diff --git a/src/ssl/support.cc b/src/ssl/support.cc index cfdb05b851..6953459e5d 100644 --- a/src/ssl/support.cc +++ b/src/ssl/support.cc @@ -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 */ diff --git a/src/ssl/support.h b/src/ssl/support.h index 6ac0fd4406..e2df0bddde 100644 --- a/src/ssl/support.h +++ b/src/ssl/support.h @@ -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