]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
ssl_bump_peeked access list
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Tue, 6 May 2014 09:46:07 +0000 (12:46 +0300)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Tue, 6 May 2014 09:46:07 +0000 (12:46 +0300)
This access list is a temporary solution for peek-and-splice project and used to
take the final decision "bump" or "splice" in peek-and-splice bumping mode.

This is what this patch try to do:
  - Get Client Hello message
  - Start connection.
  - Inside bio, before write the SSL HELLO message, try to emulate client hello
    message:
     a) extract client hello message features
     b) Check if we are able support client features and if not, splicing is not
        able to be supported.
     c) Creates an SSL object to connect to server and try to set it with
        the extracted features.
        This step currently includes many hacks and modify undocumented SSL
        object members.

extensions)
  - in PeerConnector.cc
      a) If can not be spliced do not splice.
      b) check the ssl_bump_peeked access list to splice or not.

src/SquidConfig.h
src/cache_cf.cc
src/cf.data.depend
src/cf.data.pre
src/ssl/PeerConnector.cc
src/ssl/PeerConnector.h
src/ssl/bio.cc
src/ssl/bio.h
src/ssl/support.cc
src/ssl/support.h

index 97d09a31002ec3c081b6d5fd44b91ef2403f8a24..30a6d53f105e999d1871f64b30ef1ce361bcab12 100644 (file)
@@ -390,6 +390,7 @@ public:
 
 #if USE_OPENSSL
         acl_access *ssl_bump;
+        acl_access *ssl_bump_peeked;
 #endif
 #if FOLLOW_X_FORWARDED_FOR
         acl_access *followXFF;
index 6ca7b61248f511715856ec51816715466eb78171..dd70b946db6215e85196232e4f10ffeff6938243 100644 (file)
@@ -242,6 +242,10 @@ static void free_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt);
 static void parse_sslproxy_ssl_bump(acl_access **ssl_bump);
 static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump);
 static void free_sslproxy_ssl_bump(acl_access **ssl_bump);
+
+static void parse_sslproxy_ssl_bump_peeked(acl_access **ssl_bump);
+static void dump_sslproxy_ssl_bump_peeked(StoreEntry *entry, const char *name, acl_access *ssl_bump);
+static void free_sslproxy_ssl_bump_peeked(acl_access **ssl_bump);
 #endif /* USE_OPENSSL */
 
 static void parse_ftp_epsv(acl_access **ftp_epsv);
@@ -4729,6 +4733,53 @@ static void free_sslproxy_ssl_bump(acl_access **ssl_bump)
     free_acl_access(ssl_bump);
 }
 
+static void
+parse_sslproxy_ssl_bump_peeked(acl_access **ssl_bump_peeked)
+{
+    char *bm;
+    if ((bm = ConfigParser::NextToken()) == NULL) {
+        self_destruct();
+        return;
+    }
+
+    allow_t action = allow_t(ACCESS_ALLOWED);
+
+    if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) {
+        action.kind = Ssl::bumpServerFirst;
+    } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpSplice]) == 0) {
+        action.kind = Ssl::bumpSplice;
+    } else {
+        debugs(3, DBG_CRITICAL, "FATAL: unknown ssl_bump_peeked mode: " << bm);
+        self_destruct();
+        return;
+    }
+
+    Acl::AndNode *rule = new Acl::AndNode;
+    rule->context("(ssl_bump_peeked rule)", config_input_line);
+    rule->lineParse();
+
+    assert(ssl_bump_peeked);
+    if (!*ssl_bump_peeked) {
+        *ssl_bump_peeked = new Acl::Tree;
+        (*ssl_bump_peeked)->context("(ssl_bump_peeked rules)", config_input_line);
+    }
+
+    (*ssl_bump_peeked)->add(rule, action);
+}
+
+static void
+dump_sslproxy_ssl_bump_peeked(StoreEntry *entry, const char *name, acl_access *ssl_bump_peeked)
+{
+    if (ssl_bump_peeked)
+        dump_SBufList(entry, ssl_bump_peeked->treeDump(name, Ssl::BumpModeStr));
+}
+
+static void
+free_sslproxy_ssl_bump_peeked(acl_access **ssl_bump_peeked)
+{
+    free_acl_access(ssl_bump_peeked);
+}
+
 #endif
 
 static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers)
