On 12/13/2015 11:16 AM, Amos Jeffries wrote:
On 11/12/2015 6:36 a.m., Christos Tsantilas wrote:
This patch adds the following formatting codes:

   %ssl::>negotiated_version
   The SSL version of the client-to-Squid connection.

   %ssl::<negotiated_version
   The SSL version of the Squid-to-server connection.

   %ssl::>received_hello_version
   The SSL version of the Hello message received from SSL client

   %ssl::<received_hello_version
   The SSL version of the Hello message received from SSL server.

   %ssl::>received_supported_version
   The maximum SSL version supported by the the SSL client.

   %ssl::<received_supported_version
   The maximum SSL version supported by the the SSL server.

   %ssl::>cipher
   The negotiated cipher of the client-to-Squid connection.

   %ssl::<cipher
   The negotiated cipher of the Squid-to-server connection.

These are useful for statistics collection, security reviews, and
reviews prior to adjusting the list of the allowed SSL protocols and
ciphers.

This is a Measurement Factory project


Looks good. But there are some minor issues to resolve.



src/ssl/support.h:
* s/SSL/TLS/ in the new documentation

fixed in my new patch


* Can you please put this new class in libsecurity and the Security::
namespace?

The Security::NegotiationHistory class moved to security/NegotiationHistory.[cc,h] files


in src/ssl/support.cc:
* since printTlsVersion() is used in format codes that sometimes used
for HTTP headers, please make it output the IETF protocol-version
format. ie. protocol/major.minor

OK.



in src/comm/Connection.cc:
* tlsHistory is only sometime protected by USE_OPENSSL.
  - it should be defined in the .h as a void* for non-OpenSSL builds.
Which can avoid many of the wrappers.
- if you moved the class definitino to libsecurity it should always be
available anyway, so not need the wrappers in this code.

The "#ifdef USE_OPENSSL/#endif" removed from Connection.[cc,h] code.
Maybe we need it around the new methods and members, to same some bytes per connection object, if the squid did not compiled with SSL/TLS support


in src/comm/Connection.h:
* replace "/** SSL conenction details*/"
   with "/// TLS connection details"

fixed



in src/SquidConfig.h
* logTlsServerHelloDetails should be bool type.

Fixed. It is the first bool type in Config classes.



in src/cf.data.pre:
* s/SSL/TLS/ Squid-4 no longer performs SSL.
ok
However I must note that still accepts SSL hello messages.


* s/client-to-Squid/client/

* s/Squid-to-server/last server or peer/

OK on this.




* please make the codes shorter. We still have to work within a
relatively short line length for the entire log format.

Well, I did not fix it in the new patch. We need the other developers opinion.

The short names does not improve the speed or something in operation.
However they confuses system admins when trying to read a logformat strings.
I am suggesting to keep the long names for logformat codes as is.



There also seems to be a lot of confusion over the meaning of "SSL
version" in the documentation.
  - I suggest:

   %ssl::<v - Negotiated TLS version on the client connection.

   %ssl::<cv - ClientHello message version received on the client connection.

   %ssl::<sv - ServerHello message version sent on the client connection.


   %ssl::>v - Negotiated TLS version on the last server or peer connection.

   %ssl::>cv - ClientHello message version sent on the last server or
peer connection.

   %ssl::>sv - ServerHello message version received on the last server or
peer connection.


in src/format/ByteCode.h:
* s/LFT_SSL_/LFT_TLS_/ on the new codes.

This is fixed in new patch.


The codes so far have a bit of a naming convention:

  LFT_(TLS|SSL)_(CLIENT|SERVER)_(LOCAL_)_FOO

- CLIENT for client connection
- SERVER for server connection
- LOCAL_ for Squid details on the connection
- FOO for the FOO detail name

I disagree in this too..
- The client sends a Hello message

- The hello message version may be is SSLv2, or SSLv3 or TLS1.x. Or better this is the version of the SSL/TLS Record protocol includes the hello handshake message.

- The client Hello message includes the (maximum) supported TLS version by the client, which is not always the same as Hello (SSL/TLS record) message version.

- The server sends back hello server handshake message in an SSL/TLS record of version X, and which includes the supported TLS version Y by server.

The CLIENT, SERVER and LOCAL names are not enough to hold the above.
I believe that the current scheme is  better.


Then sorted by tag, so related things are grouped together.

If you could follow that it would help readability.

Which would mean:
   LFT_TLS_CLIENT_HELLO_VERSION       (%ssl:<cv)
   LFT_TLS_CLIENT_LOCAL_HELLO_VERSION (%ssl:<sv)
   LFT_TLS_CLIENT_NEGOTIATED_VERSION  (%ssl:<v)

   LFT_TLS_SERVER_HELLO_VERSION       (%ssl:>sv)
   LFT_TLS_SERVER_LOCAL_HELLO_VERSION (%ssl:>cv)
   LFT_TLS_SERVER_NEGOTIATED_VERSION  (%ssl:>v)



in src/ssl/bio.cc:
* s/SSL/TLS/ - at least in the new documentation.

ok


* since you are changing the initialization list for
Ssl::Bio::sslFeatures::sslFeatures(), please make the entries one member
per line for easier reading.

ok



* Ssl::Bio::sslFeatures::parseMsgHead()
  - why is the SSL version parse being changed?
  - what was wrong with reporting the actual on-wire value given?

-        sslVersion = (head[3] << 8) | head[4];
+        sslHelloVersion = 0x0002;

This is code refers to a Hello message sent into an SSLv23 SSL record.
The maximum supported version (in head[3] and head[4] bytes) will be extracted latter in parseV23message.



in src/ssl/bio.h:
* s/SSL/TLS/ - at least in the new documentation.

ok

* receivedHelloFeatures_ says "The futures received".
  - typo of "features" or are you actually documenting C++ futures
functionality usage?

..the future which will never come....



Log TLS Cryptography Parameters

This patch adds the following formatting codes:
  %ssl::>negotiated_version  The TLS version of the client-to-Squid connection.
  %ssl::<negotiated_version  The TLS version of the Squid-to-server connection.
  %ssl::>received_hello_version The TLS version of the Hello message received
                                from TLS client
  %ssl::<received_hello_version The TLS version of the Hello message received
                                from TLS server.
  %ssl::>received_supported_version The maximum TLS version supported by the
                                    the TLS client.
  %ssl::<received_supported_version The maximum TLS version supported by the
                                    the TLS server.
  %ssl::>cipher   The negotiated cipher of the client-to-Squid connection.
  %ssl::<cipher   The negotiated cipher of the Squid-to-server connection.

These are useful for statistics collection, security reviews, and reviews
prior to adjusting the list of the allowed TLS protocols and ciphers.

This is a Measurement Factory project

=== modified file 'src/SquidConfig.h'
--- src/SquidConfig.h	2015-12-02 19:45:15 +0000
+++ src/SquidConfig.h	2015-12-13 18:53:12 +0000
@@ -318,40 +318,43 @@
         int emailErrData;
         int httpd_suppress_version_string;
         int global_internal_static;
         int collapsed_forwarding;
 
 #if FOLLOW_X_FORWARDED_FOR
         int acl_uses_indirect_client;
         int delay_pool_uses_indirect_client;
         int log_uses_indirect_client;
 #if LINUX_NETFILTER
         int tproxy_uses_indirect_client;
 #endif
 #endif /* FOLLOW_X_FORWARDED_FOR */
 
         int WIN32_IpAddrChangeMonitor;
         int memory_cache_first;
         int memory_cache_disk;
         int hostStrictVerify;
         int client_dst_passthru;
         int dns_mdns;
