]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Improve TLS/SSL parsing code in Handshale.cc and use it inside bio.cc for client
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Tue, 29 Mar 2016 19:17:34 +0000 (22:17 +0300)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Tue, 29 Mar 2016 19:17:34 +0000 (22:17 +0300)
and server messages

  - full implementation for TLS and SSLv2 parsers inside Handshake.cc/h files
  - remove parsing code from bio.cc
  - Store parsed info in new Security::TlsDetails struct and remove the
    Ssl::sslFeatures class
  - improve SSLv2 parsing code.

src/client_side.cc
src/security/Handshake.cc
src/security/Handshake.h
src/security/NegotiationHistory.cc
src/ssl/PeekingPeerConnector.cc
src/ssl/bio.cc
src/ssl/bio.h

index 5203897df4c15036376c51eee39caca892c0cd6b..c0de5520ee2e49332eba1768356fc74bd6897406 100644 (file)
@@ -3176,10 +3176,10 @@ clientPeekAndSpliceSSL(int fd, void *data)
 
     if (bio->gotHello()) {
         if (conn->serverBump()) {
-            Ssl::Bio::sslFeatures const &features = bio->receivedHelloFeatures();
-            if (!features.serverName.isEmpty()) {
-                conn->serverBump()->clientSni = features.serverName;
-                conn->resetSslCommonName(features.serverName.c_str());
+            Security::TlsDetails::Pointer const &details = bio->receivedHelloDetails();
+            if (!details->serverName.isEmpty()) {
+                conn->serverBump()->clientSni = details->serverName;
+                conn->resetSslCommonName(details->serverName.c_str());
             }
         }
 
index f0e05ab27139d6f9ff7abeb2dead42824d760c6a..747d0bd9acca7e263620ed9c2698f187dbdf8286 100644 (file)
@@ -21,22 +21,14 @@ Security::ProtocolVersion::ProtocolVersion(BinaryTokenizer &tk):
 {
 }
 
-Security::ProtocolVersion::ProtocolVersion(uint8_t maj, uint8_t min):
-    vMajor(maj),
-    vMinor(min)
-{
-}
-
 Security::TLSPlaintext::TLSPlaintext(BinaryTokenizer &tk):
     FieldGroup(tk, "TLSPlaintext"),
     type(tk.uint8(".type")),
-    version((type & 0x80) ? ProtocolVersion(2, 0): ProtocolVersion(tk))
+    version(tk),
+    length(tk.uint16(".length"))
 {
-    if (type & 0x80){ // V2 compatible protocol
-        length = tk.uint8(".length");
-    } else { //TLS protocol
-        length = tk.uint16(".length");
-    }
+    Must(version.vMajor == 3 && version.vMinor <= 3);
+    Must(type >= ctChangeCipherSpec && type <= ctApplicationData);
     fragment = tk.area(length, ".fragment");
     commit(tk);
 }
@@ -91,6 +83,20 @@ Security::Extension::Extension(BinaryTokenizer &tk):
     commit(tk);
 }
 
+Security::SSL2Record::SSL2Record(BinaryTokenizer &tk):
+    FieldGroup(tk, "SSL2Record")
+{
+    uint16_t head = tk.uint16(".head(Record+Length)");
+    length = head & 0x7FFF;
+    type = tk.uint8(".type");
+    if ((head & 0x8000) == 0 || length == 0 || type != 0x01)
+        throw TexcHere("Not an SSLv2 message");
+    version = 0x02;
+    // The remained message has length of length-sizeof(type)=(length-1);
+    fragment = tk.area(length - 1, ".fragment");
+    commit(tk);
+}
+
 //The SNI extension has the type 0 (extType == 0)
 // RFC6066 sections 3, 10.2
 // The two first bytes indicates the length of the SNI data
@@ -102,10 +108,10 @@ Security::SniExtension::SniExtension(BinaryTokenizer &tk):
     type(tk.uint8(".type"))
 {
     if (type == 0) {
-        P16String aName(tk, "server name");
+        P16String aName(tk, ".serverName");
         serverName = aName.body;
     } else
-        tk.skip(listLength - 1, "list without list type");
+        tk.skip(listLength - 1, ".unknownType");
     commit(tk);
 }
 
@@ -138,10 +144,37 @@ operator <<(std::ostream &os, const DebugFrame &frame)
     return os << frame.size << "-byte type-" << frame.type << ' ' << frame.name;
 }
 
+bool
+Security::HandshakeParser::parseRecordVersion2Try()
+{
+    try {
+        const SSL2Record record(tkRecords);
+        details->tlsVersion = record.version;
+        parseVersion2HandshakeMessage(record.fragment);
+        state = atHelloReceived;
+        parseDone = true;
+        return true;
+    } catch (const BinaryTokenizer::InsufficientInput &) {
+        throw BinaryTokenizer::InsufficientInput();
+    } catch (const std::exception &ex) {
+        debugs(83, 5, "fallback to the TLS records parser: " << ex.what());
+        useTlsParser = true;
+        tkRecords.rollback();
+        return false;
+    }
+    return false;
+}
+
 /// parses a single TLS Record Layer frame
 void
 Security::HandshakeParser::parseRecord()
 {
+    if (details == NULL)
+        details = new TlsDetails;
+
+    if (!useTlsParser && parseRecordVersion2Try())
+        return;
+
     const TLSPlaintext record(tkRecords);
 
     Must(record.length <= (1 << 14)); // RFC 5246: length MUST NOT exceed 2^14
@@ -149,10 +182,7 @@ Security::HandshakeParser::parseRecord()
     // RFC 5246: MUST NOT send zero-length [non-application] fragments
     Must(record.length || record.type == ContentType::ctApplicationData);
 
-    if (details == NULL) {
-        details = new TlsDetails;
-        details->tlsVersion = (record.version.vMajor & 0xFF) << 8 | (record.version.vMinor & 0xFF);
-    }
+    details->tlsVersion = (record.version.vMajor & 0xFF) << 8 | (record.version.vMinor & 0xFF);
 
     if (currentContentType != record.type) {
         Must(tkMessages.atEnd()); // no currentContentType leftovers
@@ -186,11 +216,6 @@ Security::HandshakeParser::parseMessages()
         case ContentType::ctApplicationData:
             parseApplicationDataMessage();
             continue;
-        case ContentType::ctVersion2: {
-            SBuf raw; //Should fixed to body after record
-            parseVersion2HandshakeMessage(raw);
-            continue;
-        }
         }
         skipMessage("unknown ContentType msg");
     }
@@ -269,12 +294,16 @@ Security::HandshakeParser::parseVersion2HandshakeMessage(const SBuf &raw)
     Must(details);
 
     details->tlsSupportedVersion = tkHsk.uint16("tlsSupportedVersion");