index 4776fd6bade899cdc561c55f7a07f8c8bb3b4eaf..52b4aa7eddd0c090761465d6202663dfe03fe299 100644 (file)
@@ -73,6 +73,7 @@ wccp2_service
 wccp2_service_info
 wordlist
 sslproxy_ssl_bump      acl
+sslproxy_ssl_bump_peeked acl
 sslproxy_cert_sign     acl
 sslproxy_cert_adapt    acl
 ftp_epsv                acl
index a99011006d96b3940bfa555bcff9aa8a2f103bd6..191c5fe319033848a60127d7974b1493cf4a351a 100644 (file)
@@ -2491,6 +2491,29 @@ DOC_START
        ssl_bump server-first all
 DOC_END
 
+NAME: ssl_bump_peeked
+IFDEF: USE_OPENSSL
+TYPE: sslproxy_ssl_bump_peeked
+LOC: Config.accessList.ssl_bump_peeked
+DEFAULT: none
+DOC_START
+       This option used to control splicing decision after we peek at the
+       certificates
+
+       ssl_bump_peeked <mode> [!]acl ...
+
+       The following peeked modes are supported:
+
+       server-first
+               Allow bumping the connection
+
+       splice
+               Do not bump the connection if possible.
+
+       ssl_bump_peeked server-first safeToBump
+       ssl_bump_peeked splice all
+DOC_END
+
 NAME: sslproxy_flags
 IFDEF: USE_OPENSSL
 DEFAULT: none
index 01e633240147831cf542d4fa47a3c2f2f45b9872..8103198774e7f4bdd5395158513f3fa645bd0afb 100644 (file)
@@ -14,6 +14,7 @@
 #include "globals.h"
 #include "HttpRequest.h"
 #include "neighbors.h"
+#include "SquidConfig.h"
 #include "ssl/bio.h"
 #include "ssl/cert_validate_message.h"
 #include "ssl/Config.h"
@@ -153,7 +154,7 @@ Ssl::PeerConnector::initializeSsl()
             if (features.sslVersion >= 3) {
                 b = SSL_get_rbio(ssl);
                 Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
-                srvBio->setClientRandom(features.client_random);
+                srvBio->setClientFeatures(features);
                 srvBio->recordInput(true);
             }
         }
@@ -265,24 +266,45 @@ Ssl::PeerConnector::negotiateSsl()
 }
 
 void switchToTunnel(HttpRequest *request, int *status_ptr, Comm::ConnectionPointer & clientConn, Comm::ConnectionPointer &srvConn);
+
 void
-Ssl::PeerConnector::checkForPeekAndSplice()
+Ssl::PeerConnector::cbCheckForPeekAndSplice(allow_t answer, void *data)
+{
+    Ssl::PeerConnector *peerConnect = (Ssl::PeerConnector *) data;
+    peerConnect->checkForPeekAndSplice(true, (Ssl::BumpMode)answer.kind);
+}
+
+bool
+Ssl::PeerConnector::checkForPeekAndSplice(bool checkDone, Ssl::BumpMode peekMode)
 {
     SSL *ssl = fd_table[serverConn->fd].ssl;
     BIO *b = SSL_get_rbio(ssl);
     Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
     debugs(83,5, "Will check for peek and splice on fd " << serverConn->fd);
-    const bool splice = true;
+    bool splice;
+    if (!srvBio->canSplice())
+        splice = false;
+    else if (!checkDone) {
+        ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(
+                           ::Config.accessList.ssl_bump_peeked,
+                           request.getRaw(), NULL);
+        acl_checklist->nonBlockingCheck(Ssl::PeerConnector::cbCheckForPeekAndSplice, this);
+        return false;
+    } else
+        splice = (peekMode == Ssl::bumpSplice);
+
     if (!splice) {
         //Allow write, proceed with the connection
         srvBio->holdWrite(false);
         srvBio->recordInput(false);
         Comm::SetSelect(serverConn->fd, COMM_SELECT_WRITE, &NegotiateSsl, this, 0);
         debugs(83,5, "Retry the fwdNegotiateSSL on fd " << serverConn->fd);
+        return true;
     } else {
         static int status_code = 0;
         debugs(83,5, "Revert to tunnel fd " << clientConn->fd << " with fd " << serverConn->fd);
         switchToTunnel(request.getRaw(), &status_code, clientConn, serverConn);
+        return false;
     }
 }
 