+#if USE_OPENSSL
+        bool logTlsServerHelloDetails;
+#endif
     } onoff;
 
     int pipeline_max_prefetch;
 
     int forward_max_tries;
     int connect_retries;
 
     class ACL *aclList;
 
     struct {
         acl_access *http;
         acl_access *adapted_http;
         acl_access *icp;
         acl_access *miss;
         acl_access *NeverDirect;
         acl_access *AlwaysDirect;
         acl_access *ASlists;
         acl_access *noCache;
         acl_access *sendHit;
         acl_access *storeMiss;

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2015-12-02 19:45:15 +0000
+++ src/cf.data.pre	2015-12-13 18:58:49 +0000
@@ -4182,40 +4182,64 @@
 				SSL certificate or a dash ('-') if Squid has
 				received an invalid/malformed certificate or
 				no certificate at all. Consider encoding the
 				logged value because Subject often has spaces.
 
 		ssl::>cert_issuer
 				The Issuer field of the received client
 				SSL certificate or a dash ('-') if Squid has
 				received an invalid/malformed certificate or
 				no certificate at all. Consider encoding the
 				logged value because Issuer often has spaces.
 
 		ssl::<cert_errors
 				The list of certificate validation errors
 				detected by Squid (including OpenSSL and
 				certificate validation helper components). The
 				errors are listed in the discovery order. By
 				default, the error codes are separated by ':'.
 				Accepts an optional separator argument.
 
+		%ssl::>negotiated_version The negotiated TLS version of the
+				client connection.
+
+		%ssl::<negotiated_version The negotiated TLS version of the
+				last server or peer connection.
+
+		%ssl::>received_hello_version The TLS version of the Hello
+				message received from TLS client.
+
+		%ssl::<received_hello_version The TLS version of the Hello
+				message received from TLS server.
+
+		%ssl::>received_supported_version The maximum TLS version
+				supported by the TLS client.
+
+		%ssl::<received_supported_version The maximum TLS version
+				supported by the TLS server.
+
+		%ssl::>negotiated_cipher The negotiated cipher of the
+				client connection.
+
+		%ssl::<negotiated_cipher The negotiated cipher of the
+				last server or peer connection.
+
 	If ICAP is enabled, the following code becomes available (as
 	well as ICAP log codes documented with the icap_log option):
 
 		icap::tt        Total ICAP processing time for the HTTP
 				transaction. The timer ticks when ICAP
 				ACLs are checked and when ICAP
 				transaction is in progress.
 
 	If adaptation is enabled the following codes become available:
 
 		adapt::<last_h	The header of the last ICAP response or
 				meta-information from the last eCAP
 				transaction related to the HTTP transaction.
 				Like <h, accepts an optional header name
 				argument.
 
 		adapt::sum_trs Summed adaptation transaction response
 				times recorded as a comma-separated list in
 				the order of transaction start time. Each time
 				value is recorded as an integer number,

=== modified file 'src/client_side.cc'
--- src/client_side.cc	2015-12-04 13:35:13 +0000
+++ src/client_side.cc	2015-12-13 19:31:05 +0000
@@ -86,40 +86,41 @@
 #include "helper/Reply.h"
 #include "http.h"
 #include "http/one/RequestParser.h"
 #include "http/one/TeChunkedParser.h"
 #include "HttpHdrContRange.h"
 #include "HttpHeaderTools.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 #include "ident/Config.h"
 #include "ident/Ident.h"
 #include "internal.h"
 #include "ipc/FdNotes.h"
 #include "ipc/StartListening.h"
 #include "log/access_log.h"
 #include "MemBuf.h"
 #include "MemObject.h"
 #include "mime_header.h"
 #include "parser/Tokenizer.h"
 #include "profiler/Profiler.h"
 #include "rfc1738.h"
+#include "security/NegotiationHistory.h"
 #include "servers/forward.h"
 #include "SquidConfig.h"
 #include "SquidTime.h"
 #include "StatCounters.h"
 #include "StatHist.h"
 #include "Store.h"
 #include "TimeOrTag.h"
 #include "tools.h"
 #include "URL.h"
 
 #if USE_AUTH
 #include "auth/UserRequest.h"
 #endif
 #if USE_DELAY_POOLS
 #include "ClientInfo.h"
 #endif
 #if USE_OPENSSL
 #include "ssl/bio.h"
 #include "ssl/context_storage.h"
 #include "ssl/gadgets.h"
@@ -3391,42 +3392,42 @@
             * Because there are two possible usable cast, if you get an error here, try the other
             * commented line. */
 
             PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
             /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL); */
 
 #else
 
             debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source." );
 
 #endif
             /* Note: This does not automatically fflush the log file.. */
         }
 
         debugs(83, 2, "clientNegotiateSSL: New session " <<
                SSL_get_session(ssl) << " on FD " << fd << " (" <<
                fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port <<
                ")");
     }
 
