The information about PeekingPeerConnector splicing the connections
was lost in some cases, resulting in two different bugs:
- With a certificate validator, the PeekingPeerConnector class calls
back FwdState, which calls the ConnStateData class, which then tries
secure the connection with the already tunneled SSL client and closes
the connection on negotiating errors.
- Without a certificate validator, the PeekingPeerConnector class
never calls FwdState class, and both PeekingPeerConnector and FwdState
objects stall until finishing tunnelState closes server and client
connections.
Now, PeerConnector always calls FwdState back, marking spliced
connections as such. This has the following positive side-effects:
- When FwdState learns about spliced connections, it does not call
ConnStateData back. Instead, it terminates and gets destroyed. The
tunnel continues uninterrupted.
- The PeekingPeerConnector job ends and is destroyed instead of
waiting to call FwdState.
Fixed step3 splicing.
The information about PeekingPeerConnector splicing the connections
was lost in some cases, resulting in two different bugs:
- With a certificate validator, the PeekingPeerConnector class calls
back FwdState, which calls the ConnStateData class, which then tries
secure the connection with the already tunneled SSL client and
closes the connection on negotiating errors.
- Without a certificate validator, the PeekingPeerConnector class
never calls FwdState class, and both PeekingPeerConnector and
FwdState objects stall until finishing tunnelState closes server
and client connections.
Now, PeerConnector always calls FwdState back, marking spliced
connections as such. This has the following positive side-effects:
- When FwdState learns about spliced connections, it does not call
ConnStateData back. Instead, it terminates and gets destroyed.
The tunnel continues uninterrupted.
- The PeekingPeerConnector job ends and is destroyed instead of
waiting to call FwdState.
This is a Measurement Factory project.
=== modified file 'src/FwdState.cc'
--- src/FwdState.cc 2016-02-02 15:39:23 +0000
+++ src/FwdState.cc 2016-02-04 16:04:10 +0000
@@ -716,40 +716,50 @@
}
#endif
// if not encrypting just run the post-connect actions
Security::EncryptorAnswer nil;
connectedToPeer(nil);
}
void
FwdState::connectedToPeer(Security::EncryptorAnswer &answer)
{
if (ErrorState *error = answer.error.get()) {
fail(error);
answer.error.clear(); // preserve error for errorSendComplete()
if (CachePeer *p = serverConnection()->getPeer())
peerConnectFailed(p);
retryOrBail();
return;
}
+ if (answer.tunneled) {
+ // TODO: When ConnStateData establishes tunnels, its state changes
+ // [in ways that may affect logging?]. Consider informing
+ // ConnStateData about our tunnel or otherwise unifying tunnel
+ // establishment [side effects].
+ unregister(serverConn); // async call owns it now
+ complete(); // destroys us
+ return;
+ }
+
// should reach ConnStateData before the dispatched Client job starts
CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
ConnStateData::notePeerConnection, serverConnection());
if (serverConnection()->getPeer())
peerConnectSucceded(serverConnection()->getPeer());
flags.connected_okay = true;
dispatch();
}
void
FwdState::connectTimeout(int fd)
{
debugs(17, 2, "fwdConnectTimeout: FD " << fd << ": '" << entry->url() << "'" );
assert(serverDestinations[0] != NULL);
assert(fd == serverDestinations[0]->fd);
if (entry->isEmpty()) {
ErrorState *anErr = new ErrorState(ERR_CONNECT_FAIL, Http::scGatewayTimeout, request);
=== modified file 'src/security/EncryptorAnswer.h'
--- src/security/EncryptorAnswer.h 2016-01-01 00:12:18 +0000
+++ src/security/EncryptorAnswer.h 2016-02-03 18:10:10 +0000
@@ -4,34 +4,38 @@
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
* Please see the COPYING and CONTRIBUTORS files for details.
*/
#ifndef SQUID_SECURITY_ENCRYPTORANSWER_H
#define SQUID_SECURITY_ENCRYPTORANSWER_H
#include "base/CbcPointer.h"
#include "comm/forward.h"
class ErrorState;
namespace Security {
/// Peer encrypted connection setup results (supplied via a callback).
/// The connection to peer was secured if and only if the error member is nil.
class EncryptorAnswer
{
public:
+ EncryptorAnswer(): tunneled(false) {}
~EncryptorAnswer(); ///< deletes error if it is still set
Comm::ConnectionPointer conn; ///< peer connection (secured on success)
/// answer recepients must clear the error member in order to keep its info
/// XXX: We should refcount ErrorState instead of cbdata-protecting it.
CbcPointer<ErrorState> error; ///< problem details (nil on success)
+
+ /// whether we spliced the connections instead of negotiating encryption
+ bool tunneled;
};
std::ostream &operator <<(std::ostream &, const Security::EncryptorAnswer &);
} // namepace Security
#endif /* SQUID_SECURITY_ENCRYPTORANSWER_H */
=== modified file 'src/ssl/PeekingPeerConnector.cc'
--- src/ssl/PeekingPeerConnector.cc 2016-02-02 15:39:23 +0000
+++ src/ssl/PeekingPeerConnector.cc 2016-02-03 18:15:42 +0000
@@ -87,40 +87,41 @@
if (request->clientConnectionManager.valid()) {
request->clientConnectionManager->sslBumpMode = finalAction;
request->clientConnectionManager->serverBump()->act.step3 = finalAction;
}
if (finalAction == Ssl::bumpTerminate) {
serverConn->close();
clientConn->close();
} else if (finalAction != Ssl::bumpSplice) {
//Allow write, proceed with the connection
srvBio->holdWrite(false);
srvBio->recordInput(false);
debugs(83,5, "Retry the fwdNegotiateSSL on FD " << serverConn->fd);
Ssl::PeerConnector::noteWantWrite();
} else {
splice = true;
// Ssl Negotiation stops here. Last SSL checks for valid certificates
// and if done, switch to tunnel mode
if (sslFinalized()) {
debugs(83,5, "Abort NegotiateSSL on FD " << serverConn->fd << " and splice the connection");
+ callBack();
}
}
}
Ssl::BumpMode
Ssl::PeekingPeerConnector::checkForPeekAndSpliceGuess() const
{
if (const ConnStateData *csd = request->clientConnectionManager.valid()) {
const Ssl::BumpMode currentMode = csd->sslBumpMode;
if (currentMode == Ssl::bumpStare) {
debugs(83,5, "default to bumping after staring");
return Ssl::bumpBump;
}
debugs(83,5, "default to splicing after " << currentMode);
} else {
debugs(83,3, "default to splicing due to missing info");
}
return Ssl::bumpSplice;
}
@@ -251,40 +252,41 @@
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) {
//retrieved received TLS client informations
auto clientSsl = fd_table[clientConn->fd].ssl.get();
clientConn->tlsNegotiations()->fillWith(clientSsl);
switchToTunnel(request.getRaw(), clientConn, serverConn);
+ tunnelInsteadOfNegotiating();
}
}
}
void
Ssl::PeekingPeerConnector::noteWantWrite()
{
const int fd = serverConnection()->fd;
Security::SessionPtr ssl = fd_table[fd].ssl.get();
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();
}
@@ -359,20 +361,31 @@
void
Ssl::PeekingPeerConnector::serverCertificateVerified()
{
if (ConnStateData *csd = request->clientConnectionManager.valid()) {
Security::CertPointer serverCert;
if(Ssl::ServerBump *serverBump = csd->serverBump())
serverCert.resetAndLock(serverBump->serverCert.get());
else {
const int fd = serverConnection()->fd;
Security::SessionPtr ssl = fd_table[fd].ssl.get();
serverCert.reset(SSL_get_peer_certificate(ssl));
}
if (serverCert.get()) {
csd->resetSslCommonName(Ssl::CommonHostName(serverCert.get()));
debugs(83, 5, "HTTPS server CN: " << csd->sslCommonName() <<
" bumped: " << *serverConnection());
}
}
}
+void
+Ssl::PeekingPeerConnector::tunnelInsteadOfNegotiating()
+{
+ Must(callback != NULL);
+ CbDialer *dialer = dynamic_cast<CbDialer*>(callback->getDialer());
+ Must(dialer);
+ dialer->answer().tunneled = true;
+ debugs(83, 5, "The SSL negotiation with server aborted");
+}
+
+
=== modified file 'src/ssl/PeekingPeerConnector.h'
--- src/ssl/PeekingPeerConnector.h 2016-02-02 15:39:23 +0000
+++ src/ssl/PeekingPeerConnector.h 2016-02-03 18:12:18 +0000
@@ -51,33 +51,37 @@
/// about bumping, splicing or terminating the connection.
void checkForPeekAndSplice();
/// Callback function for ssl_bump acl check in step3 SSL bump step.
void checkForPeekAndSpliceDone(allow_t answer);
/// Handles the final bumping decision.
void checkForPeekAndSpliceMatched(const Ssl::BumpMode finalMode);
/// Guesses the final bumping decision when no ssl_bump rules match.
Ssl::BumpMode checkForPeekAndSpliceGuess() const;
/// Runs after the server certificate verified to update client
/// connection manager members
void serverCertificateVerified();
/// A wrapper function for checkForPeekAndSpliceDone for use with acl
static void cbCheckForPeekAndSpliceDone(allow_t answer, void *data);
private:
+
+ /// Inform caller class that the SSL negotiation aborted
+ void tunnelInsteadOfNegotiating();
+
Comm::ConnectionPointer clientConn; ///< TCP connection to the client
AsyncCall::Pointer callback; ///< we call this with the results
AsyncCall::Pointer closeHandler; ///< we call this when the connection closed
bool splice; ///< whether we are going to splice or not
bool resumingSession; ///< whether it is an SSL resuming session connection
bool serverCertificateHandled; ///< whether handleServerCertificate() succeeded
};
} // namespace Ssl
#endif /* USE_OPENSSL */
#endif /* SQUID_SRC_SSL_PEEKINGPEERCONNECTOR_H */
=== modified file 'src/ssl/PeerConnector.h'
--- src/ssl/PeerConnector.h 2016-02-02 15:39:23 +0000
+++ src/ssl/PeerConnector.h 2016-02-03 18:11:03 +0000
@@ -131,57 +131,57 @@
/// Called when the SSL_connect function aborts with an SSL negotiation error
/// \param result the SSL_connect return code
/// \param ssl_error the error code returned from the SSL_get_error function
/// \param ssl_lib_error the error returned from the ERR_Get_Error function
virtual void noteSslNegotiationError(const int result, const int ssl_error, const int ssl_lib_error);
/// Called when the SSL negotiation to the server completed and the certificates
/// validated using the cert validator.
/// \param error if not NULL the SSL negotiation was aborted with an error
virtual void noteNegotiationDone(ErrorState *error) {}
/// Must implemented by the kid classes to return the Security::ContextPtr object to use
/// for building the SSL objects.
virtual Security::ContextPtr getSslContext() = 0;
/// mimics FwdState to minimize changes to FwdState::initiate/negotiateSsl
Comm::ConnectionPointer const &serverConnection() const { return serverConn; }
void bail(ErrorState *error); ///< Return an error to the PeerConnector caller
+ /// Callback the caller class, and pass the ready to communicate secure
+ /// connection or an error if PeerConnector failed.
+ void callBack();
+
/// If called the certificates validator will not used
void bypassCertValidator() {useCertValidator_ = false;}
HttpRequestPointer request; ///< peer connection trigger or cause
Comm::ConnectionPointer serverConn; ///< TCP connection to the peer
/// Certificate errors found from SSL validation procedure or from cert
/// validator
Ssl::CertErrors *certErrors;
AccessLogEntryPointer al; ///< info for the future access.log entry
private:
PeerConnector(const PeerConnector &); // not implemented
PeerConnector &operator =(const PeerConnector &); // not implemented
- /// Callback the caller class, and pass the ready to communicate secure
- /// connection or an error if PeerConnector failed.
- void callBack();
-
/// Process response from cert validator helper
void sslCrtvdHandleReply(Ssl::CertValidationResponsePointer);
/// Check SSL errors returned from cert validator against sslproxy_cert_error access list
Ssl::CertErrors *sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &, Ssl::ErrorDetail *&);
/// A wrapper function for negotiateSsl for use with Comm::SetSelect
static void NegotiateSsl(int fd, void *data);
AsyncCall::Pointer callback; ///< we call this with the results
AsyncCall::Pointer closeHandler; ///< we call this when the connection closed
time_t negotiationTimeout; ///< the SSL connection timeout to use
time_t startTime; ///< when the peer connector negotiation started
bool useCertValidator_; ///< whether the certificate validator should bypassed
};
} // namespace Ssl
#endif /* USE_OPENSSL */
#endif /* SQUID_SRC_SSL_PEERCONNECTOR_H */
_______________________________________________
squid-dev mailing list
[email protected]
http://lists.squid-cache.org/listinfo/squid-dev