@@ -435,7 +457,7 @@ Ssl::PeerConnector::handleNegotiateError(const int ret)
         case SSL_ERROR_WANT_WRITE:
             if (request->clientConnectionManager->sslBumpMode == Ssl::bumpPeekAndSplice && srvBio->holdWrite()) {
                 debugs(81, DBG_IMPORTANT, "hold write on SSL connection on FD " << fd);
-                checkForPeekAndSplice();
+                checkForPeekAndSplice(false, Ssl::bumpNone);
                 return;
             }
             Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, this, 0);
@@ -445,6 +467,19 @@ Ssl::PeerConnector::handleNegotiateError(const int ret)
         case SSL_ERROR_SYSCALL:
             ssl_lib_error = ERR_get_error();
 
+            // If we are in peek-and-splice mode and still we did not write to
+            // server yet, try to see if we should splice.
+            // In this case the connection can be saved.
+            // If the checklist decision is do not splice a new error will
+            // occure in the next SSL_connect call, and we will fail again.
+#if 1
+            if (request->clientConnectionManager->sslBumpMode == Ssl::bumpPeekAndSplice && srvBio->holdWrite()) {
+                debugs(81, DBG_IMPORTANT, "fwdNegotiateSSL: Error but, hold write on SSL connection on FD " << fd);
+                checkForPeekAndSplice(false, Ssl::bumpNone);
+                return;
+            }
+#endif
+
             // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1
             if (ssl_error == SSL_ERROR_SYSCALL && ret == -1 && ssl_lib_error == 0)
                 sysErrNo = errno;
index 8ab4b702ab44ac9790d8b300f9ee26c88d5432b7..73c7b2713539dca1982e2c08ad2e6f2d06ea5b38 100644 (file)
@@ -125,7 +125,7 @@ protected:
     /// It is called multiple times untill the negotiation finish or aborted.
     void negotiateSsl();
 
-    void checkForPeekAndSplice();
+    bool checkForPeekAndSplice(bool, Ssl::BumpMode);
 
     /// Called when the SSL negotiation step aborted because data needs to
     /// be transferred to/from SSL server or on error. In the first case
@@ -158,6 +158,9 @@ private:
     /// A wrapper function for negotiateSsl for use with Comm::SetSelect
     static void NegotiateSsl(int fd, void *data);
 
+    /// A wrapper function for checkForPeekAndSplice for use with acl
+    static void cbCheckForPeekAndSplice(allow_t answer, void *data);
+
     HttpRequestPointer request; ///< peer connection trigger or cause
     Comm::ConnectionPointer serverConn; ///< TCP connection to the peer
     Comm::ConnectionPointer clientConn; ///< TCP connection to the client
index 9ec0bc95016e95e144bd18db637bfb6482ed94dd..80039d018fe12c19388b278b108200b3e511f5c3 100644 (file)
@@ -193,7 +193,7 @@ Ssl::ClientBio::read(char *buf, int size, BIO *table)
     if (headerState < 2) {
 
         if (rbuf.isNull())
-            rbuf.init(1024, 4096);
+            rbuf.init(1024, 16384);
 
         size = rbuf.spaceSize() > size ? size : rbuf.spaceSize();
 
@@ -221,10 +221,12 @@ Ssl::ClientBio::read(char *buf, int size, BIO *table)
             debugs(83, 7, "SSL version 3 handshake message");
             headerBytes = (head[3] << 8) + head[4];
             debugs(83, 7, "SSL Header Size: " << headerBytes);
+            headerBytes +=5;
 #ifdef DO_SSLV23
         } else if ((head[0] & 0x80) && head[2] == 0x01 && head[3] == 0x03) { 
             debugs(83, 7, "SSL version 2 handshake message with v3 support");
             headerBytes = head[1];
+            headerBytes +=5;
 #endif
         }else {
             debugs(83, 7, "Not an SSL acceptable handshake message (SSLv2 message?)");
@@ -273,10 +275,19 @@ Ssl::ServerBio::stateChanged(const SSL *ssl, int where, int ret)
 }
 
 void