-    debugs(83, 3, "clientNegotiateSSL: FD " << fd << " negotiated cipher " <<
-           SSL_get_cipher(ssl));
+    // Connection established. Retrieve TLS connection parameters for logging.
+    conn->clientConnection->tlsNegotiations()->fillWith(ssl);
 
     client_cert = SSL_get_peer_certificate(ssl);
 
     if (client_cert != NULL) {
         debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
                " client certificate: subject: " <<
                X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
 
         debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
                " client certificate: issuer: " <<
                X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
 
         X509_free(client_cert);
     } else {
         debugs(83, 5, "clientNegotiateSSL: FD " << fd <<
                " has no certificate.");
     }
 
 #if defined(TLSEXT_NAMETYPE_host_name)
     if (!conn->serverBump()) {
@@ -3874,41 +3875,41 @@
 
     int ret = 0;
     if ((ret = Squid_SSL_accept(conn, clientPeekAndSpliceSSL)) < 0)
         debugs(83, 2, "SSL_accept failed.");
 
     BIO *b = SSL_get_rbio(ssl);
     assert(b);
     Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
     if (ret < 0) {
         const err_type err = bio->noSslClient() ? ERR_PROTOCOL_UNKNOWN : ERR_SECURE_ACCEPT_FAIL;
         if (!conn->spliceOnError(err))
             conn->clientConnection->close();
         return;
     }
 
     if (bio->rBufData().contentSize() > 0)
         conn->receivedFirstByte();
 
     if (bio->gotHello()) {
         if (conn->serverBump()) {
-            Ssl::Bio::sslFeatures const &features = bio->getFeatures();
+            Ssl::Bio::sslFeatures const &features = bio->receivedHelloFeatures();
             if (!features.serverName.isEmpty()) {
                 conn->serverBump()->clientSni = features.serverName;
                 conn->resetSslCommonName(features.serverName.c_str());
             }
         }
 
         debugs(83, 5, "I got hello. Start forwarding the request!!! ");
         Comm::SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
         Comm::SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
         conn->startPeekAndSpliceDone();
         return;
     }
 }
 
 void ConnStateData::startPeekAndSplice()
 {
     // will call httpsPeeked() with certificate and connection, eventually
     auto unConfiguredCTX = Ssl::createSSLContext(port->signingCert, port->signPkey, *port);
     fd_table[clientConnection->fd].dynamicSslContext = unConfiguredCTX;
 
@@ -3951,40 +3952,44 @@
         bumpAction = (Ssl::BumpMode)answer.kind;
     } else
         bumpAction = Ssl::bumpSplice;
 
     connState->serverBump()->act.step2 = bumpAction;
     connState->sslBumpMode = bumpAction;
 
     if (bumpAction == Ssl::bumpTerminate) {
         connState->clientConnection->close();
     } else if (bumpAction != Ssl::bumpSplice) {
         connState->startPeekAndSpliceDone();
     } else
         connState->splice();
 }
 
 void
 ConnStateData::splice()
 {
     //Normally we can splice here, because we just got client hello message
     auto ssl = fd_table[clientConnection->fd].ssl;
+
+    //retrieve received TLS client information
+    clientConnection->tlsNegotiations()->fillWith(ssl);
+
     BIO *b = SSL_get_rbio(ssl);
     Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
     MemBuf const &rbuf = bio->rBufData();
     debugs(83,5, "Bio for  " << clientConnection << " read " << rbuf.contentSize() << " helo bytes");
     // Do splice:
     fd_table[clientConnection->fd].read_method = &default_read_method;
     fd_table[clientConnection->fd].write_method = &default_write_method;
 
     if (transparent()) {
         // set the current protocol to something sensible (was "HTTPS" for the bumping process)
         // we are sending a faked-up HTTP/1.1 message wrapper, so go with that.
         transferProtocol = Http::ProtocolVersion();
         // XXX: copy from MemBuf reallocates, not a regression since old code did too
         SBuf temp;
         temp.append(rbuf.content(), rbuf.contentSize());
         fakeAConnectRequest("intercepted TLS spliced", temp);
     } else {
         // XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
 
         // reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)

=== modified file 'src/comm/Connection.cc'
--- src/comm/Connection.cc	2015-08-24 14:20:07 +0000
+++ src/comm/Connection.cc	2015-12-13 19:31:33 +0000
@@ -1,67 +1,71 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #include "squid.h"
 #include "CachePeer.h"
 #include "cbdata.h"
 #include "comm.h"
 #include "comm/Connection.h"
 #include "fde.h"
 #include "neighbors.h"
 #include "SquidConfig.h"
 #include "SquidTime.h"
+#include "security/NegotiationHistory.h"
 
 class CachePeer;
 bool
 Comm::IsConnOpen(const Comm::ConnectionPointer &conn)
 {
     return conn != NULL && conn->isOpen();
 }
 
 Comm::Connection::Connection() :
     peerType(HIER_NONE),
     fd(-1),
     tos(0),
     nfmark(0),
     flags(COMM_NONBLOCKING),
     peer_(nullptr),
-    startTime_(squid_curtime)
+    startTime_(squid_curtime),
+    tlsHistory(nullptr)
 {
     *rfc931 = 0; // quick init the head. the rest does not matter.
 }
 
 static int64_t lost_conn = 0;
 Comm::Connection::~Connection()
 {
     if (fd >= 0) {
         debugs(5, 4, "BUG #3329: Orphan Comm::Connection: " << *this);
         debugs(5, 4, "NOTE: " << ++lost_conn << " Orphans since last started.");
         close();
     }
 
     cbdataReferenceDone(peer_);
+
+    delete tlsHistory;
 }
 
 Comm::ConnectionPointer
 Comm::Connection::copyDetails() const
 {
     ConnectionPointer c = new Comm::Connection;
 
     c->setAddrs(local, remote);
     c->peerType = peerType;
     c->tos = tos;
     c->nfmark = nfmark;
     c->flags = flags;
     c->startTime_ = startTime_;
 
     // ensure FD is not open in the new copy.
     c->fd = -1;
 
     // ensure we have a cbdata reference to peer_ not a straight ptr copy.
     c->peer_ = cbdataReference(getPeer());
 
@@ -102,20 +106,28 @@
     /* set to self. nothing to do. */
     if (getPeer() == p)
         return;
 
     cbdataReferenceDone(peer_);
     if (p) {
         peer_ = cbdataReference(p);
     }
 }
 
 time_t
 Comm::Connection::timeLeft(const time_t idleTimeout) const
 {
     if (!Config.Timeout.pconnLifetime)
         return idleTimeout;
 
     const time_t lifeTimeLeft = lifeTime() < Config.Timeout.pconnLifetime ? Config.Timeout.pconnLifetime - lifeTime() : 1;
     return min(lifeTimeLeft, idleTimeout);
 }
 
+Security::NegotiationHistory *
+Comm::Connection::tlsNegotiations()
+{
+    if (!tlsHistory)
+        tlsHistory = new Security::NegotiationHistory;
+    return tlsHistory;
+}
+

=== modified file 'src/comm/Connection.h'
--- src/comm/Connection.h	2015-08-31 06:17:22 +0000
+++ src/comm/Connection.h	2015-12-13 19:32:13 +0000
@@ -11,40 +11,45 @@
 #ifndef _SQUIDCONNECTIONDETAIL_H_
 #define _SQUIDCONNECTIONDETAIL_H_
 
 #include "comm/forward.h"
 #include "defines.h"
 #if USE_SQUID_EUI
 #include "eui/Eui48.h"
 #include "eui/Eui64.h"
 #endif
 #include "hier_code.h"
 #include "ip/Address.h"
 #include "ip/forward.h"
 #include "mem/forward.h"
 #include "SquidTime.h"
 
 #include <iosfwd>
 #include <ostream>
 
 class CachePeer;
 
+namespace Security
+{
+class NegotiationHistory;
+};
+
 namespace Comm
 {
 
 /* TODO: make these a struct of boolean flags members in the connection instead of a bitmap.
  * we can't do that until all non-comm code uses Commm::Connection objects to create FD
  * currently there is code still using comm_open() and comm_openex() synchronously!!
  */
 #define COMM_UNSET              0x00
 #define COMM_NONBLOCKING        0x01  // default flag.
 #define COMM_NOCLOEXEC          0x02
 #define COMM_REUSEADDR          0x04  // shared FD may be both accept()ing and read()ing
 #define COMM_DOBIND             0x08  // requires a bind()
 #define COMM_TRANSPARENT        0x10  // arrived via TPROXY
 #define COMM_INTERCEPTION       0x20  // arrived via NAT
 
 /**
  * Store data about the physical and logical attributes of a connection.
  *
  * Some link state can be infered from the data, however this is not an
  * object for state data. But a semantic equivalent for FD with easily
@@ -90,40 +95,44 @@
      * The caller is responsible for all CBDATA operations regarding the
      * used of the pointer returned.
      */
     CachePeer * getPeer() const;
 
     /** alter the stored CachePeer pointer.
      * Perform appropriate CBDATA operations for locking the CachePeer pointer
      */
     void setPeer(CachePeer * p);
 
     /** The time the connection started */
     time_t startTime() const {return startTime_;}
 
     /** The connection lifetime */
     time_t lifeTime() const {return squid_curtime - startTime_;}
 
     /** The time left for this connection*/
     time_t timeLeft(const time_t idleTimeout) const;
 
     void noteStart() {startTime_ = squid_curtime;}
+
+    Security::NegotiationHistory *tlsNegotiations();
+    const Security::NegotiationHistory *hasTlsNegotiations() const {return tlsHistory;}
+
 private:
     /** These objects may not be exactly duplicated. Use copyDetails() instead. */
     Connection(const Connection &c);
 
     /** These objects may not be exactly duplicated. Use copyDetails() instead. */
     Connection & operator =(const Connection &c);
 
 public:
     /** Address/Port for the Squid end of a TCP link. */
     Ip::Address local;
 
     /** Address for the Remote end of a TCP link. */
     Ip::Address remote;
 
     /** Hierarchy code for this connection link */
     hier_code peerType;
 
     /** Socket used by this connection. Negative if not open. */
     int fd;
 
@@ -132,40 +141,43 @@
 
     /** Netfilter MARK values currently sent on this connection */
     nfmark_t nfmark;
 
     /** COMM flags set on this connection */
     int flags;
 
     char rfc931[USER_IDENT_SZ];
 
 #if USE_SQUID_EUI
     Eui::Eui48 remoteEui48;
     Eui::Eui64 remoteEui64;
 #endif
 
 private:
     /** cache_peer data object (if any) */
     CachePeer *peer_;
 
     /** The time the connection object was created */
     time_t startTime_;
+
+    /** TLS connection details*/
+    Security::NegotiationHistory *tlsHistory;
 };
 
 }; // namespace Comm
 
 // NP: Order and namespace here is very important.
 //     * The second define inlines the first.
 //     * Stream inheritance overloading is searched in the global scope first.
 
 inline std::ostream &
 operator << (std::ostream &os, const Comm::Connection &conn)
 {
     os << "local=" << conn.local << " remote=" << conn.remote;
     if (conn.fd >= 0)
         os << " FD " << conn.fd;
     if (conn.flags != COMM_UNSET)
         os << " flags=" << conn.flags;
 #if USE_IDENT
     if (*conn.rfc931)
         os << " IDENT::" << conn.rfc931;
 #endif

=== modified file 'src/format/ByteCode.h'
--- src/format/ByteCode.h	2015-10-09 09:23:22 +0000
+++ src/format/ByteCode.h	2015-12-13 19:04:11 +0000
@@ -201,40 +201,48 @@
 
     LFT_ICAP_REP_HEADER,
     LFT_ICAP_REP_HEADER_ELEM,
     LFT_ICAP_REP_ALL_HEADERS,
 
     LFT_ICAP_TR_RESPONSE_TIME,
     LFT_ICAP_IO_TIME,
     LFT_ICAP_OUTCOME,
     LFT_ICAP_STATUS_CODE,
 #endif
     LFT_CREDENTIALS,
 
 #if USE_OPENSSL
     LFT_SSL_BUMP_MODE,
     LFT_SSL_USER_CERT_SUBJECT,
     LFT_SSL_USER_CERT_ISSUER,
     LFT_SSL_CLIENT_SNI,
     LFT_SSL_SERVER_CERT_SUBJECT,
     LFT_SSL_SERVER_CERT_ISSUER,
     LFT_SSL_SERVER_CERT_ERRORS,
+    LFT_TLS_CLIENT_NEGOTIATED_VERSION,
+    LFT_TLS_SERVER_NEGOTIATED_VERSION,
+    LFT_TLS_CLIENT_NEGOTIATED_CIPHER,
+    LFT_TLS_SERVER_NEGOTIATED_CIPHER,
+    LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION,
+    LFT_TLS_SERVER_RECEIVED_HELLO_VERSION,
+    LFT_TLS_CLIENT_SUPPORTED_VERSION,
+    LFT_TLS_SERVER_SUPPORTED_VERSION,
 #endif
 
     LFT_NOTE,
     LFT_PERCENT,            /* special string cases for escaped chars */
 
     // TODO assign better bytecode names and Token strings for these
 #if USE_OPENSSL
     LFT_EXT_ACL_USER_CERT_RAW,
     LFT_EXT_ACL_USER_CERTCHAIN_RAW,
     LFT_EXT_ACL_USER_CERT,
     LFT_EXT_ACL_USER_CA_CERT,
 #endif
     LFT_EXT_ACL_CLIENT_EUI48,
     LFT_EXT_ACL_CLIENT_EUI64,
     LFT_EXT_ACL_NAME,
     LFT_EXT_ACL_DATA
 
 } ByteCode_t;
 
 /// Quoting style for a format output.

=== modified file 'src/format/Format.cc'
--- src/format/Format.cc	2015-10-11 18:12:00 +0000
+++ src/format/Format.cc	2015-12-13 19:05:27 +0000
@@ -7,40 +7,41 @@
  */
 
 #include "squid.h"
 #include "AccessLogEntry.h"
 #include "client_side.h"
 #include "comm/Connection.h"
 #include "err_detail_type.h"
 #include "errorpage.h"
 #include "fde.h"
 #include "format/Format.h"
 #include "format/Quoting.h"
 #include "format/Token.h"
 #include "fqdncache.h"
 #include "HttpRequest.h"
 #include "MemBuf.h"
 #include "rfc1738.h"
 #include "SquidTime.h"
 #include "Store.h"
 #include "tools.h"
 #include "URL.h"
+#include "security/NegotiationHistory.h"
 #if USE_OPENSSL
 #include "ssl/ErrorDetail.h"
 #include "ssl/ServerBump.h"
 #endif
 
 /// Convert a string to NULL pointer if it is ""
 #define strOrNull(s) ((s)==NULL||(s)[0]=='\0'?NULL:(s))
 
 Format::Format::Format(const char *n) :
     format(NULL),
     next(NULL)
 {
     name = xstrdup(n);
 }
 
 Format::Format::~Format()
 {
     // erase the list without consuming stack space
     while (next) {
         // unlink the next entry for deletion
@@ -1242,40 +1243,80 @@
                 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
                     const char *separator = fmt->data.string ? fmt->data.string : ":";
                     for (Ssl::CertErrors *sslError = srvBump->sslErrors; sslError != NULL;  sslError = sslError->next) {
                         if (sb.size())
                             sb.append(separator);
                         if (const char *errorName = Ssl::GetErrorName(sslError->element.code))
                             sb.append(errorName);
                         else
                             sb.append(sslErrorName(sslError->element.code, tmp, sizeof(tmp)));
                     }
                     if (sb.size())
                         out = sb.termedBuf();
                 }
             }
             break;
 
         case LFT_SSL_SERVER_CERT_ISSUER:
         case LFT_SSL_SERVER_CERT_SUBJECT:
             // Not implemented
             break;
+
+        case LFT_TLS_CLIENT_NEGOTIATED_VERSION:
+            if (al->tcpClient != nullptr && al->tcpClient->hasTlsNegotiations())
+                out = al->tcpClient->hasTlsNegotiations()->negotiatedVersion();
+            break;
+
+        case LFT_TLS_SERVER_NEGOTIATED_VERSION:
+            if (al->hier.tcpServer != nullptr && al->hier.tcpServer->hasTlsNegotiations())
+                out = al->hier.tcpServer->hasTlsNegotiations()->negotiatedVersion();
+            break;
+
+        case LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION:
+            if (al->tcpClient != nullptr && al->tcpClient->hasTlsNegotiations())
+                out = al->tcpClient->hasTlsNegotiations()->helloVersion();
+            break;
+
+        case LFT_TLS_SERVER_RECEIVED_HELLO_VERSION:
+            if (al->hier.tcpServer != nullptr && al->hier.tcpServer->hasTlsNegotiations())
+                out = al->hier.tcpServer->hasTlsNegotiations()->helloVersion();
+            break;
+
+        case LFT_TLS_CLIENT_SUPPORTED_VERSION:
+            if (al->tcpClient != nullptr && al->tcpClient->hasTlsNegotiations())
+                out = al->tcpClient->hasTlsNegotiations()->supportedVersion();
+            break;
+
+        case LFT_TLS_SERVER_SUPPORTED_VERSION:
+            if (al->hier.tcpServer != nullptr && al->hier.tcpServer->hasTlsNegotiations())
+                out = al->hier.tcpServer->hasTlsNegotiations()->supportedVersion();
+            break;
+
+        case LFT_TLS_CLIENT_NEGOTIATED_CIPHER:
+            if (al->tcpClient != nullptr && al->tcpClient->hasTlsNegotiations())
+                out = al->tcpClient->hasTlsNegotiations()->cipherName();
+            break;
+
+        case LFT_TLS_SERVER_NEGOTIATED_CIPHER:
+            if (al->hier.tcpServer != nullptr && al->hier.tcpServer->hasTlsNegotiations())
+                out = al->hier.tcpServer->hasTlsNegotiations()->cipherName();
+            break;
 #endif
 
         case LFT_REQUEST_URLGROUP_OLD_2X:
             assert(LFT_REQUEST_URLGROUP_OLD_2X == 0); // should never happen.
 
         case LFT_NOTE:
             tmp[0] = fmt->data.header.separator;
             tmp[1] = '\0';
             if (fmt->data.header.header && *fmt->data.header.header) {
                 const char *separator = tmp;
 #if USE_ADAPTATION
                 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
                 if (ah != NULL && ah->metaHeaders != NULL) {
                     if (const char *meta = ah->metaHeaders->find(fmt->data.header.header, separator))
                         sb.append(meta);
                 }
 #endif
                 if (al->notes != NULL) {
                     if (const char *note = al->notes->find(fmt->data.header.header, separator)) {
                         if (sb.size())

=== modified file 'src/format/Token.cc'
--- src/format/Token.cc	2015-10-09 09:23:22 +0000
+++ src/format/Token.cc	2015-12-13 19:21:32 +0000
@@ -202,40 +202,48 @@
 
     TokenTableEntry("tr",  LFT_ICAP_TR_RESPONSE_TIME),
     TokenTableEntry("tio", LFT_ICAP_IO_TIME),
     TokenTableEntry("to",  LFT_ICAP_OUTCOME),
     TokenTableEntry("Hs",  LFT_ICAP_STATUS_CODE),
 
     TokenTableEntry(NULL, LFT_NONE)           /* this must be last */
 };
 #endif
 
 #if USE_OPENSSL
 // SSL (ssl::) tokens
 static TokenTableEntry TokenTableSsl[] = {
     TokenTableEntry("bump_mode", LFT_SSL_BUMP_MODE),
     TokenTableEntry(">cert_subject", LFT_SSL_USER_CERT_SUBJECT),
     TokenTableEntry(">cert_issuer", LFT_SSL_USER_CERT_ISSUER),
     TokenTableEntry(">sni", LFT_SSL_CLIENT_SNI),
     /*TokenTableEntry("<cert_subject", LFT_SSL_SERVER_CERT_SUBJECT), */
     /*TokenTableEntry("<cert_issuer", LFT_SSL_SERVER_CERT_ISSUER), */
     TokenTableEntry("<cert_errors", LFT_SSL_SERVER_CERT_ERRORS),
+    TokenTableEntry(">negotiated_version", LFT_TLS_CLIENT_NEGOTIATED_VERSION),
+    TokenTableEntry("<negotiated_version", LFT_TLS_SERVER_NEGOTIATED_VERSION),
+    TokenTableEntry(">negotiated_cipher", LFT_TLS_CLIENT_NEGOTIATED_CIPHER),
+    TokenTableEntry("<negotiated_cipher", LFT_TLS_SERVER_NEGOTIATED_CIPHER),
+    TokenTableEntry(">received_hello_version", LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION),
+    TokenTableEntry("<received_hello_version", LFT_TLS_SERVER_RECEIVED_HELLO_VERSION),
+    TokenTableEntry(">received_supported_version", LFT_TLS_CLIENT_SUPPORTED_VERSION),
+    TokenTableEntry("<received_supported_version", LFT_TLS_SERVER_SUPPORTED_VERSION),
     TokenTableEntry(NULL, LFT_NONE)
 };
 #endif
 } // namespace Format
 
 /// Register all components custom format tokens
 void
 Format::Token::Init()
 {
     // TODO standard log tokens
 
 #if USE_ADAPTATION
     TheConfig.registerTokens(String("adapt"),::Format::TokenTableAdapt);
 #endif
 #if ICAP_CLIENT
     TheConfig.registerTokens(String("icap"),::Format::TokenTableIcap);
 #endif
 #if USE_OPENSSL
     TheConfig.registerTokens(String("ssl"),::Format::TokenTableSsl);
 #endif
@@ -582,40 +590,48 @@
         debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"oa\" formatting code is deprecated. Use the \"<la\" instead.");
         type = LFT_SERVER_LOCAL_IP;
         break;
 
     case LFT_REQUEST_URLPATH_OLD_31:
         debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"rp\" formatting code is deprecated. Use the \">rp\" instead.");
         type = LFT_CLIENT_REQ_URLPATH;
         break;
 
     case LFT_REQUEST_VERSION_OLD_2X:
         debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \">v\" formatting code is deprecated. Use the \">rv\" instead.");
         type = LFT_REQUEST_VERSION;
         break;
 
 #if !USE_SQUID_EUI
     case LFT_CLIENT_EUI:
         debugs(46, DBG_CRITICAL, "WARNING: The \">eui\" formatting code requires EUI features which are disabled in this Squid.");
         break;
 #endif
 