-    tkHsk.commit();
-    P16String ciphers(tkHsk, "Ciphers list");
-    // TODO: retrieve ciphers list
-    P16String session(tkHsk, "Session ID");
-    details->sessionId = session.body;
-    P16String challenge(tkHsk, "Challenge");
+    uint16_t ciphersLen = tkHsk.uint16(".cipherSpecLength");
+    uint16_t sessionIdLen = tkHsk.uint16(".sessionIdLength");
+    uint16_t challengeLen = tkHsk.uint16(".challengeLength");
+
+    SBuf ciphers = tkHsk.area(ciphersLen, "Ciphers list");
+    parseV23Ciphers(ciphers);
+    details->sessionId = tkHsk.area(sessionIdLen, "Session Id");
+
+    // We do not actually need it:
+    SBuf challenge = tkHsk.area(challengeLen, "Challenge");
 }
 
 void
@@ -288,11 +317,13 @@ Security::HandshakeParser::parseClientHelloHandshakeMessage(const SBuf &raw)
     P8String session(tkHsk, "Session ID");
     details->sessionId = session.body;
     P16String ciphers(tkHsk, "Ciphers list");
-    // TODO: retrieve ciphers list
+    parseCiphers(ciphers.body);
     P8String compression(tkHsk, "Compression methods");
     details->compressMethod = compression.length > 0 ? 1 : 0; // Only deflate supported here.
-    P16String extensions(tkHsk, "Extensions List");
-    parseExtensions(extensions.body);
+    if (!tkHsk.atEnd()) { //Then we have extensions
+        P16String extensions(tkHsk, "Extensions List");
+        parseExtensions(extensions.body);
+    }
 }
 
 void