-Ssl::ServerBio::setClientRandom(const unsigned char *r)
+Ssl::ServerBio::setClientFeatures(const Ssl::Bio::sslFeatures &features)
 {
-    memcpy(clientRandom, r, SSL3_RANDOM_SIZE);
-    randomSet = true;
+    clientFeatures.sslVersion = features.sslVersion;
+    clientFeatures.compressMethod = features.compressMethod;
+    clientFeatures.serverName = features.serverName;
+    clientFeatures.clientRequestedCiphers = features.clientRequestedCiphers;
+    clientFeatures.unknownCiphers = features.unknownCiphers;
+    memcpy(clientFeatures.client_random, features.client_random, SSL3_RANDOM_SIZE);
+    clientFeatures.helloMessage.init(features.helloMessage.contentSize(), features.helloMessage.contentSize());
+    clientFeatures.helloMessage.append(features.helloMessage.content(), features.helloMessage.contentSize());
+    clientFeatures.doHeartBeats = features.doHeartBeats;
+    clientFeatures.extensions = features.extensions;
+    featuresSet = true;
 };
 
 int
@@ -286,56 +297,167 @@ Ssl::ServerBio::read(char *buf, int size, BIO *table)
 
     if (bytes > 0 && record_) {
         if (rbuf.isNull())
-            rbuf.init(1024, 8196);
+            rbuf.init(1024, 16384);
         rbuf.append(buf, bytes);
+        debugs(83, 5, "Record is enabled store " << bytes << " bytes");
     }
+    debugs(83, 5, "Read " << bytes << " from " << size << " bytes");
     return bytes;
 }
 