+#if USE_OPENSSL
+    case LFT_TLS_SERVER_NEGOTIATED_VERSION:
+    case LFT_TLS_SERVER_RECEIVED_HELLO_VERSION:
+    case LFT_TLS_SERVER_SUPPORTED_VERSION:
+        Config.onoff.logTlsServerHelloDetails = true;
+        break;
+#endif
+
     case LFT_REQUEST_URLGROUP_OLD_2X:
         debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"rG\" formatting code is deprecated. Use \"note{urlgroup}\" instead.");
         type = LFT_NOTE;
         data.header.header = xstrdup("urlgroup");
         break;
 
     default:
         break;
     }
 
     return (cur - def);
 }
 
 Format::Token::Token() : type(LFT_NONE),
     label(NULL),
     widthMin(-1),
     widthMax(-1),
     quote(LOG_QUOTE_NONE),
     left(false),
     space(false),

=== modified file 'src/security/Makefile.am'
--- src/security/Makefile.am	2015-12-08 01:48:40 +0000
+++ src/security/Makefile.am	2015-12-13 18:17:10 +0000
@@ -1,24 +1,26 @@
 ## Copyright (C) 1996-2015 The Squid Software Foundation and contributors
 ##
 ## Squid software is distributed under GPLv2+ license and includes
 ## contributions from numerous individuals and organizations.
 ## Please see the COPYING and CONTRIBUTORS files for details.
 ##
 
 include $(top_srcdir)/src/Common.am
 include $(top_srcdir)/src/TestHeaders.am
 
 noinst_LTLIBRARIES = libsecurity.la
 
 libsecurity_la_SOURCES= \
 	Context.h \
 	EncryptorAnswer.cc \
 	EncryptorAnswer.h \
 	forward.h \
 	KeyData.h \
 	LockingPointer.h \
