From: Christos Tsantilas Date: Fri, 16 Dec 2011 17:17:57 +0000 (+0200) Subject: Certificate mimicking X-Git-Tag: BumpSslServerFirst.take01~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9a90aacefcc5fd89985dd260d9bf3b2b755cee27;p=thirdparty%2Fsquid.git Certificate mimicking This patch try to mimic true server certificate properties when generating a fake SSL certificate for SslBump. If ssl_crtd is enabled, it receives the true server certificate and mimic its properties. Otherwise, the certificate mimicking code will run in the worker. Currently the following properties mimicked: subject name, not before/after, and subject alternate name. --- diff --git a/src/client_side.cc b/src/client_side.cc index 37a9d9d86b..d1577c94c6 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -3580,6 +3580,12 @@ ConnStateData::getSslContextStart() debugs(33, 5, HERE << "SSL certificate for " << host << " haven't found in cache"); } + Ssl::X509_Pointer serverCert; + if (Comm::IsConnOpen(pinning.serverConnection)) { + SSL *ssl = fd_table[pinning.serverConnection->fd].ssl; + serverCert.reset(SSL_get_peer_certificate(ssl)); + } + #if USE_SSL_CRTD debugs(33, 5, HERE << "Generating SSL certificate for " << host << " using ssl_crtd."); Ssl::CrtdMessage request_message; @@ -3588,12 +3594,16 @@ ConnStateData::getSslContextStart() map.insert(std::make_pair(Ssl::CrtdMessage::param_host, host)); std::string bufferToWrite; Ssl::writeCertAndPrivateKeyToMemory(port->signingCert, port->signPkey, bufferToWrite); + if (serverCert.get()) { + Ssl::appendCertToMemory(serverCert, bufferToWrite); + debugs(33, 5, HERE << "Append Mimic Certificate to body request: " << bufferToWrite); + } request_message.composeBody(map, bufferToWrite); Ssl::Helper::GetInstance()->sslSubmit(request_message, sslCrtdHandleReplyWrapper, this); return; #else debugs(33, 5, HERE << "Generating SSL certificate for " << host); - dynCtx = Ssl::generateSslContext(host, port->signingCert, port->signPkey); + dynCtx = Ssl::generateSslContext(host, serverCert, port->signingCert, port->signPkey); getSslContextDone(dynCtx, true); return; #endif //USE_SSL_CRTD diff --git a/src/ssl/gadgets.cc b/src/ssl/gadgets.cc index c1263e78b0..9c9512575f 100644 --- a/src/ssl/gadgets.cc +++ b/src/ssl/gadgets.cc @@ -160,6 +160,30 @@ bool Ssl::writeCertAndPrivateKeyToMemory(Ssl::X509_Pointer const & cert, Ssl::EV return true; } +bool Ssl::appendCertToMemory(Ssl::X509_Pointer const & cert, std::string & bufferToWrite) +{ + if (!cert) + return false; + + BIO_Pointer bio(BIO_new(BIO_s_mem())); + if (!bio) + return false; + + if (!PEM_write_bio_X509 (bio.get(), cert.get())) + return false; + + char *ptr = NULL; + long len = BIO_get_mem_data(bio.get(), &ptr); + if (!ptr) + return false; + + if (!bufferToWrite.empty()) + bufferToWrite.append(" "); // add a space... + + bufferToWrite.append(ptr, len); + return true; +} + bool Ssl::writeCertAndPrivateKeyToFile(Ssl::X509_Pointer const & cert, Ssl::EVP_PKEY_Pointer const & pkey, char const * filename) { if (!pkey || !cert) @@ -198,6 +222,19 @@ bool Ssl::readCertAndPrivateKeyFromMemory(Ssl::X509_Pointer & cert, Ssl::EVP_PKE return true; } +bool Ssl::readCertFromMemory(X509_Pointer & cert, char const * bufferToRead) +{ + Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem())); + BIO_puts(bio.get(), bufferToRead); + + X509 * certPtr = NULL; + cert.reset(PEM_read_bio_X509(bio.get(), &certPtr, 0, 0)); + if (!cert) + return false; + + return true; +} + bool Ssl::generateSslCertificateAndPrivateKey(char const *host, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey, Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey, BIGNUM const * serial) { pkey.reset(createSslPrivateKey()); @@ -219,6 +256,94 @@ bool Ssl::generateSslCertificateAndPrivateKey(char const *host, Ssl::X509_Pointe return true; } +static bool mimicCertificate(Ssl::X509_Pointer & cert, Ssl::X509_Pointer const & caCert, Ssl::X509_Pointer const &certToMimic) +{ + // not an Ssl::X509_NAME_Pointer because X509_REQ_get_subject_name() + // returns a pointer to the existing subject name. Nothing to clean here. + X509_NAME *name = X509_get_subject_name(certToMimic.get()); + if (!name) + return false; + // X509_set_subject_name will call X509_dup for name + X509_set_subject_name(cert.get(), name); + + + // We should get caCert notBefore and notAfter fields and do not allow + // notBefore/notAfter values from certToMimic before/after notBefore/notAfter + // fields from caCert. + // Currently there is not any way in openssl tollkit to compare two ASN1_TIME + // objects. + ASN1_TIME *aTime; + if ((aTime = X509_get_notBefore(certToMimic.get())) || (aTime = X509_get_notBefore(caCert.get())) ) { + if (!X509_set_notBefore(cert.get(), aTime)) + return false; + } + else if (!X509_gmtime_adj(X509_get_notBefore(cert.get()), (-2)*24*60*60)) + return false; + + if ((aTime = X509_get_notAfter(certToMimic.get())) || (aTime = X509_get_notAfter(caCert.get())) ) { + if (!X509_set_notAfter(cert.get(), aTime)) + return NULL; + } else if (!X509_gmtime_adj(X509_get_notAfter(cert.get()), 60*60*24*356*3)) + return NULL; + + + unsigned char *alStr; + int alLen; + alStr = X509_alias_get0(certToMimic.get(), &alLen); + if (alStr) { + X509_alias_set1(cert.get(), alStr, alLen); + } + + // Add subjectAltName extension used to support multiple hostnames with one certificate + int pos=X509_get_ext_by_NID (certToMimic.get(), OBJ_sn2nid("subjectAltName"), -1); + X509_EXTENSION *ext=X509_get_ext(certToMimic.get(), pos); + if (ext) + X509_add_ext(cert.get(), ext, -1); + + return true; +} + +bool Ssl::generateSslCertificate(Ssl::X509_Pointer const &certToMimic, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey, Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_Pointer & pkey, BIGNUM const * serial) +{ + if (!certToMimic.get()) + return false; + + pkey.reset(createSslPrivateKey()); + if (!pkey) + return false; + + Ssl::X509_Pointer cert(X509_new()); + if (!cert) + return false; + + // Set pub key and serial given by the caller + if (!X509_set_pubkey(cert.get(), pkey.get())) + return false; + if (!setSerialNumber(X509_get_serialNumber(cert.get()), serial)) + return false; + + // inherit properties from certToMimic + if (!mimicCertificate(cert, signedX509, certToMimic)) + return false; + + // Set issuer name, from CA or our subject name for self signed cert + if (!X509_set_issuer_name(cert.get(), signedX509.get() ? X509_get_subject_name(signedX509.get()) : X509_get_subject_name(cert.get()))) + return false; + + /*Now sign the request */ + int ret = 0; + if (signedPkey.get()) + ret = X509_sign(cert.get(), signedPkey.get(), EVP_sha1()); + else //else sign with self key (self signed request) + ret = X509_sign(cert.get(), pkey.get(), EVP_sha1()); + + if (!ret) + return false; + + certToStore.reset(cert.release()); + return true; +} + /** \ingroup ServerProtocolSSLInternal * Read certificate from file. diff --git a/src/ssl/gadgets.h b/src/ssl/gadgets.h index f19a2170a3..25281837b8 100644 --- a/src/ssl/gadgets.h +++ b/src/ssl/gadgets.h @@ -91,6 +91,12 @@ X509_REQ * createNewX509Request(EVP_PKEY_Pointer const & pkey, const char * host */ bool writeCertAndPrivateKeyToMemory(X509_Pointer const & cert, EVP_PKEY_Pointer const & pkey, std::string & bufferToWrite); +/** + \ingroup SslCrtdSslAPI + * Append SSL certificate to bufferToWrite. + */ +bool appendCertToMemory(X509_Pointer const & cert, std::string & bufferToWrite); + /** \ingroup SslCrtdSslAPI * Write private key and SSL certificate to file. @@ -103,6 +109,12 @@ bool writeCertAndPrivateKeyToFile(X509_Pointer const & cert, EVP_PKEY_Pointer co */ bool readCertAndPrivateKeyFromMemory(X509_Pointer & cert, EVP_PKEY_Pointer & pkey, char const * bufferToRead); +/** + \ingroup SslCrtdSslAPI + * Read SSL certificate from memory. + */ +bool readCertFromMemory(X509_Pointer & cert, char const * bufferToRead); + /** \ingroup SslCrtdSslAPI * Sign SSL request. @@ -119,6 +131,15 @@ 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 + * Decide on the kind of certificate and generate a CA- or self-signed one. + * The generated certificate will inherite properties from certToMimic + * Return generated certificate and private key in resultX509 and resultPkey + * variables. + */ +bool generateSslCertificate(X509_Pointer const &certToMimic, 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. diff --git a/src/ssl/ssl_crtd.cc b/src/ssl/ssl_crtd.cc index 354d62ef99..cb2c97d1aa 100644 --- a/src/ssl/ssl_crtd.cc +++ b/src/ssl/ssl_crtd.cc @@ -83,6 +83,7 @@ usage: ssl_crtd -g -s ssl_store_path \endverbatim */ +#define CERT_BEGIN_STR "-----BEGIN CERTIFICATE" static const char *const B_KBYTES_STR = "KB"; static const char *const B_MBYTES_STR = "MB"; static const char *const B_GBYTES_STR = "GB"; @@ -224,7 +225,25 @@ static bool proccessNewRequest(Ssl::CrtdMessage const & request_message, std::st Ssl::X509_Pointer cert; Ssl::EVP_PKEY_Pointer pkey; - db.find("/CN=" + host, cert, pkey); + Ssl::X509_Pointer certToMimic; + + const char *s; + std::string cert_subject; + if ((s = strstr(body_part.c_str(), CERT_BEGIN_STR))) { + s += strlen(CERT_BEGIN_STR); + if ((s = strstr(s, CERT_BEGIN_STR))) { + Ssl::readCertFromMemory(certToMimic, s); + if (certToMimic.get()) { + char buf[1024]; + cert_subject = X509_NAME_oneline(X509_get_subject_name(certToMimic.get()), buf, sizeof(buf)); + } + } + } + + if (cert_subject.empty()) + cert_subject = "/CN=" + host; + + db.find(cert_subject, cert, pkey); if (!cert || !pkey) { Ssl::X509_Pointer certToSign; @@ -233,8 +252,13 @@ static bool proccessNewRequest(Ssl::CrtdMessage const & request_message, std::st Ssl::BIGNUM_Pointer serial(db.getCurrentSerialNumber()); - if (!Ssl::generateSslCertificateAndPrivateKey(host.c_str(), certToSign, pkeyToSign, cert, pkey, serial.get())) - throw std::runtime_error("Cannot create ssl certificate or private key."); + if (certToMimic.get()) { + Ssl::generateSslCertificate(certToMimic, certToSign, pkeyToSign, cert, pkey, serial.get()); + } + else + if (!Ssl::generateSslCertificateAndPrivateKey(host.c_str(), certToSign, pkeyToSign, cert, pkey, serial.get())) + throw std::runtime_error("Cannot create ssl certificate or private key."); + if (!db.addCertAndPrivateKey(cert, pkey) && db.IsEnabledDiskStore()) throw std::runtime_error("Cannot add certificate to db."); } diff --git a/src/ssl/support.cc b/src/ssl/support.cc index dc961c0103..a6a0eebeb4 100644 --- a/src/ssl/support.cc +++ b/src/ssl/support.cc @@ -1211,13 +1211,18 @@ SSL_CTX * Ssl::generateSslContextUsingPkeyAndCertFromMemory(const char * data) return createSSLContext(cert, pkey); } -SSL_CTX * Ssl::generateSslContext(char const *host, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey) +SSL_CTX * Ssl::generateSslContext(char const *host, Ssl::X509_Pointer const & mimicCert, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey) { Ssl::X509_Pointer cert; Ssl::EVP_PKEY_Pointer pkey; - if (!generateSslCertificateAndPrivateKey(host, signedX509, signedPkey, cert, pkey, NULL)) { + if (mimicCert .get()) { + if (!generateSslCertificate(mimicCert, signedX509, signedPkey, cert, pkey, NULL)) + return NULL; + } + else if (!generateSslCertificateAndPrivateKey(host, signedX509, signedPkey, cert, pkey, NULL)) { return NULL; } + if (!cert) return NULL; diff --git a/src/ssl/support.h b/src/ssl/support.h index c84b4cf460..d34e1fd231 100644 --- a/src/ssl/support.h +++ b/src/ssl/support.h @@ -108,7 +108,7 @@ namespace Ssl \ingroup ServerProtocolSSLAPI * Decide on the kind of certificate and generate a CA- or self-signed one */ -SSL_CTX *generateSslContext(char const *host, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey); +SSL_CTX *generateSslContext(char const *host, Ssl::X509_Pointer const & mimicCert, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey); /** \ingroup ServerProtocolSSLAPI