+bool
+adjustSSL(SSL *ssl, Ssl::Bio::sslFeatures &features)
+{
+    // If the client supports compression but our context does not support
+    // we can not adjust.
+    if (features.compressMethod && ssl->ctx->comp_methods == NULL) {
+        debugs(83, 5, "Client Hello Data supports compression, but we do not!");
+        return false;
+    }
+
+    //Check ciphers list
+    size_t token = 0;
+    size_t end = 0;
+    while (token != std::string::npos) {
+      end = features.clientRequestedCiphers.find(':',token);
+      std::string cipher;
+      cipher.assign(features.clientRequestedCiphers, token, end - token);
+      token = (end != std::string::npos ? end + 1 : std::string::npos);
+      bool found = false;
+      STACK_OF(SSL_CIPHER) *cipher_stack = SSL_get_ciphers(ssl);
+      for (int i = 0; i < sk_SSL_CIPHER_num(cipher_stack); i++) {
+          SSL_CIPHER *c = sk_SSL_CIPHER_value(cipher_stack, i);
+          const char *cname = SSL_CIPHER_get_name(c);
+          if (cipher.compare(cname)) {
+              found = true;
+              break;
+          }
+      }
+      if (!found) {
+          debugs(83, 5, "Client Hello Data supports cipher '"<< cipher <<"' but we do not support it!");
+          return false;
+      }
+    }
+
+#if !defined(SSL_TLSEXT_HB_ENABLED)
+    if (features.doHeartBeats) {
+        debugs(83, 5, "Client Hello Data supports HeartBeats but we do not support!");
+        return false;
+    }
+#endif
+
+    for (std::list<int>::iterator it = features.extensions.begin(); it != features.extensions.end(); ++it) {
+        static int supportedExtensions[] = {
+            TLSEXT_TYPE_server_name,
+#ifdef TLSEXT_TYPE_opaque_prf_input
+            TLSEXT_TYPE_opaque_prf_input,
+#endif
+#ifdef TLSEXT_TYPE_heartbeat
+            TLSEXT_TYPE_heartbeat,
+#endif
+#ifdef TLSEXT_TYPE_renegotiate
+            TLSEXT_TYPE_renegotiate,
+#endif
+#ifdef TLSEXT_TYPE_ec_point_formats
+            TLSEXT_TYPE_ec_point_formats,
+#endif
+#ifdef TLSEXT_TYPE_elliptic_curves
+            TLSEXT_TYPE_elliptic_curves,
+#endif
+#ifdef TLSEXT_TYPE_session_ticket
+            TLSEXT_TYPE_session_ticket,
+#endif
+#ifdef TLSEXT_TYPE_status_request
+            TLSEXT_TYPE_status_request,
+#endif
+#ifdef TLSEXT_TYPE_use_srtp
+            TLSEXT_TYPE_use_srtp,
+#endif
+#if 1 //Allow 13172 Firefox supported extension for testing purposes
+            13172,
+#endif
+            -1
+        };
+        bool found = false;
+        for (int i = 0; supportedExtensions[i] != -1; i++) {
+            if (*it == supportedExtensions[i]) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            debugs(83, 5, "Extension " << *it <<  " does not supported!");
+            return false;
+        }
+    }
+
+
+    debugs(83, 5, "Hello Data are OK and can be mimicked!");
+
+    //Adjust ssl structure data.
+
+    // We need to fix the random in SSL struct:
+    memcpy(ssl->s3->client_random, features.client_random, SSL3_RANDOM_SIZE);
+
+    SSL3_BUFFER *wb=&(ssl->s3->wbuf);
+    assert(wb->len > (size_t)features.helloMessage.contentSize());
+    memcpy(wb->buf, features.helloMessage.content(), features.helloMessage.contentSize());
+    wb->left = features.helloMessage.contentSize();
+
+    size_t mainHelloSize = features.helloMessage.contentSize() - 5;
+    const char *mainHello = features.helloMessage.content() + 5;
+    assert(ssl->init_buf->max > mainHelloSize);
+    memcpy(ssl->init_buf->data, mainHello, mainHelloSize);
+    debugs(83, 5, "Hello Data init and adjustd sizes :" << ssl->init_num << " = "<< mainHelloSize);
+    ssl->init_num = mainHelloSize;
+    ssl->s3->wpend_ret = mainHelloSize;
+    ssl->s3->wpend_tot = mainHelloSize;
+    return true;
+}
+
 int
 Ssl::ServerBio::write(const char *buf, int size, BIO *table)
 {
 
     if (holdWrite_) {
-        debugs(83, 7,  "Hold write, for SSL connection on " << fd_);
+        debugs(83, 7,  "Hold write, for SSL connection on " << fd_ << "will not write bytes of size " << size);
         BIO_set_retry_write(table);
         return -1;
     }
 
     if (!helloBuild) {
+        if (helloMsg.isNull())
+            helloMsg.init(1024, 16384);
+
         if (
             buf[1] >= 3  //it is an SSL Version3 message
             && buf[0] == 0x16 // and it is a Handshake/Hello message
             ) {
-            if (helloMsg.isNull())
-                helloMsg.init(1024, 4096);
 
             //Hello message is the first message we write to server
             assert(!helloMsg.hasContent());
 
             SSL *ssl = fd_table[fd_].ssl;
-            if (randomSet && ssl && ssl->s3) {
-                assert(size > 11 + SSL3_RANDOM_SIZE);
-                helloMsg.append(buf, 11);
-                //The random number is stored in the 11 position of the 
-                // message we are going to sent
-                helloMsg.append((char *)clientRandom, SSL3_RANDOM_SIZE);
-                size_t len = size - 11 - SSL3_RANDOM_SIZE;
-                helloMsg.append(buf + 11 + SSL3_RANDOM_SIZE, len);
-
-                // We need to fix the random in SSL struct:
-                memcpy(ssl->s3->client_random, clientRandom, SSL3_RANDOM_SIZE);
-                // We also need to fix the raw message in SSL struct
-                // stored in SSL->init_buf. Looks that it is used to get
-                // digest of the previous sent SSL message, to compute keys
-                // for encryption/decryption:
-                memcpy(ssl->init_buf->data + 6, clientRandom, SSL3_RANDOM_SIZE);
-
-                debugs(83, 7,  "SSL HELLO message for FD " << fd_ << ": Random number is adjusted");
+            if (featuresSet && ssl && ssl->s3) {
+                if (adjustSSL(ssl, clientFeatures)) {
+                    allowSplice = true;
+                    helloMsg.append(clientFeatures.helloMessage.content(), clientFeatures.helloMessage.contentSize());
+                    debugs(83, 7,  "SSL HELLO message for FD " << fd_ << ": Random number is adjusted");
+                }
             }
         }
+        // If we do not build any hello message, copy the current
+        if (!helloMsg.hasContent())
+            helloMsg.append(buf, size);
+
         helloBuild = true;
         helloMsgSize = helloMsg.contentSize();
+
+        if (allowSplice) {
+        // Do not write yet.....
+            BIO_set_retry_write(table);
+            return -1;
+        }
     }
 
     if (helloMsg.hasContent()) {
@@ -350,9 +472,10 @@ Ssl::ServerBio::write(const char *buf, int size, BIO *table)
         }
 
         // Sending hello message complete. Do not send more data for now...
-        holdWrite_ = true; 
+        holdWrite_ = true;
+
         // The size should be less than the size of the hello message
-        assert(size >= helloMsgSize);
+        //assert(size >= helloMsgSize);
         return helloMsgSize;
     } else
         return Ssl::Bio::write(buf, size, table);
@@ -486,7 +609,7 @@ squid_ssl_info(const SSL *ssl, int where, int ret)
     }
 }
 