+	NegotiationHistory.cc \
+	NegotiationHistory.h \
 	PeerOptions.cc \
 	PeerOptions.h \
 	ServerOptions.cc \
 	ServerOptions.h \
 	Session.h

=== added file 'src/security/NegotiationHistory.cc'
--- src/security/NegotiationHistory.cc	1970-01-01 00:00:00 +0000
+++ src/security/NegotiationHistory.cc	2015-12-13 18:45:39 +0000
@@ -0,0 +1,73 @@
+#include "squid.h"
+#include "MemBuf.h"
+#include "security/NegotiationHistory.h"
+#include "SquidConfig.h"
+#include "ssl/bio.h"
+#include "ssl/support.h"
+
+const char *
+Security::NegotiationHistory::printTlsVersion(int v) const
+{
+#if USE_OPENSSL
+    switch(v) {
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+    case TLS1_2_VERSION:
+        return "TLS/1.2";
+    case TLS1_1_VERSION:
+        return "TLS/1.1";
+#endif
+    case TLS1_VERSION:
+        return "TLS/1.0";
+    case SSL3_VERSION:
+        return "SSL/3.0";
+    case SSL2_VERSION:
+        return "SSL/2.0";
+    default:
+        return nullptr;
+    }
+#else
+    return nullptr;
+#endif
+}
+
+#if USE_OPENSSL
+void
+Security::NegotiationHistory::fillWith(SSL *ssl)
+{
+    if ((cipher = SSL_get_current_cipher(ssl)) != NULL) {
+        // Set the negotiated version only if the cipher negotiated
+        // else probably the negotiation is not completed and version
+        // is not the final negotiated version
+        version_ = ssl->version;
+    }
+
+    BIO *b = SSL_get_rbio(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();
+    }
+
+    const Ssl::Bio::sslFeatures &features = bio->receivedHelloFeatures();
+    helloVersion_ = features.sslHelloVersion;
+    supportedVersion_ = features.sslVersion;
+
+    debugs(83, 5, "SSL connection info on FD " << bio->fd() <<
+           " SSL version " << version_ <<
+           " negotiated cipher " << cipherName());
+}
+#endif
+
+const char *
+Security::NegotiationHistory::cipherName() const
+{
+#if USE_OPENSSL
+    if (!cipher)
+        return nullptr;
+
+    return SSL_CIPHER_get_name(cipher);
+#else
+    return nullptr;
+#endif
+}