index 1f3a2339fad4b3b27dd4fbb60944f93636ca705f..5041d83aa10b8c739823ba10e6ce41f9fb9a4f71 100644 (file)
@@ -33,7 +33,6 @@ public:
 
 /// TLS Record Layer's content types from RFC 5246 Section 6.2.1
 enum ContentType {
-    ctVersion2 = 128,
     ctChangeCipherSpec = 20,
     ctAlert = 21,
     ctHandshake = 22,
@@ -44,7 +43,6 @@ enum ContentType {
 struct ProtocolVersion
 {
     explicit ProtocolVersion(BinaryTokenizer &tk);
-    ProtocolVersion(uint8_t, uint8_t);
 
     // the "v" prefix works around environments that #define major and minor
     uint8_t vMajor;
@@ -62,6 +60,15 @@ struct TLSPlaintext: public FieldGroup
     SBuf fragment; ///< exactly length bytes
 };
 
+struct SSL2Record: public FieldGroup
+{
+    explicit SSL2Record(BinaryTokenizer &tk);
+    uint16_t version;
+    uint16_t length;
+    uint8_t type;
+    SBuf fragment;
+};
+
 /// TLS Handshake protocol's handshake types from RFC 5246 Section 7.4
 enum HandshakeType {
     hskClientHello = 1,
@@ -142,6 +149,9 @@ public:
     typedef RefCount<TlsDetails> Pointer;
 
     TlsDetails();
+    /// Prints to os stream a human readable form of TlsDetails object
+    std::ostream & print(std::ostream &os) const;
+
     int tlsVersion; ///< The TLS hello message version
     int tlsSupportedVersion; ///< The requested/used TLS version
     int compressMethod; ///< The requested/used compressed  method
@@ -158,13 +168,19 @@ public:
     std::list<uint16_t> extensions;
 };
 
+inline
+std::ostream &operator <<(std::ostream &os, Security::TlsDetails const &details)
+{
+    return details.print(os);
+}
+
 /// Incremental SSL Handshake parser.
 class HandshakeParser {
 public:
     /// The parsing states
     typedef enum {atHelloNone = 0, atHelloStarted, atHelloReceived, atCertificatesReceived, atHelloDoneReceived, atNstReceived, atCcsReceived, atFinishReceived} ParserState;
 
-    HandshakeParser(): state(atHelloNone), ressumingSession(false), parseDone(false), parseError(false), currentContentType(0), unParsedContent(0), parsingPos(0), currentMsg(0), currentMsgSize(0), certificatesMsgPos(0), certificatesMsgSize(0) {}
+    HandshakeParser(): state(atHelloNone), ressumingSession(false), parseDone(false), parseError(false), currentContentType(0), unParsedContent(0), parsingPos(0), currentMsg(0), currentMsgSize(0), certificatesMsgPos(0), certificatesMsgSize(0), useTlsParser(false) {}
 
     /// Parses the initial sequence of raw bytes sent by the SSL server.
     /// Returns true upon successful completion (HelloDone or Finished received).
@@ -176,6 +192,7 @@ public:
     /// Otherwise, returns false (and sets parseError to true on errors).
     bool parseClientHello(const SBuf &data);
 
+    TlsDetails::Pointer details;
 #if USE_OPENSSL
     Ssl::X509_STACK_Pointer serverCertificates; ///< parsed certificates chain
 #endif
@@ -209,6 +226,7 @@ private:
     void parseApplicationDataMessage();
     void skipMessage(const char *msgType);
 
+    bool parseRecordVersion2Try();
     void parseVersion2HandshakeMessage(const SBuf &raw);
     void parseClientHelloHandshakeMessage(const SBuf &raw);
     void parseServerHelloHandshakeMessage(const SBuf &raw);
@@ -228,7 +246,7 @@ private:
     BinaryTokenizer tkRecords; // TLS record layer (parsing uninterpreted data)
     BinaryTokenizer tkMessages; // TLS message layer (parsing fragments)
 
-    TlsDetails::Pointer details;
+    bool useTlsParser; // Whether to use TLS parser or a V2 compatible parser
 };
 
 }
index 5bad38247d417a5dd56389f3b58a65fd6047a17d..d9906104704c09eb9cdff9db911e174936094916 100644 (file)
@@ -65,13 +65,14 @@ Security::NegotiationHistory::fillWith(Security::SessionPtr ssl)
     Ssl::Bio *bio = static_cast<Ssl::Bio *>(b->ptr);
 
     if (::Config.onoff.logTlsServerHelloDetails) {
-        if (Ssl::ServerBio *srvBio = dynamic_cast<Ssl::ServerBio *>(bio))
-            srvBio->extractHelloFeatures();
+        //PRobably move this if inside HandhakeParser
+        // if (Ssl::ServerBio *srvBio = dynamic_cast<Ssl::ServerBio *>(bio))
+        //    srvBio->extractHelloFeatures();
     }
 
-    const Ssl::Bio::sslFeatures &features = bio->receivedHelloFeatures();
-    helloVersion_ = features.sslHelloVersion;
-    supportedVersion_ = features.sslVersion;
+    const Security::TlsDetails::Pointer &details = bio->receivedHelloDetails();
+    helloVersion_ = details->tlsVersion;
+    supportedVersion_ = details->tlsSupportedVersion;
 
     debugs(83, 5, "SSL connection info on FD " << bio->fd() <<
            " SSL version " << version_ <<
index 88ae4ed4d407b6783f945c6dd3f3c38faf16165e..89ec1a1dcced56aae51d516d6992b675d88456be 100644 (file)
@@ -155,9 +155,9 @@ Ssl::PeekingPeerConnector::initializeSsl()
         if (auto clientSsl = fd_table[clientConn->fd].ssl.get()) {
             BIO *b = SSL_get_rbio(clientSsl);
             cltBio = static_cast<Ssl::ClientBio *>(b->ptr);
-            const Ssl::Bio::sslFeatures &features = cltBio->receivedHelloFeatures();
-            if (!features.serverName.isEmpty())
-                hostName = new SBuf(features.serverName);
+            const Security::TlsDetails::Pointer &details = cltBio->receivedHelloDetails();
+            if (details != NULL && !details->serverName.isEmpty())
+                hostName = new SBuf(details->serverName);
         }
 
         if (!hostName) {
@@ -175,15 +175,15 @@ Ssl::PeekingPeerConnector::initializeSsl()
         Must(!csd->serverBump() || csd->serverBump()->step <= Ssl::bumpStep2);
         if (csd->sslBumpMode == Ssl::bumpPeek || csd->sslBumpMode == Ssl::bumpStare) {
             assert(cltBio);
-            const Ssl::Bio::sslFeatures &features = cltBio->receivedHelloFeatures();
-            if (features.sslVersion != -1) {
-                features.applyToSSL(ssl, csd->sslBumpMode);
+            const Security::TlsDetails::Pointer &details = cltBio->receivedHelloDetails();
+            if (details->tlsVersion != -1) {
+                applyTlsDetailsToSSL(ssl, details, csd->sslBumpMode);
                 // Should we allow it for all protocols?
-                if (features.sslVersion >= 3) {
+                if (details->tlsVersion >= 3) {
                     BIO *b = SSL_get_rbio(ssl);
                     Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
                     // Inherite client features, like SSL version, SNI and other
-                    srvBio->setClientFeatures(features);
+                    srvBio->setClientFeatures(details, cltBio->rBufData());
                     srvBio->recordInput(true);
                     srvBio->mode(csd->sslBumpMode);
                 }
index 9c2b22ad795b415ae815b5b631bd7a0cb53ad992..ef242d0cfa4307c94927613132d3eeaa7b141e16 100644 (file)
@@ -195,35 +195,15 @@ Ssl::ClientBio::write(const char *buf, int size, BIO *table)
 int
 Ssl::ClientBio::read(char *buf, int size, BIO *table)
 {
-    if (helloState < atHelloReceived) {
+    if (!parser_.parseDone) {
         int bytes = readAndBuffer(table, "TLS client Hello");
         if (bytes <= 0)
             return bytes;
-    }
-
-    if (helloState == atHelloNone) {
-        helloSize = receivedHelloFeatures_.parseMsgHead(rbuf);
-        if (helloSize == 0) {
-            // Not enough bytes to get hello message size
-            BIO_set_retry_read(table);
-            return -1;
-        } else if (helloSize < 0) {
-            wrongProtocol = true;
+        if (!parser_.parseClientHello(rbuf)) {
+            if (!parser_.parseError) 
+                BIO_set_retry_read(table);
             return -1;
         }
-
-        helloState = atHelloStarted; //Next state
-    }
-
-    if (helloState == atHelloStarted) {
-        debugs(83, 7, "SSL Header: " << Raw(nullptr, rbuf.rawContent(), rbuf.length()).hex());
-
-        if (helloSize > (int)rbuf.length()) {
-            BIO_set_retry_read(table);
-            return -1;
-        }
-        receivedHelloFeatures_.get(rbuf);
-        helloState = atHelloReceived;
     }
 
     if (holdRead_) {
@@ -232,7 +212,7 @@ Ssl::ClientBio::read(char *buf, int size, BIO *table)
         return -1;
     }
 
-    if (helloState == atHelloReceived) {
+    if (parser_.parseDone) {
         if (!rbuf.isEmpty()) {
             int bytes = (size <= (int)rbuf.length() ? size : rbuf.length());
             memcpy(buf, rbuf.rawContent(), bytes);
@@ -252,9 +232,10 @@ Ssl::ServerBio::stateChanged(const SSL *ssl, int where, int ret)
 }
 
 void
-Ssl::ServerBio::setClientFeatures(const Ssl::Bio::sslFeatures &features)
+Ssl::ServerBio::setClientFeatures(Security::TlsDetails::Pointer const &details, SBuf const &aHello)
 {
-    clientFeatures = features;
+    clientTlsDetails = details;
+    clientHelloMessage = aHello;
 };
 
 int
@@ -312,7 +293,7 @@ Ssl::ServerBio::read(char *buf, int size, BIO *table)
 // This is mostly possible in the cases where the web client uses openSSL
 // library similar with this one used by squid.
 static bool
-adjustSSL(SSL *ssl, Ssl::Bio::sslFeatures &features)
+adjustSSL(SSL *ssl, Security::TlsDetails::Pointer const &details, SBuf &helloMessage)
 {
 #if SQUID_USE_OPENSSL_HELLO_OVERWRITE_HACK
     if (!ssl->s3) {
@@ -323,9 +304,9 @@ adjustSSL(SSL *ssl, Ssl::Bio::sslFeatures &features)
     // If the client supports compression but our context does not support
     // we can not adjust.
 #if !defined(OPENSSL_NO_COMP)
-    const bool requireCompression = (features.compressMethod && ssl->ctx->comp_methods == NULL);
+    const bool requireCompression = (details->compressMethod && ssl->ctx->comp_methods == NULL);
 #else
-    const bool requireCompression = features.compressMethod;
+    const bool requireCompression = details->compressMethod;
 #endif
     if (requireCompression) {
         debugs(83, 5, "Client Hello Data supports compression, but we do not!");
@@ -333,37 +314,31 @@ adjustSSL(SSL *ssl, Ssl::Bio::sslFeatures &features)
     }
 
     // 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);
+    for (auto cipherId: details->ciphers) {
         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)) {
+            const long cid = SSL_CIPHER_get_id(c);
+            if (cipherId == (0xFFFF & cid)) {
                 found = true;
                 break;
             }
         }
         if (!found) {
-            debugs(83, 5, "Client Hello Data supports cipher '"<< cipher <<"' but we do not support it!");
+            debugs(83, 5, "Client Hello Data supports cipher '"<< cipherId <<"' but we do not support it!");
             return false;
         }
     }
 
 #if !defined(SSL_TLSEXT_HB_ENABLED)
-    if (features.doHeartBeats) {
+    if (details->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) {
+    for (std::list<uint16_t>::iterator it = details->extensions.begin(); it != details->extensions.end(); ++it) {
         static int supportedExtensions[] = {
 #if defined(TLSEXT_TYPE_server_name)
             TLSEXT_TYPE_server_name,
@@ -411,19 +386,20 @@ adjustSSL(SSL *ssl, Ssl::Bio::sslFeatures &features)
     }
 
     SSL3_BUFFER *wb=&(ssl->s3->wbuf);
-    if (wb->len < (size_t)features.helloMessage.length())
+    if (wb->len < (size_t)helloMessage.length())
         return false;
 
     debugs(83, 5, "OpenSSL SSL struct will be adjusted to mimic client hello data!");
 
     //Adjust ssl structure data.
     // We need to fix the random in SSL struct:
-    memcpy(ssl->s3->client_random, features.client_random, SSL3_RANDOM_SIZE);
-    memcpy(wb->buf, features.helloMessage.rawContent(), features.helloMessage.length());
-    wb->left = features.helloMessage.length();
+    if (details->clientRandom.length() == SSL3_RANDOM_SIZE)
+        memcpy(ssl->s3->client_random, details->clientRandom.c_str(), SSL3_RANDOM_SIZE);
+    memcpy(wb->buf, helloMessage.rawContent(), helloMessage.length());
+    wb->left = helloMessage.length();
 
-    size_t mainHelloSize = features.helloMessage.length() - 5;
-    const char *mainHello = features.helloMessage.rawContent() + 5;
+    size_t mainHelloSize = helloMessage.length() - 5;
+    const char *mainHello = helloMessage.rawContent() + 5;
     assert((size_t)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);
@@ -456,18 +432,18 @@ Ssl::ServerBio::write(const char *buf, int size, BIO *table)
             assert(helloMsg.isEmpty());
 
             auto ssl = fd_table[fd_].ssl.get();
-            if (clientFeatures.initialized_ && ssl) {
+            if (ssl) {
                 if (bumpMode_ == Ssl::bumpPeek) {
-                    if (adjustSSL(ssl, clientFeatures))
+                    if (adjustSSL(ssl, clientTlsDetails, clientHelloMessage))
                         allowBump = true;
                     allowSplice = true;
-                    helloMsg.append(clientFeatures.helloMessage);
+                    helloMsg.append(clientHelloMessage);
                     debugs(83, 7,  "SSL HELLO message for FD " << fd_ << ": Random number is adjusted for peek mode");
                 } else { /*Ssl::bumpStare*/
                     allowBump = true;
-                    if (adjustSSL(ssl, clientFeatures)) {
+                    if (adjustSSL(ssl, clientTlsDetails, clientHelloMessage)) {
                         allowSplice = true;
-                        helloMsg.append(clientFeatures.helloMessage);
+                        helloMsg.append(clientHelloMessage);
                         debugs(83, 7,  "SSL HELLO message for FD " << fd_ << ": Random number is adjusted for stare mode");
                     }
                 }
@@ -517,13 +493,6 @@ Ssl::ServerBio::flush(BIO *table)
     }
 }
 
-void
-Ssl::ServerBio::extractHelloFeatures()
-{
-    if (!receivedHelloFeatures_.initialized_)
-        receivedHelloFeatures_.get(rbuf, false);
-}
-
 bool
 Ssl::ServerBio::resumingSession()
 {
@@ -650,472 +619,60 @@ squid_ssl_info(const SSL *ssl, int where, int ret)
     }
 }
 
-Ssl::Bio::sslFeatures::sslFeatures():
-    sslHelloVersion(-1),
-    sslVersion(-1),
-    compressMethod(-1),
-    helloMsgSize(0),
-    unknownCiphers(false),
-    doHeartBeats(true),
-    tlsTicketsExtension(false),
-    hasTlsTicket(false),
-    tlsStatusRequest(false),
-    initialized_(false)
-{
-    memset(client_random, 0, SSL3_RANDOM_SIZE);
-}
-
-int Ssl::Bio::sslFeatures::toSquidSSLVersion() const
-{
-    if (sslVersion == SSL2_VERSION)
-        return 2;
-    else if (sslVersion == SSL3_VERSION)
-        return 3;
-    else if (sslVersion == TLS1_VERSION)
-        return 4;
-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
-    else if (sslVersion == TLS1_1_VERSION)
-        return 5;
-    else if (sslVersion == TLS1_2_VERSION)
-        return 6;
-#endif
-    else
-        return 1;
-}
 
-bool
-Ssl::Bio::sslFeatures::get(const SSL *ssl)
+void
+applyTlsDetailsToSSL(SSL *ssl, Security::TlsDetails::Pointer const &details, Ssl::BumpMode bumpMode)
 {
-    sslVersion = SSL_version(ssl);
-    debugs(83, 7, "SSL version: " << SSL_get_version(ssl) << " (" << sslVersion << ")");
-
+    // To increase the possibility for bumping after peek mode selection or
+    // splicing after stare mode selection it is good to set the
+    // SSL protocol version.
+    // The SSL_set_ssl_method is not the correct method because it will strict
+    // SSL version which can be used to the SSL version used for client hello message.
+    // For example will prevent comunnicating with a tls1.0 server if the
+    // client sent and tlsv1.2 Hello message.
 #if defined(TLSEXT_NAMETYPE_host_name)
-    if (const char *server = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))
-        serverName = server;
-    debugs(83, 7, "SNI server name: " << serverName);
-#endif
-
-#if !defined(OPENSSL_NO_COMP)
-    if (ssl->session->compress_meth)
-        compressMethod = ssl->session->compress_meth;
-    else if (sslVersion >= 3) //if it is 3 or newer version then compression is disabled
-#endif
-        compressMethod = 0;
-    debugs(83, 7, "SSL compression: " << compressMethod);
-
-    STACK_OF(SSL_CIPHER) * ciphers = NULL;
-    if (ssl->server)
-        ciphers = ssl->session->ciphers;
-    else
-        ciphers = ssl->cipher_list;
-    if (ciphers) {
-        for (int i = 0; i < sk_SSL_CIPHER_num(ciphers); ++i) {
-            SSL_CIPHER *c = sk_SSL_CIPHER_value(ciphers, i);
-            if (c != NULL) {
-                if (!clientRequestedCiphers.empty())
-                    clientRequestedCiphers.append(":");
-                clientRequestedCiphers.append(c->name);
-            }
-        }
+    if (!details->serverName.isEmpty()) {
+        SSL_set_tlsext_host_name(ssl, details->serverName.c_str());
     }
-    debugs(83, 7, "Ciphers requested by client: " << clientRequestedCiphers);
-
-    if (sslVersion >=3 && ssl->s3 && ssl->s3->client_random[0]) {
-        memcpy(client_random, ssl->s3->client_random, SSL3_RANDOM_SIZE);
-    }
-
-    initialized_ = true;
-    return true;
-}
-
-int
-Ssl::Bio::sslFeatures::parseMsgHead(const SBuf &buf)
-{
-    debugs(83, 7, "SSL Header: " << Raw(nullptr, buf.rawContent(), buf.length()).hex());
-
-    if (buf.length() < 5)
-        return 0;
-
-    if (helloMsgSize > 0)
-        return helloMsgSize;
-
-    const unsigned char *head = (const unsigned char *)buf.rawContent();
-    // Check for SSLPlaintext/TLSPlaintext record
-    // RFC6101 section 5.2.1
-    // RFC5246 section 6.2.1
-    if (head[0] == 0x16) {
-        debugs(83, 7, "SSL version 3 handshake message");
-        // The SSL version exist in the 2nd and 3rd bytes
-        sslHelloVersion = (head[1] << 8) | head[2];
-        debugs(83, 7, "SSL Version :" << std::hex << std::setw(8) << std::setfill('0') << sslVersion);
-        // The hello message size exist in 4th and 5th bytes
-        helloMsgSize = (head[3] << 8) + head[4];
-        debugs(83, 7, "SSL Header Size: " << helloMsgSize);
-        helloMsgSize +=5;
-    } else if ((head[0] & 0x80) && head[2] == 0x01 && head[3] == 0x03) {
-        debugs(83, 7, "SSL version 2 handshake message with v3 support");
-        sslHelloVersion = 0x0002;
-        debugs(83, 7, "SSL Version :" << std::hex << std::setw(8) << std::setfill('0') << sslVersion);
-        // The hello message size exist in 2nd byte
-        helloMsgSize = head[1];
-        helloMsgSize +=2;
-    } else {
-        debugs(83, 7, "Not an SSL acceptable handshake message (SSLv2 message?)");
-        return (helloMsgSize = -1);
-    }
-
-    // Set object as initialized. Even if we did not full parsing yet
-    // The basic features, like the SSL version is set
-    initialized_ = true;
-    return helloMsgSize;
-}
-
-bool
-Ssl::Bio::sslFeatures::get(const SBuf &buf, bool record)
-{
-    int msgSize;
-    if ((msgSize = parseMsgHead(buf)) <= 0) {
-        debugs(83, 7, "Not a known SSL handshake message");
-        return false;
-    }
-
-    if (msgSize > (int)buf.length()) {
-        debugs(83, 2, "Partial SSL handshake message, can not parse!");
-        return false;
-    }
-
-    if (record)
-        helloMessage = buf;
-
-    const unsigned char *msg = (const unsigned char *)buf.rawContent();
-    if (msg[0] & 0x80)
-        return parseV23Hello(msg, (size_t)msgSize);
-    else {
-        // Hello messages require 5 bytes header + 1 byte Msg type + 3 bytes for Msg size
-        if (buf.length() < 9)
-            return false;
-
-        // Check for the Handshake/Message type
-        // The type 2 is a ServerHello, the type 1 is a ClientHello
-        // RFC5246 section 7.4
-        if (msg[5] == 0x2) { // ServerHello message
-            return parseV3ServerHello(msg, (size_t)msgSize);
-        } else if (msg[5] == 0x1) // ClientHello message,
-            return parseV3Hello(msg, (size_t)msgSize);
-    }
-
-    return false;
-}
-
-bool
-Ssl::Bio::sslFeatures::parseV3ServerHello(const unsigned char *messageContainer, size_t messageContainerSize)
-{
-    // Parse a ServerHello Handshake message
-    // RFC5246 section 7.4, 7.4.1.3
-    // The ServerHello starts at messageContainer + 5
-    const unsigned char *serverHello = messageContainer + 5;
-
-    // The Length field (bytes 1-3) plus 4 bytes of the serverHello message header (1 handshake type + 3 hello length)
-    const size_t helloSize = ((serverHello[1] << 16) | (serverHello[2] << 8) | serverHello[3]) + 4;
-    debugs(83, 7, "ServerHello message size: " << helloSize);
-    if (helloSize > messageContainerSize) {
-        debugs(83, 2, "ServerHello parse error");
-        return false;
-    }
-
-    // helloSize should be at least 38 bytes long:
-    // (SSL Version + Random + SessionId Length + Cipher Suite + Compression Method)
-    if (helloSize < 38) {
-        debugs(83, 2, "Too short ServerHello message");
-        return false;
-    }
-
-    debugs(83, 7, "Get fake features from v3 ServerHello message.");
-    // Get the correct version of the sub-hello message
-    sslVersion = (serverHello[4] << 8) | serverHello[5];
-    // At the position 38 (HelloHeader (6bytes) + SSL3_RANDOM_SIZE (32bytes))
-    const size_t sessIdLen = static_cast<size_t>(serverHello[38]);
-    debugs(83, 7, "Session ID Length: " <<  sessIdLen);
-
-    // The size should be enough to hold at least the following
-    // 4 (hello header)
-    // + 2 (SSL Version) + 32 (random) + 1 (sessionId length)
-    // + sessIdLength + 2 (cipher suite) + 1 (compression method)
-    // = 42 + sessIdLength
-    if (42 + sessIdLen > helloSize) {
-        debugs(83, 2, "ciphers length parse error");
-        return false;
-    }
-
-    // The sessionID stored at 39 position, after sessionID length field
-    sessionId.assign(reinterpret_cast<const char *>(serverHello + 39), sessIdLen);
-
-    // Check if there are extensions in hello message
-    // RFC5246 section 7.4.1.4
-    if (helloSize > 42 + sessIdLen + 2) {
-        // 42 + sessIdLen
-        const unsigned char *pToExtensions = serverHello + 42 + sessIdLen;
-        const size_t extensionsLen = (pToExtensions[0] << 8) | pToExtensions[1];
-        // Check if the hello size can hold extensions
-        if (42 + 2 + sessIdLen + extensionsLen > helloSize ) {
-            debugs(83, 2, "Extensions length parse error");
-            return false;
-        }
-
-        pToExtensions += 2;
-        const unsigned char *ext = pToExtensions;
-        while (ext + 4 <= pToExtensions + extensionsLen) {
-            const size_t extType = (ext[0] << 8) | ext[1];
-            ext += 2;
-            const size_t extLen = (ext[0] << 8) | ext[1];
-            ext += 2;
-            debugs(83, 7, "TLS Extension: " << std::hex << extType << " of size:" << extLen);
-            // SessionTicket TLS Extension, RFC5077 section 3.2
-            if (extType == 0x23) {
-                tlsTicketsExtension = true;
-            }
-            ext += extLen;
-        }
-    }
-    return true;
-}
-
-bool
-Ssl::Bio::sslFeatures::parseV3Hello(const unsigned char *messageContainer, size_t messageContainerSize)
-{
-    // Parse a ClientHello Handshake message
-    // RFC5246 section 7.4, 7.4.1.2
-    // The ClientHello starts at messageContainer + 5
-    const unsigned char * clientHello = messageContainer + 5;
-
-    debugs(83, 7, "Get fake features from v3 ClientHello message.");
-    // The Length field (bytes 1-3) plus 4 bytes of the clientHello message header (1 handshake type + 3 hello length)
-    const size_t helloSize = ((clientHello[1] << 16) | (clientHello[2] << 8) | clientHello[3]) + 4;
-    debugs(83, 7, "ClientHello message size: " << helloSize);
-    if (helloSize > messageContainerSize) {
-        debugs(83, 2, "ClientHello parse error");
-        return false;
-    }
-
-    // helloSize should be at least 38 bytes long:
-    // (SSL Version(2) + Random(32) + SessionId Length(1) + Cipher Suite Length(2) + Compression Method Length(1))
-    if (helloSize < 38) {
-        debugs(83, 2, "Too short ClientHello message");
-        return false;
-    }
-
-    //For SSLv3 or TLSv1.* protocols we can get some more informations
-    if (messageContainer[1] != 0x3 || clientHello[0] != 0x1 /*HELLO A message*/) {
-        debugs(83, 2, "Not an SSLv3/TLSv1.x client hello message, stop parsing here");
-        return true;
-    }
-
-    // Get the correct version of the sub-hello message
-    sslVersion = (clientHello[4] << 8) | clientHello[5];
-    //Get Client Random number. It starts on the position 6 of clientHello message
-    memcpy(client_random, clientHello + 6, SSL3_RANDOM_SIZE);
-    debugs(83, 7, "Client random: " <<  Raw(nullptr, (char *)client_random, SSL3_RANDOM_SIZE).hex());
-
-    // At the position 38 (6+SSL3_RANDOM_SIZE)
-    const size_t sessIDLen = static_cast<size_t>(clientHello[38]);
-    debugs(83, 7, "Session ID Length: " <<  sessIDLen);
-
-    // The helloSize should be enough to hold at least the following
-    // 1 handshake type + 3 hello Length
-    // + 2 (SSL Version) + 32 (random) + 1 (sessionId length)
-    // + sessIdLength + 2 (cipher suite length) + 1 (compression method length)
-    // = 42 + sessIdLength
-    if (42 + sessIDLen > helloSize) {
-        debugs(83, 2, "Session ID length parse error");
-        return false;
-    }
-
-    // The sessionID stored art 39 position, after sessionID length field
-    sessionId.assign(reinterpret_cast<const char *>(clientHello + 39), sessIDLen);
-
-    //Ciphers list. It is stored after the Session ID.
-    // It is a variable-length vector(RFC5246 section 4.3)
-    const unsigned char *ciphers = clientHello + 39 + sessIDLen;
-    const size_t ciphersLen = (ciphers[0] << 8) | ciphers[1];
-    if (42 + sessIDLen + ciphersLen > helloSize) {
-        debugs(83, 2, "ciphers length parse error");
-        return false;
-    }
-
-    ciphers += 2;
-    if (ciphersLen) {
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
-        const SSL_METHOD *method = TLS_method();
-#else
-        const SSL_METHOD *method = SSLv23_method();
 #endif
-        for (size_t i = 0; i < ciphersLen; i += 2) {
-            // each cipher in v3/tls  HELLO message is of size 2
-            const SSL_CIPHER *c = method->get_cipher_by_char((ciphers + i));
-            if (c != NULL) {
-                if (!clientRequestedCiphers.empty())
-                    clientRequestedCiphers.append(":");
-                clientRequestedCiphers.append(c->name);
-            } else
-                unknownCiphers = true;
-        }
-    }
-    debugs(83, 7, "Ciphers requested by client: " << clientRequestedCiphers);
-
-    // Compression field: 1 bytes the number of compression methods and
-    // 1 byte for each compression method
-    const unsigned char *compression = ciphers + ciphersLen;
-    if (compression[0] > 1)
-        compressMethod = 1;
-    else
-        compressMethod = 0;
-    debugs(83, 7, "SSL compression methods number: " << static_cast<int>(compression[0]));
-
-    // Parse Extensions, RFC5246 section 7.4.1.4
-    const unsigned char *pToExtensions = compression + 1 + static_cast<int>(compression[0]);
-    if ((size_t)((pToExtensions - clientHello) + 2) < helloSize) {
-        const size_t extensionsLen = (pToExtensions[0] << 8) | pToExtensions[1];
-        if ((pToExtensions - clientHello) + 2 + extensionsLen > helloSize) {
-            debugs(83, 2, "Extensions length parse error");
-            return false;
-        }
-
-        pToExtensions += 2;
-        const unsigned char *ext = pToExtensions;
-        while (ext + 4 <= pToExtensions + extensionsLen) {
-            const size_t extType = (ext[0] << 8) | ext[1];
-            ext += 2;
-            const size_t extLen = (ext[0] << 8) | ext[1];
-            ext += 2;
-            debugs(83, 7, "TLS Extension: " << std::hex << extType << " of size:" << extLen);
-
-            if (ext + extLen > pToExtensions + extensionsLen) {
-                debugs(83, 2, "Extension " << std::hex << extType << " length parser error");
-                return false;
-            }
-
-            //The SNI extension has the type 0 (extType == 0)
-            // RFC6066 sections 3, 10.2
-            // The two first bytes indicates the length of the SNI data (should be extLen-2)
-            // The next byte is the hostname type, it should be '0' for normal hostname (ext[2] == 0)
-            // The 3rd and 4th bytes are the length of the hostname
-            if (extType == 0 && ext[2] == 0) {
-                const size_t hostLen = (ext[3] << 8) | ext[4];
-                if (hostLen < extLen)
-                    serverName.assign(reinterpret_cast<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, RFC6520
-                doHeartBeats = true;
-            } else if (extType == 0x23) {
-                //SessionTicket TLS Extension RFC5077
-                tlsTicketsExtension = true;
-                if (extLen != 0)
-                    hasTlsTicket = true;
-            } else if (extType == 0x05) {
-                // RFC6066 sections 8, 10.2
-                tlsStatusRequest = true;
-            } else if (extType == 0x3374) {
-                // detected TLS next protocol negotiate extension
-            } else if (extType == 0x10) {
-                // Application-Layer Protocol Negotiation Extension, RFC7301
-                const size_t listLen = (ext[0] << 8) | ext[1];
-                if (listLen < extLen)
-                    tlsAppLayerProtoNeg.assign(reinterpret_cast<const char *>(ext+5), listLen);
-            } else
-                extensions.push_back(extType);
-
-            ext += extLen;
-        }
-    }
-    return true;
-}
 
-bool
-Ssl::Bio::sslFeatures::parseV23Hello(const unsigned char *hello, size_t size)
-{
-    debugs(83, 7, "Get fake features from v23 ClientHello message.");
-    if (size < 7)
-        return false;
-
-    // Get the SSL/TLS version supported by client
-    sslVersion = (hello[3] << 8) | hello[4];
-
-    //Ciphers list. It is stored after the Session ID.
-    const unsigned int ciphersLen = (hello[5] << 8) | hello[6];
-    const unsigned char *ciphers = hello + 11;
-
-    if (size < ciphersLen + 11)
-        return false;
-
-    if (ciphersLen) {
+    if (!details->ciphers.empty()) {
+        SBuf strCiphers;
+        for (auto cipherId: details->ciphers) {
+            unsigned char cbytes[3];
+            cbytes[0] = (cipherId >> 8) & 0xFF;
+            cbytes[1] = cipherId & 0xFF;
+            cbytes[2] = 0;
 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
-        const SSL_METHOD *method = TLS_method();
+            const SSL_METHOD *method = TLS_method();
 #else
-        const SSL_METHOD *method = SSLv23_method();
+            const SSL_METHOD *method = SSLv23_method();
 #endif
-        for (unsigned int i = 0; i < ciphersLen; i += 3) {
-            // The v2 hello messages cipher has 3 bytes.
-            // The v2 cipher has the first byte not null
-            // Because we are going to sent only v3 message we
-            // are ignoring these ciphers
-            if (ciphers[i] != 0)
-                continue;
-            const SSL_CIPHER *c = method->get_cipher_by_char((ciphers + i + 1));
+            const SSL_CIPHER *c = method->get_cipher_by_char(cbytes);
             if (c != NULL) {
-                if (!clientRequestedCiphers.empty())
-                    clientRequestedCiphers.append(":");
-                clientRequestedCiphers.append(c->name);
+                if (!strCiphers.isEmpty())
+                    strCiphers.append(":");
+                strCiphers.append(c->name);
             }
         }
+        if (!strCiphers.isEmpty())
+            SSL_set_cipher_list(ssl, strCiphers.c_str());
     }
-    debugs(83, 7, "Ciphers requested by client: " << clientRequestedCiphers);
-
-    const unsigned int sessionIdLength = (hello[7] << 8) | hello[8];
-    debugs(83, 7, "SessionID length: " << sessionIdLength);
-    // SessionID starts at: hello+11+ciphersLen
-    if (sessionIdLength)
-        sessionId.assign((const char *)(hello + 11 + ciphersLen), sessionIdLength);
 
-    const unsigned int challengeLength = (hello[5] << 9) | hello[10];
-    debugs(83, 7, "Challenge Length: " << challengeLength);
-    //challenge starts at: hello+11+ciphersLen+sessionIdLength
-
-    compressMethod = 0;
-    return true;
-}
-
-void
-Ssl::Bio::sslFeatures::applyToSSL(SSL *ssl, Ssl::BumpMode bumpMode) const
-{
-    // To increase the possibility for bumping after peek mode selection or
-    // splicing after stare mode selection it is good to set the
-    // SSL protocol version.
-    // The SSL_set_ssl_method is not the correct method because it will strict
-    // SSL version which can be used to the SSL version used for client hello message.
-    // For example will prevent comunnicating with a tls1.0 server if the
-    // client sent and tlsv1.2 Hello message.
-#if defined(TLSEXT_NAMETYPE_host_name)
-    if (!serverName.isEmpty()) {
-        SSL_set_tlsext_host_name(ssl, serverName.c_str());
-    }
-#endif
-    if (!clientRequestedCiphers.empty())
-        SSL_set_cipher_list(ssl, clientRequestedCiphers.c_str());
 #if defined(SSL_OP_NO_COMPRESSION) /* XXX: OpenSSL 0.9.8k lacks SSL_OP_NO_COMPRESSION */
-    if (compressMethod == 0)
+    if (details->compressMethod == 0)
         SSL_set_options(ssl, SSL_OP_NO_COMPRESSION);
 #endif
 
 #if defined(TLSEXT_STATUSTYPE_ocsp)
-    if (tlsStatusRequest)
+    if (details->tlsStatusRequest)
         SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp);
 #endif
 
 #if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
-    if (!tlsAppLayerProtoNeg.isEmpty()) {
+    if (!details->tlsAppLayerProtoNeg.isEmpty()) {
         if (bumpMode == Ssl::bumpPeek)
-            SSL_set_alpn_protos(ssl, (const unsigned char*)tlsAppLayerProtoNeg.rawContent(), tlsAppLayerProtoNeg.length());
+            SSL_set_alpn_protos(ssl, (const unsigned char*)details->tlsAppLayerProtoNeg.rawContent(), tlsAppLayerProtoNeg.length());
         else {
             static const unsigned char supported_protos[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'};
             SSL_set_alpn_protos(ssl, supported_protos, sizeof(supported_protos));
@@ -1124,17 +681,4 @@ Ssl::Bio::sslFeatures::applyToSSL(SSL *ssl, Ssl::BumpMode bumpMode) const
 #endif
 }
 
-std::ostream &
-Ssl::Bio::sslFeatures::print(std::ostream &os) const
-{
-    static std::string buf;
-    // TODO: Also print missing features like the HeartBeats and AppLayerProtoNeg
-    return os << "v" << sslVersion <<
-           " SNI:" << (serverName.isEmpty() ? SBuf("-") : serverName) <<
-           " comp:" << compressMethod <<
-           " Ciphers:" << clientRequestedCiphers <<
-           " Random:" << Raw(nullptr, (char *)client_random, SSL3_RANDOM_SIZE).hex();
-}
-
-#endif /* USE_SSL */
-
+#endif // USE_OPENSSL
index 1399f0908da492a22647e14209caf34076e7cf57..8182f0e950888351a12def51ea7b39171ad38eb0 100644 (file)
@@ -33,52 +33,6 @@ public:
         BIO_TO_SERVER
     };
 
-    /// Class to store SSL connection features
-    class sslFeatures
-    {
-    public:
-        sslFeatures();
-        bool get(const SSL *ssl); ///< Retrieves the features from SSL object
-        /// Retrieves features from raw SSL Hello message.
-        /// \param record  whether to store Message to the helloMessage member
-        bool get(const SBuf &, bool record = true);
-        /// Parses a v3 ClientHello message
-        bool parseV3Hello(const unsigned char *hello, size_t helloSize);
-        /// Parses a v23 ClientHello message
-        bool parseV23Hello(const unsigned char *hello, size_t helloSize);
-        /// Parses a v3 ServerHello message.
-        bool parseV3ServerHello(const unsigned char *hello, size_t helloSize);
-        /// Prints to os stream a human readable form of sslFeatures object
-        std::ostream & print(std::ostream &os) const;
-        /// Converts to the internal squid SSL version form the sslVersion
-        int toSquidSSLVersion() const;
-        /// Configure the SSL object with the SSL features of the sslFeatures object
-        void applyToSSL(SSL *ssl, Ssl::BumpMode bumpMode) const;
-        /// Parses an SSL Message header. It returns the ssl Message size.
-        /// \retval >0 if the hello size is retrieved
-        /// \retval 0 if the contents of the buffer are not enough
-        /// \retval <0 if the contents of buf are not SSLv3 or TLS hello message
-        int parseMsgHead(const SBuf &);
-    public:
-        int sslHelloVersion; ///< The SSL hello message version
-        int sslVersion; ///< The requested/used SSL version
-        int compressMethod; ///< The requested/used compressed  method
-        int helloMsgSize; ///< the hello message size
-        mutable SBuf serverName; ///< The SNI hostname, if any
-        std::string clientRequestedCiphers; ///< The client requested ciphers
-        bool unknownCiphers; ///< True if one or more ciphers are unknown
-        bool doHeartBeats;
-        bool tlsTicketsExtension; ///< whether TLS tickets extension is enabled
-        bool hasTlsTicket; ///< whether a TLS ticket is included
-        bool tlsStatusRequest; ///< whether the TLS status request extension is set
-        SBuf tlsAppLayerProtoNeg; ///< The value of the TLS application layer protocol extension if it is enabled
-        /// The client random number
-        unsigned char client_random[SSL3_RANDOM_SIZE];
-        SBuf sessionId;
-        std::list<int> extensions;
-        SBuf helloMessage;
-        bool initialized_;
-    };
     explicit Bio(const int anFd);
     virtual ~Bio();
 
@@ -107,15 +61,16 @@ public:
     /// Reads data from socket and record them to a buffer
     int readAndBuffer(BIO *table, const char *description);
 
-    /// Return the TLS features requested by TLS client
-    const Bio::sslFeatures &receivedHelloFeatures() const {return receivedHelloFeatures_;}
+    /// Return the TLS Details requested/advirised by TLS client or server.
+    const Security::TlsDetails::Pointer &receivedHelloDetails() const {return parser_.details;}
 
     const SBuf &rBufData() {return rbuf;}
 protected:
     const int fd_; ///< the SSL socket we are reading and writing
     SBuf rbuf;  ///< Used to buffer input data.
     /// The features retrieved from client or Server TLS hello message
-    Bio::sslFeatures receivedHelloFeatures_;
+    //Security::TlsDetails::Pointer receivedHelloDetails_;
+    Security::HandshakeParser parser_; ///< The SSL messages parser.
 };
 
 /// BIO node to handle socket IO for squid client side
@@ -124,9 +79,7 @@ protected:
 class ClientBio: public Bio
 {
 public:
-    /// The ssl hello message read states
-    typedef enum {atHelloNone = 0, atHelloStarted, atHelloReceived} HelloReadState;
-    explicit ClientBio(const int anFd): Bio(anFd), holdRead_(false), holdWrite_(false), helloState(atHelloNone), helloSize(0), wrongProtocol(false) {}
+    explicit ClientBio(const int anFd): Bio(anFd), holdRead_(false), holdWrite_(false), helloSize(0)/*, wrongProtocol(false)*/ {}
 
     /// The ClientBio version of the Ssl::Bio::stateChanged method
     /// When the client hello message retrieved, fill the
@@ -139,19 +92,18 @@ public:
     /// to socket and sets the "read retry" flag of the BIO to true
     virtual int read(char *buf, int size, BIO *table);
     /// Return true if the client hello message received and analized
-    bool gotHello() { return (helloState == atHelloReceived); }
+    bool gotHello() const { return (parser_.parseDone && !parser_.parseError); }
     /// Prevents or allow writting on socket.
     void hold(bool h) {holdRead_ = holdWrite_ = h;}
     /// True if client does not looks like an SSL client
-    bool noSslClient() {return wrongProtocol;}
+    bool noSslClient() {return parser_.parseError;}
 private:
     /// True if the SSL state corresponds to a hello message
     bool isClientHello(int state);
     bool holdRead_; ///< The read hold state of the bio.
     bool holdWrite_;  ///< The write hold state of the bio.
-    HelloReadState helloState; ///< The SSL hello read state
     int helloSize; ///< The SSL hello message sent by client size
-    bool wrongProtocol; ///< true if client SSL hello parsing failed
+    //bool wrongProtocol; ///< true if client SSL hello parsing failed
 };
 
 /// BIO node to handle socket IO for squid server side
@@ -187,11 +139,7 @@ public:
     /// Flushes any buffered data
     virtual void flush(BIO *table);
     /// Sets the random number to use in client SSL HELLO message
-    void setClientFeatures(const sslFeatures &features);
-
-    /// Parses server Hello message if it is recorded and extracts
-    /// server-supported features.
-    void extractHelloFeatures();
+    void setClientFeatures(Security::TlsDetails::Pointer const &details, SBuf const &hello);
 
     bool resumingSession();
 
@@ -226,7 +174,10 @@ public:
     const Ssl::X509_STACK_Pointer &serverCertificatesIfAny() { return parser_.serverCertificates; } /* XXX: may be nil */
 
 private:
-    sslFeatures clientFeatures; ///< SSL client features extracted from ClientHello message or SSL object
+    /// SSL client features extracted from ClientHello message or SSL object
+    Security::TlsDetails::Pointer clientTlsDetails;
+    /// TLS client hello message, used to adapt our tls Hello message to the server
+    SBuf clientHelloMessage;
     SBuf helloMsg; ///< Used to buffer output data.
     mb_size_t  helloMsgSize;
     bool helloBuild; ///< True if the client hello message sent to the server
@@ -239,16 +190,12 @@ private:
 
     ///< The size of data stored in rbuf which passed to the openSSL
     size_t rbufConsumePos;
-    Security::HandshakeParser parser_; ///< The SSL messages parser.
 };
 
-inline
-std::ostream &operator <<(std::ostream &os, Ssl::Bio::sslFeatures const &f)
-{
-    return f.print(os);
-}
-
 } // namespace Ssl
 
+void
+applyTlsDetailsToSSL(SSL *ssl, Security::TlsDetails::Pointer const &details, Ssl::BumpMode bumpMode);
+
 #endif /* SQUID_SSL_BIO_H */