-Ssl::Bio::sslFeatures::sslFeatures(): sslVersion(-1), compressMethod(-1)
+Ssl::Bio::sslFeatures::sslFeatures(): sslVersion(-1), compressMethod(-1), unknownCiphers(false), doHeartBeats(true)
 {
     memset(client_random, 0, SSL3_RANDOM_SIZE);
 }
@@ -621,6 +744,8 @@ Ssl::Bio::sslFeatures::parseV3Hello(const unsigned char *hello)
     // The following hello message size exist in 4th and 5th bytes
     int helloSize = (hello[3] << 8) | hello[4];
     helloSize += 5; //Include the 5 header bytes.
+    helloMessage.init(helloSize, helloSize);
+    helloMessage.append((const char *)hello, helloSize);
 
     //For SSLv3 or TLSv1.* protocols we can get some more informations
     if (hello[1] == 0x3 && hello[5] == 0x1 /*HELLO A message*/) {
@@ -648,7 +773,8 @@ Ssl::Bio::sslFeatures::parseV3Hello(const unsigned char *hello)
                     if(!clientRequestedCiphers.empty())
                         clientRequestedCiphers.append(":");
                     clientRequestedCiphers.append(c->name);
-                }
+                } else
+                    unknownCiphers = true;
             }
         }
         debugs(83, 7, "Ciphers requested by client: " << clientRequestedCiphers);
@@ -662,11 +788,11 @@ Ssl::Bio::sslFeatures::parseV3Hello(const unsigned char *hello)
             compressMethod = 0;
         debugs(83, 7, "SSL compression methods number: " << (int)compression[0]);
 
-        const unsigned char *extensions = compression + 1 + (int)compression[0];
-        if (extensions <  hello + helloSize) { 
-            int extensionsLen = (extensions[0] << 8) | extensions[1];
-            const unsigned char *ext = extensions + 2;
-            while (ext < extensions+extensionsLen){
+        const unsigned char *pToExtensions = compression + 1 + (int)compression[0];
+        if (pToExtensions <  hello + helloSize) { 
+            int extensionsLen = (pToExtensions[0] << 8) | pToExtensions[1];
+            const unsigned char *ext = pToExtensions + 2;
+            while (ext < pToExtensions + extensionsLen){
                 short extType = (ext[0] << 8) | ext[1];
                 ext += 2;
                 short extLen = (ext[0] << 8) | ext[1];
@@ -680,7 +806,12 @@ Ssl::Bio::sslFeatures::parseV3Hello(const unsigned char *hello)
                     int hostLen = (ext[3] << 8) | ext[4];
                     serverName.assign((const char *)(ext+5), hostLen);
                     debugs(83, 7, "Found server name: " << serverName);
-                }
+                } else if (extType == 15 && ext[0] != 0) {
+                    // The heartBeats are the type 15
+                    doHeartBeats = true;
+                } else
+                    extensions.push_back(extType);
+                    
                 ext += extLen;
             }
         }
@@ -699,6 +830,8 @@ Ssl::Bio::sslFeatures::parseV23Hello(const unsigned char *hello)
     // The following hello message size exist in 2nd byte
     int helloSize = hello[1];
     helloSize += 2; //Include the 2 header bytes.