=== added file 'src/security/NegotiationHistory.h'
--- src/security/NegotiationHistory.h	1970-01-01 00:00:00 +0000
+++ src/security/NegotiationHistory.h	2015-12-13 18:38:21 +0000
@@ -0,0 +1,40 @@
+#ifndef SQUID_SRC_SECURITY_NEGOTIATION_HISTORY_H
+#define SQUID_SRC_SECURITY_NEGOTIATION_HISTORY_H
+
+#if USE_OPENSSL
+#if HAVE_OPENSSL_SSL_H
+#include <openssl/ssl.h>
+#endif
+#endif
+
+namespace Security {
+class NegotiationHistory
+{
+public:
+    NegotiationHistory(): helloVersion_(-1), supportedVersion_(-1), version_(-1), cipher(NULL) {}
+#if USE_OPENSSL
+    void fillWith(SSL *); ///< Extract negotiation information from TLS object
+#endif
+    const char *cipherName() const; ///< The name of negotiated cipher
+    /// String representation of TLS negotiated version
+    const char *negotiatedVersion() const {return printTlsVersion(version_);}
+    /// String representation of the received TLS hello message version.
+    const char *helloVersion() const {return printTlsVersion(helloVersion_);}
+    /// String representation of the maximum supported TLS version
+    /// by remote peer
+    const char *supportedVersion() const {return printTlsVersion(supportedVersion_);}
+private:
+    /// String representation of the TLS version 'v'
+    const char *printTlsVersion(int v) const;
+    int helloVersion_; ///< The TLL version of the hello message
+    int supportedVersion_; ///< The maximum supported TLS version
+    int version_; ///< The negotiated TLL version
+#if USE_OPENSSL
+    const SSL_CIPHER *cipher; ///< The negotiated cipher
+#endif
+};
+
+} // namespace Security
+
+#endif /* SQUID_SRC_SECURITY_NEGOTIATION_HISTORY_H */
+

=== modified file 'src/ssl/PeerConnector.cc'
--- src/ssl/PeerConnector.cc	2015-12-07 09:53:57 +0000
+++ src/ssl/PeerConnector.cc	2015-12-13 19:34:44 +0000
@@ -3,40 +3,41 @@
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 /* DEBUG: section 17    Request Forwarding */
 
 #include "squid.h"
 #include "acl/FilledChecklist.h"
 #include "base/AsyncCbdataCalls.h"
 #include "CachePeer.h"
 #include "client_side.h"
 #include "comm/Loops.h"
 #include "errorpage.h"
 #include "fde.h"
 #include "globals.h"
 #include "helper/ResultCode.h"
 #include "HttpRequest.h"
 #include "neighbors.h"
+#include "security/NegotiationHistory.h"
 #include "SquidConfig.h"
 #include "ssl/bio.h"
 #include "ssl/cert_validate_message.h"
 #include "ssl/Config.h"
 #include "ssl/ErrorDetail.h"
 #include "ssl/helper.h"
 #include "ssl/PeerConnector.h"
 #include "ssl/ServerBump.h"
 #include "ssl/support.h"
 
 CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeerConnector);
 CBDATA_NAMESPACED_CLASS_INIT(Ssl, BlindPeerConnector);
 CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeekingPeerConnector);
 
 Ssl::PeerConnector::PeerConnector(const Comm::ConnectionPointer &aServerConn, AsyncCall::Pointer &aCallback, const time_t timeout) :
     AsyncJob("Ssl::PeerConnector"),
     serverConn(aServerConn),
     certErrors(NULL),
     callback(aCallback),
     negotiationTimeout(timeout),
@@ -663,61 +664,61 @@
 {
     SSL *ssl = Ssl::PeerConnector::initializeSsl();
     if (!ssl)
         return NULL;
 
     if (ConnStateData *csd = request->clientConnectionManager.valid()) {
 
         // client connection is required in the case we need to splice
         // or terminate client and server connections
         assert(clientConn != NULL);
         SBuf *hostName = NULL;
         Ssl::ClientBio *cltBio = NULL;
 
         //Enable Status_request tls extension, required to bump some clients
         SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp);
 
         // In server-first bumping mode, clientSsl is NULL.
         if (SSL *clientSsl = fd_table[clientConn->fd].ssl) {
             BIO *b = SSL_get_rbio(clientSsl);
             cltBio = static_cast<Ssl::ClientBio *>(b->ptr);
-            const Ssl::Bio::sslFeatures &features = cltBio->getFeatures();
+            const Ssl::Bio::sslFeatures &features = cltBio->receivedHelloFeatures();
             if (!features.serverName.isEmpty())
                 hostName = new SBuf(features.serverName);
         }
 
         if (!hostName) {
             // While we are peeking at the certificate, we may not know the server
             // name that the client will request (after interception or CONNECT)
             // unless it was the CONNECT request with a user-typed address.
             const bool isConnectRequest = !csd->port->flags.isIntercepted();
             if (!request->flags.sslPeek || isConnectRequest)
                 hostName = new SBuf(request->url.host());
         }
 
         if (hostName)
             SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)hostName);
 
         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->getFeatures();
