]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Certificate mimicking
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Fri, 16 Dec 2011 17:17:57 +0000 (19:17 +0200)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Fri, 16 Dec 2011 17:17:57 +0000 (19:17 +0200)
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.

src/client_side.cc
src/ssl/gadgets.cc
src/ssl/gadgets.h
src/ssl/ssl_crtd.cc
src/ssl/support.cc
src/ssl/support.h

index 37a9d9d86b92472ef9f3c11ea4e6046fed079e3a..d1577c94c67b5dccd4bbeab54008ec233e71be5d 100644 (file)
@@ -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
index c1263e78b01cef24bef8f2788fb012c29dddfa65..9c9512575f0d7eda92d3c9ba0addc2d371e2b38c 100644 (file)
@@ -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.
index f19a2170a37e0e8d5628840a855f8909034e64b4..25281837b882a3e810ebb7cb0473e868c126444a 100644 (file)
@@ -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.
index 354d62ef99d9ddf7011a38b7213edf6e1b8a2bc3..cb2c97d1aa13b36a7bfce0fb695df503373843f2 100644 (file)
@@ -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.");
     }
index dc961c010371121a49f524caf8f4c5672cdfd064..a6a0eebeb42d7b66ad824e14f445d70ef51b05a9 100644 (file)
@@ -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;
 
index c84b4cf460728fb48c117011ec7ce1f8a42247a2..d34e1fd231b322c2d94152ff90474f489e0d9495 100644 (file)
@@ -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