+    helloMessage.init(helloSize, helloSize);
+    helloMessage.append((char *)hello, helloSize);
 
     //Ciphers list. It is stored after the Session ID.
 
index 690a6ab09924cf3bc392e5f7eefc32ebf2680745..eb8f08cd85faadf8a7c09566a59d0c15f7fe3ebd 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "MemBuf.h"
 #include <iosfwd>
+#include <list>
 #if HAVE_OPENSSL_BIO_H
 #include <openssl/bio.h>
 #endif
@@ -37,11 +38,15 @@ public:
         int compressMethod; ///< The requested/used compressed  method
         std::string serverName; ///< The SNI hostname, if any
         std::string clientRequestedCiphers; ///< The client requested ciphers
+        bool unknownCiphers; ///< True if one or more ciphers are unknown
         std::string ecPointFormatList;///< tlsExtension ecPointFormatList
         std::string ellipticCurves; ///< tlsExtension ellipticCurveList
         std::string opaquePrf; ///< tlsExtension opaquePrf
+        bool doHeartBeats;
         /// The client random number
         unsigned char client_random[SSL3_RANDOM_SIZE];
+        std::list<int> extensions;
+        MemBuf helloMessage;
     };
     explicit Bio(const int anFd);
     ~Bio();
@@ -109,7 +114,7 @@ private:
 /// BIO node to handle socket IO for squid server side
 class ServerBio: public Bio {
 public:
-    explicit ServerBio(const int anFd): Bio(anFd), randomSet(false), helloMsgSize(0), helloBuild(false), holdWrite_(false), record_(false) {}
+    explicit ServerBio(const int anFd): Bio(anFd), featuresSet(false), helloMsgSize(0), helloBuild(false), allowSplice(false), holdWrite_(false), record_(false) {}
     /// The ServerBio version of the Ssl::Bio::stateChanged method
     virtual void stateChanged(const SSL *ssl, int where, int ret);
     /// The ServerBio version of the Ssl::Bio::write method
@@ -124,19 +129,21 @@ public:
     /// Flushes any buffered data
     virtual void flush(BIO *table);
     /// Sets the random number to use in client SSL HELLO message
-    void setClientRandom(const unsigned char *r);
+    void setClientFeatures(const sslFeatures &features);
 
     bool holdWrite() const {return holdWrite_;}
     void holdWrite(bool h) {holdWrite_ = h;}
     void recordInput(bool r) {record_ = r;}
     const MemBuf &rBufData() {return rbuf;}
+    bool canSplice() {return allowSplice;}
 private:
     /// A random number to use as "client random" in client hello message
-    unsigned char clientRandom[SSL3_RANDOM_SIZE];
-    bool randomSet; ///< True if the clientRandom member is set and can be used
+    sslFeatures clientFeatures;
+    bool featuresSet; ///< True if the clientFeatures member is set and can be used
     MemBuf helloMsg; ///< Used to buffer output data.
     int helloMsgSize;
     bool helloBuild; ///< True if the client hello message sent to the server
+    bool allowSplice;
     bool holdWrite_;  ///< The write hold state of the bio.
     bool record_;
     MemBuf rbuf;  ///< Used to buffer input data.
index 33566e137a822289b232050fbb4339b729c13865..39547235361bbc93ceee2d9633df3c376812ee11 100644 (file)
@@ -66,6 +66,7 @@ const char *Ssl::BumpModeStr[] = {
     "client-first",
     "server-first",
     "peek-and-splice",
+    "splice",
     NULL
 };
 
index c5384115705f6cb0c291b45310fdd2f96f8dfe6c..45af1afa7f3bbce7f7ced00224a0c9db250154dc 100644 (file)
@@ -158,7 +158,7 @@ GETX509ATTRIBUTE GetX509Fingerprint;
   \ingroup ServerProtocolSSLAPI
  * Supported ssl-bump modes
  */
-enum BumpMode {bumpNone = 0, bumpClientFirst, bumpServerFirst, bumpPeekAndSplice, bumpEnd};
+enum BumpMode {bumpNone = 0, bumpClientFirst, bumpServerFirst, bumpPeekAndSplice, bumpSplice, bumpEnd};
 
 /**
  \ingroup  ServerProtocolSSLAPI