int hostStrictVerify;
int client_dst_passthru;
int dns_mdns;
+#if USE_OPENSSL
+ bool logTlsServerHelloDetails;
+#endif
} onoff;
int pipeline_max_prefetch;
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):
#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"
")");
}
- 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 (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());
{
//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();
#include "neighbors.h"
#include "SquidConfig.h"
#include "SquidTime.h"
+#include "security/NegotiationHistory.h"
class CachePeer;
bool
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.
}
}
cbdataReferenceDone(peer_);
+
+ delete tlsHistory;
}
Comm::ConnectionPointer
return min(lifeTimeLeft, idleTimeout);
}
+Security::NegotiationHistory *
+Comm::Connection::tlsNegotiations()
+{
+ if (!tlsHistory)
+ tlsHistory = new Security::NegotiationHistory;
+ return tlsHistory;
+}
+
class CachePeer;
+namespace Security
+{
+class NegotiationHistory;
+};
+
namespace Comm
{
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);
/** The time the connection object was created */
time_t startTime_;
+
+ /** TLS connection details*/
+ Security::NegotiationHistory *tlsHistory;
};
}; // namespace Comm
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,
#include "Store.h"
#include "tools.h"
#include "URL.h"
+#include "security/NegotiationHistory.h"
#if USE_OPENSSL
#include "ssl/ErrorDetail.h"
#include "ssl/ServerBump.h"
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:
/*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
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;
forward.h \
KeyData.h \
LockingPointer.h \
+ NegotiationHistory.cc \
+ NegotiationHistory.h \
PeerOptions.cc \
PeerOptions.h \
ServerOptions.cc \
--- /dev/null
+#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
+}
--- /dev/null
+#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 */
+
#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"
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);
}
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?
}
}
+ // 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);
+ }
}
}
}
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);
BIO_set_retry_read(table);
return -1;
}
- features.get(rbuf);
+ receivedHelloFeatures_.get(rbuf);
helloState = atHelloReceived;
}
}
}
+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;
}
}
-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);
}
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];
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];
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;
/// 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
/// 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
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
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
/// 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_;}
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