+            const Ssl::Bio::sslFeatures &features = cltBio->receivedHelloFeatures();
             if (features.sslVersion != -1) {
                 features.applyToSSL(ssl, csd->sslBumpMode);
                 // Should we allow it for all protocols?
                 if (features.sslVersion >= 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->recordInput(true);
                     srvBio->mode(csd->sslBumpMode);
                 }
             }
         } else {
             // Set client SSL options
             SSL_set_options(ssl, ::Security::ProxyOutgoingConfig.parsedOptions);
 
             // Use SNI TLS extension only when we connect directly
             // to the origin server and we know the server host name.
             const char *sniServer = NULL;
             const bool redirected = request->flags.redirected && ::Config.onoff.redir_rewrites_host;
@@ -768,44 +769,50 @@
                 handleServerCertificate();
             }
         }
 
         if (error) {
             // For intercepted connections, set the host name to the server
             // certificate CN. Otherwise, we just hope that CONNECT is using
             // a user-entered address (a host name or a user-entered IP).
             const bool isConnectRequest = !request->clientConnectionManager->port->flags.isIntercepted();
             if (request->flags.sslPeek && !isConnectRequest) {
                 if (X509 *srvX509 = serverBump->serverCert.get()) {
                     if (const char *name = Ssl::CommonHostName(srvX509)) {
                         request->url.host(name);
                         debugs(83, 3, "reset request host: " << name);
                     }
                 }
             }
         }
     }
 
+    // retrieve TLS server information if any
+    serverConnection()->tlsNegotiations()->fillWith(ssl);
     if (!error) {
         serverCertificateVerified();
-        if (splice)
+        if (splice) {
+            //retrieved received TLS client informations
+            SSL *clientSsl = fd_table[clientConn->fd].ssl;
+            clientConn->tlsNegotiations()->fillWith(clientSsl);
             switchToTunnel(request.getRaw(), clientConn, serverConn);
+        }
     }
 }
 
 void
 Ssl::PeekingPeerConnector::noteWantWrite()
 {
     const int fd = serverConnection()->fd;
     SSL *ssl = fd_table[fd].ssl;
     BIO *b = SSL_get_rbio(ssl);
     Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
 
     if ((srvBio->bumpMode() == Ssl::bumpPeek || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) {
         debugs(81, DBG_IMPORTANT, "hold write on SSL connection on FD " << fd);
         checkForPeekAndSplice();
         return;
     }
 
     Ssl::PeerConnector::noteWantWrite();
 }
 

=== modified file 'src/ssl/bio.cc'
--- src/ssl/bio.cc	2015-10-11 05:30:33 +0000
+++ src/ssl/bio.cc	2015-12-13 20:33:15 +0000
@@ -208,63 +208,63 @@
     static std::string buf;
     buf.clear();
     for (int i = 0; i < len; i++ ) {
         char tmp[3];
         snprintf(tmp, sizeof(tmp), "%.2x", bytes[i]);
         buf.append(tmp);
     }
     return buf.c_str();
 }
 
 int
 Ssl::ClientBio::read(char *buf, int size, BIO *table)
 {
     if (helloState < atHelloReceived) {
         int bytes = readAndBuffer(buf, size, table, "TLS client Hello");
         if (bytes <= 0)
             return bytes;
     }
 
     if (helloState == atHelloNone) {
-        helloSize = features.parseMsgHead(rbuf);
+        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;
             return -1;
         }
 
         helloState = atHelloStarted; //Next state
     }
 
     if (helloState == atHelloStarted) {
         const unsigned char *head = (const unsigned char *)rbuf.content();
         const char *s = objToString(head, rbuf.contentSize());
         debugs(83, 7, "SSL Header: " << s);
 
         if (helloSize > rbuf.contentSize()) {
             BIO_set_retry_read(table);
             return -1;
         }
-        features.get(rbuf);
+        receivedHelloFeatures_.get(rbuf);
         helloState = atHelloReceived;
     }
 
     if (holdRead_) {
         debugs(83, 7, "Hold flag is set, retry latter. (Hold " << size << "bytes)");
         BIO_set_retry_read(table);
         return -1;
     }
 
     if (helloState == atHelloReceived) {
         if (rbuf.hasContent()) {
             int bytes = (size <= rbuf.contentSize() ? size : rbuf.contentSize());
             memcpy(buf, rbuf.content(), bytes);
             rbuf.consume(bytes);
             return bytes;
         } else
             return Ssl::Bio::read(buf, size, table);
     }
 
     return -1;
@@ -485,53 +485,59 @@
         }
 
         // Sending hello message complete. Do not send more data for now...
         holdWrite_ = true;
 
         // spoof openSSL that we write what it ask us to write
         return size;
     } else
         return Ssl::Bio::write(buf, size, table);
 }
 
 void
 Ssl::ServerBio::flush(BIO *table)
 {
     if (!helloMsg.isEmpty()) {
         int ret = Ssl::Bio::write(helloMsg.rawContent(), helloMsg.length(), table);
         helloMsg.consume(ret);
     }
 }
 
+void 
+Ssl::ServerBio::extractHelloFeatures()
+{
+    if (!receivedHelloFeatures_.initialized_)
+        receivedHelloFeatures_.get(rbuf, false);
+}
+
 bool
 Ssl::ServerBio::resumingSession()
 {
-    if (!serverFeatures.initialized_)
-        serverFeatures.get(rbuf, false);
+    extractHelloFeatures();
 
-    if (!clientFeatures.sessionId.isEmpty() && !serverFeatures.sessionId.isEmpty())
-        return clientFeatures.sessionId == serverFeatures.sessionId;
+    if (!clientFeatures.sessionId.isEmpty() && !receivedHelloFeatures_.sessionId.isEmpty())
+        return clientFeatures.sessionId == receivedHelloFeatures_.sessionId;
 
     // is this a session resuming attempt using TLS tickets?
     if (clientFeatures.hasTlsTicket &&
-            serverFeatures.tlsTicketsExtension &&
-            serverFeatures.hasCcsOrNst)
+            receivedHelloFeatures_.tlsTicketsExtension &&
+            receivedHelloFeatures_.hasCcsOrNst)
         return true;
 
     return false;
 }
 
 /// initializes BIO table after allocation
 static int
 squid_bio_create(BIO *bi)
 {
     bi->init = 0; // set when we store Bio object and socket fd (BIO_C_SET_FD)
     bi->num = 0;
     bi->ptr = NULL;
     bi->flags = 0;
     return 1;
 }
 
 /// cleans BIO table before deallocation
 static int
 squid_bio_destroy(BIO *table)
 {
@@ -622,41 +628,52 @@
         case BIO_CTRL_WPENDING:
     */
     default:
         return 0;
 
     }
 
     return 0; /* NOTREACHED */
 }
 
 /// wrapper for Bio::stateChanged()
 static void
 squid_ssl_info(const SSL *ssl, int where, int ret)
 {
     if (BIO *table = SSL_get_rbio(ssl)) {
         if (Ssl::Bio *bio = static_cast<Ssl::Bio*>(table->ptr))
             bio->stateChanged(ssl, where, ret);
     }
 }
 
