From: Christos Tsantilas Date: Wed, 23 Dec 2015 10:11:26 +0000 (+0200) Subject: Log TLS Cryptography Parameters X-Git-Tag: SQUID_4_0_4~25 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2bcab8520c9c0b2327549c9c4a619e94176a78a6;p=thirdparty%2Fsquid.git Log TLS Cryptography Parameters This patch adds the following formatting codes: %ssl::>negotiated_version The TLS version of the client-to-Squid connection. %ssl::received_hello_version The TLS version of the Hello message received from TLS client %ssl::received_supported_version The maximum TLS version supported by the the TLS client. %ssl::cipher The negotiated cipher of the client-to-Squid connection. %ssl::negotiated_version The negotiated TLS version of the + client connection. + + %ssl::received_hello_version The TLS version of the Hello + message received from TLS client. + + %ssl::received_supported_version The maximum TLS version + supported by the TLS client. + + %ssl::negotiated_cipher The negotiated cipher of the + client connection. + + %ssl::clientConnection->tlsNegotiations()->fillWith(ssl); client_cert = SSL_get_peer_certificate(ssl); @@ -3863,7 +3864,7 @@ clientPeekAndSpliceSSL(int fd, void *data) 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()); @@ -3940,6 +3941,10 @@ 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(b->ptr); MemBuf const &rbuf = bio->rBufData(); diff --git a/src/comm/Connection.cc b/src/comm/Connection.cc index f5123e1622..e098feb8b6 100644 --- a/src/comm/Connection.cc +++ b/src/comm/Connection.cc @@ -15,6 +15,7 @@ #include "neighbors.h" #include "SquidConfig.h" #include "SquidTime.h" +#include "security/NegotiationHistory.h" class CachePeer; bool @@ -30,7 +31,8 @@ Comm::Connection::Connection() : 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. } @@ -45,6 +47,8 @@ Comm::Connection::~Connection() } cbdataReferenceDone(peer_); + + delete tlsHistory; } Comm::ConnectionPointer @@ -119,3 +123,11 @@ Comm::Connection::timeLeft(const time_t idleTimeout) const return min(lifeTimeLeft, idleTimeout); } +Security::NegotiationHistory * +Comm::Connection::tlsNegotiations() +{ + if (!tlsHistory) + tlsHistory = new Security::NegotiationHistory; + return tlsHistory; +} + diff --git a/src/comm/Connection.h b/src/comm/Connection.h index 6a29e100be..e74bea6cc2 100644 --- a/src/comm/Connection.h +++ b/src/comm/Connection.h @@ -28,6 +28,11 @@ class CachePeer; +namespace Security +{ +class NegotiationHistory; +}; + namespace Comm { @@ -107,6 +112,10 @@ public: 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); @@ -149,6 +158,9 @@ private: /** The time the connection object was created */ time_t startTime_; + + /** TLS connection details*/ + Security::NegotiationHistory *tlsHistory; }; }; // namespace Comm diff --git a/src/format/ByteCode.h b/src/format/ByteCode.h index 217ffcf677..c19f4d4e8b 100644 --- a/src/format/ByteCode.h +++ b/src/format/ByteCode.h @@ -218,6 +218,14 @@ typedef enum { 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, diff --git a/src/format/Format.cc b/src/format/Format.cc index 402e5faf22..2639db1cc2 100644 --- a/src/format/Format.cc +++ b/src/format/Format.cc @@ -24,6 +24,7 @@ #include "Store.h" #include "tools.h" #include "URL.h" +#include "security/NegotiationHistory.h" #if USE_OPENSSL #include "ssl/ErrorDetail.h" #include "ssl/ServerBump.h" @@ -1263,6 +1264,46 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS 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: diff --git a/src/format/Token.cc b/src/format/Token.cc index 62416a474e..dd983aa6e1 100644 --- a/src/format/Token.cc +++ b/src/format/Token.cc @@ -219,6 +219,14 @@ static TokenTableEntry TokenTableSsl[] = { /*TokenTableEntry("negotiated_version", LFT_TLS_CLIENT_NEGOTIATED_VERSION), + TokenTableEntry("negotiated_cipher", LFT_TLS_CLIENT_NEGOTIATED_CIPHER), + TokenTableEntry("received_hello_version", LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION), + TokenTableEntry("received_supported_version", LFT_TLS_CLIENT_SUPPORTED_VERSION), + TokenTableEntry("= 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(b->ptr); + + if (::Config.onoff.logTlsServerHelloDetails) { + if (Ssl::ServerBio *srvBio = dynamic_cast(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 +} diff --git a/src/security/NegotiationHistory.h b/src/security/NegotiationHistory.h new file mode 100644 index 0000000000..70798f6003 --- /dev/null +++ b/src/security/NegotiationHistory.h @@ -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 +#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 */ + diff --git a/src/ssl/PeerConnector.cc b/src/ssl/PeerConnector.cc index f2b3ecf44f..263c00d33f 100644 --- a/src/ssl/PeerConnector.cc +++ b/src/ssl/PeerConnector.cc @@ -20,6 +20,7 @@ #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" @@ -676,7 +677,7 @@ Ssl::PeekingPeerConnector::initializeSsl() if (SSL *clientSsl = fd_table[clientConn->fd].ssl) { BIO *b = SSL_get_rbio(clientSsl); cltBio = static_cast(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); } @@ -696,7 +697,7 @@ 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->getFeatures(); + const Ssl::Bio::sslFeatures &features = cltBio->receivedHelloFeatures(); if (features.sslVersion != -1) { features.applyToSSL(ssl, csd->sslBumpMode); // Should we allow it for all protocols? @@ -781,10 +782,16 @@ Ssl::PeekingPeerConnector::noteNegotiationDone(ErrorState *error) } } + // 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); + } } } diff --git a/src/ssl/bio.cc b/src/ssl/bio.cc index d09905f79b..9e4e49c650 100644 --- a/src/ssl/bio.cc +++ b/src/ssl/bio.cc @@ -225,7 +225,7 @@ Ssl::ClientBio::read(char *buf, int size, BIO *table) } 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); @@ -247,7 +247,7 @@ Ssl::ClientBio::read(char *buf, int size, BIO *table) BIO_set_retry_read(table); return -1; } - features.get(rbuf); + receivedHelloFeatures_.get(rbuf); helloState = atHelloReceived; } @@ -502,19 +502,25 @@ Ssl::ServerBio::flush(BIO *table) } } +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; @@ -639,7 +645,18 @@ squid_ssl_info(const SSL *ssl, int where, int 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); } @@ -770,7 +787,7 @@ Ssl::Bio::sslFeatures::parseMsgHead(const MemBuf &buf) 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]; @@ -778,7 +795,7 @@ Ssl::Bio::sslFeatures::parseMsgHead(const MemBuf &buf) 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]; @@ -1099,6 +1116,10 @@ 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; diff --git a/src/ssl/bio.h b/src/ssl/bio.h index 0ae6e4403e..2e9b477db1 100644 --- a/src/ssl/bio.h +++ b/src/ssl/bio.h @@ -61,6 +61,7 @@ public: /// 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 @@ -116,10 +117,15 @@ public: /// 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 @@ -144,8 +150,6 @@ public: 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 @@ -153,8 +157,6 @@ public: 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 @@ -197,6 +199,10 @@ public: /// 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_;} @@ -213,7 +219,6 @@ public: 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