-Ssl::Bio::sslFeatures::sslFeatures(): sslVersion(-1), compressMethod(-1), helloMsgSize(0), unknownCiphers(false), doHeartBeats(true), tlsTicketsExtension(false), hasTlsTicket(false), tlsStatusRequest(false), hasCcsOrNst(false), initialized_(false)
+Ssl::Bio::sslFeatures::sslFeatures():
+    sslHelloVersion(-1),
+    sslVersion(-1),
+    compressMethod(-1),
+    helloMsgSize(0),
+    unknownCiphers(false),
+    doHeartBeats(true),
+    tlsTicketsExtension(false),
+    hasTlsTicket(false),
+    tlsStatusRequest(false),
+    hasCcsOrNst(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;
@@ -753,49 +770,49 @@
 }
 
 int
 Ssl::Bio::sslFeatures::parseMsgHead(const MemBuf &buf)
 {
     const unsigned char *head = (const unsigned char *)buf.content();
     const char *s = objToString(head, buf.contentSize());
     debugs(83, 7, "SSL Header: " << s);
     if (buf.contentSize() < 5)
         return 0;
 
     if (helloMsgSize > 0)
         return helloMsgSize;
 
     // 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
-        sslVersion = (head[1] << 8) | head[2];
+        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");
-        sslVersion = (head[3] << 8) | head[4];
+        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::checkForCcsOrNst(const unsigned char *msg, size_t size)
 {
     while (size > 5) {
         const int msgType = msg[0];
@@ -1082,40 +1099,44 @@
             } 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 (OPENSSL_VERSION_NUMBER >= 0x10100000L)
         const SSL_METHOD *method = TLS_method();
 #else
         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;

=== modified file 'src/ssl/bio.h'
--- src/ssl/bio.h	2015-05-20 11:00:11 +0000
+++ src/ssl/bio.h	2015-12-13 19:18:45 +0000
@@ -44,40 +44,41 @@
         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 MemBuf &);
         /// Parses msg buffer and return true if one of the Change Cipher Spec
         /// or New Session Ticket messages found
         bool checkForCcsOrNst(const unsigned char *msg, size_t size);
     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
         std::string ecPointFormatList;///< tlsExtension ecPointFormatList
         std::string ellipticCurves; ///< tlsExtension ellipticCurveList
         std::string opaquePrf; ///< tlsExtension opaquePrf
         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
         /// whether Change Cipher Spec message included in ServerHello
         /// handshake message
         bool hasCcsOrNst;
         /// The client random number
         unsigned char client_random[SSL3_RANDOM_SIZE];
         SBuf sessionId;
@@ -99,79 +100,80 @@
     virtual void flush(BIO *table) {}
 
     int fd() const { return fd_; } ///< The SSL socket descriptor
 
     /// Called by linked SSL connection whenever state changes, an alert
     /// appears, or an error occurs. See SSL_set_info_callback().
     virtual void stateChanged(const SSL *ssl, int where, int ret);
 
     /// Creates a low-level BIO table, creates a high-level Ssl::Bio object
     /// for a given socket, and then links the two together via BIO_C_SET_FD.
     static BIO *Create(const int fd, Type type);
     /// Tells ssl connection to use BIO and monitor state via stateChanged()
     static void Link(SSL *ssl, BIO *bio);
 
     /// Prepare the rbuf buffer to accept hello data
     void prepReadBuf();
 
     /// Reads data from socket and record them to a buffer
     int readAndBuffer(char *buf, int size, BIO *table, const char *description);
 
+    /// Return the TLS features requested by TLS client
+    const Bio::sslFeatures &receivedHelloFeatures() const {return receivedHelloFeatures_;}
+
     const MemBuf &rBufData() {return rbuf;}
 protected:
     const int fd_; ///< the SSL socket we are reading and writing
     MemBuf rbuf;  ///< Used to buffer input data.
+    /// The features retrieved from client or Server TLS hello message
+    Bio::sslFeatures receivedHelloFeatures_;
 };
 
 /// BIO node to handle socket IO for squid client side
 /// If bumping is enabled  this Bio detects and analyses client hello message
 /// to retrieve the SSL features supported by the client
 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) {}
 
     /// The ClientBio version of the Ssl::Bio::stateChanged method
     /// When the client hello message retrieved, fill the
     /// "features" member with the client provided informations.
     virtual void stateChanged(const SSL *ssl, int where, int ret);
     /// The ClientBio version of the Ssl::Bio::write method
     virtual int write(const char *buf, int size, BIO *table);
     /// The ClientBio version of the Ssl::Bio::read method
     /// If the holdRead flag is true then it does not write any data
     /// 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); }
-    /// Return the SSL features requested by SSL client
-    const Bio::sslFeatures &getFeatures() const {return features;}
     /// 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;}
 private:
     /// True if the SSL state corresponds to a hello message
     bool isClientHello(int state);
-    /// The futures retrieved from client SSL hello message
-    Bio::sslFeatures features;
     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
 };
 
 /// BIO node to handle socket IO for squid server side
 /// If bumping is enabled, analyses the SSL hello message sent by squid OpenSSL
 /// subsystem (step3 bumping step) against bumping mode:
 ///   * Peek mode:  Send client hello message instead of the openSSL generated
 ///                 hello message and normaly denies bumping and allow only
 ///                 splice or terminate the SSL connection
 ///   * Stare mode: Sends the openSSL generated hello message and normaly
 ///                 denies splicing and allow bump or terminate the SSL
 ///                 connection
 ///  If SQUID_USE_OPENSSL_HELLO_OVERWRITE_HACK is enabled also checks if the
 ///  openSSL library features are compatible with the features reported in
 ///  web client SSL hello message and if it is, overwrites the openSSL SSL
 ///  object members to replace hello message with web client hello message.
@@ -180,57 +182,60 @@
 class ServerBio: public Bio
 {
 public:
     explicit ServerBio(const int anFd): Bio(anFd), helloMsgSize(0), helloBuild(false), allowSplice(false), allowBump(false), holdWrite_(false), record_(false), bumpMode_(bumpNone) {}
     /// 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
     /// If a clientRandom number is set then rewrites the raw hello message
     /// "client random" field with the provided random number.
     /// It may buffer the output packets.
     virtual int write(const char *buf, int size, BIO *table);
     /// The ServerBio version of the Ssl::Bio::read method
     /// If the record flag is set then append the data to the rbuf member
     virtual int read(char *buf, int size, BIO *table);
     /// The ServerBio version of the Ssl::Bio::flush method.
     /// 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();
+
     bool resumingSession();
     /// The write hold state
     bool holdWrite() const {return holdWrite_;}
     /// Enables or disables the write hold state
     void holdWrite(bool h) {holdWrite_ = h;}
     /// Enables or disables the input data recording, for internal analysis.
     void recordInput(bool r) {record_ = r;}
     /// Whether we can splice or not the SSL stream
     bool canSplice() {return allowSplice;}
     /// Whether we can bump or not the SSL stream
     bool canBump() {return allowBump;}
     /// The bumping mode
     void mode(Ssl::BumpMode m) {bumpMode_ = m;}
     Ssl::BumpMode bumpMode() {return bumpMode_;} ///< return the bumping mode
 private:
     sslFeatures clientFeatures; ///< SSL client features extracted from ClientHello message or SSL object
-    sslFeatures serverFeatures; ///< SSL server features extracted from ServerHello message
     SBuf helloMsg; ///< Used to buffer output data.
     mb_size_t  helloMsgSize;
     bool helloBuild; ///< True if the client hello message sent to the server
     bool allowSplice; ///< True if the SSL stream can be spliced
     bool allowBump;  ///< True if the SSL stream can be bumped
     bool holdWrite_;  ///< The write hold state of the bio.
     bool record_; ///< If true the input data recorded to rbuf for internal use
     Ssl::BumpMode bumpMode_;
 };
 
 inline
 std::ostream &operator <<(std::ostream &os, Ssl::Bio::sslFeatures const &f)
 {
     return f.print(os);
 }
 
 } // namespace Ssl
 
 #endif /* SQUID_SSL_BIO_H */
 

_______________________________________________
squid-dev mailing list
[email protected]
http://lists.squid-cache.org/listinfo/squid-dev

Reply via email to