void
ConnStateData::afterClientRead()
{
+#if USE_OPENSSL
+ if (atTlsPeek) {
+ assert(!inBuf.isEmpty());
+ if (!tlsParser.parseClientHello(inBuf)) {
+ if (!tlsParser.parseError) {
+ readSomeData();
+ return;
+ }
+ }
+ atTlsPeek = false;
+ Must(sslServerBump);
+ Must(sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare);
+ startPeekAndSplice();
+ return;
+ }
+#endif
/* Process next request */
if (pipeline.empty())
fd_note(clientConnection->fd, "Reading next request");
switchedToHttps_(false),
sslServerBump(NULL),
signAlgorithm(Ssl::algSignTrusted),
+ atTlsPeek(false),
#endif
stoppedSending_(NULL),
stoppedReceiving_(NULL)
switch (ssl_error) {
case SSL_ERROR_WANT_READ:
- Comm::SetSelect(fd, COMM_SELECT_READ, callback, conn, 0);
+ Comm::SetSelect(fd, COMM_SELECT_READ, callback, (callback != NULL ? conn : NULL), 0);
return 0;
case SSL_ERROR_WANT_WRITE:
- Comm::SetSelect(fd, COMM_SELECT_WRITE, callback, conn, 0);
+ Comm::SetSelect(fd, COMM_SELECT_WRITE, callback, (callback != NULL ? conn : NULL), 0);
return 0;
case SSL_ERROR_SYSCALL:
Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientNegotiateSSL, this, 0);
switchedToHttps_ = true;
+ auto ssl = fd_table[clientConnection->fd].ssl.get();
+ BIO *b = SSL_get_rbio(ssl);
+ Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
+ bio->setReadBufData(inBuf);
+ bio->parsedDetails(tlsParser.details);
}
void
} else if (bumpServerMode == Ssl::bumpPeek || bumpServerMode == Ssl::bumpStare) {
request->flags.sslPeek = true;
sslServerBump = new Ssl::ServerBump(request, NULL, bumpServerMode);
- startPeekAndSplice();
+
+ // commSetConnTimeout() was called for this request before we switched.
+ // Fix timeout to request_start_timeout
+ typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
+ AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
+ TimeoutDialer, this, ConnStateData::requestTimeout);
+ commSetConnTimeout(clientConnection, Config.Timeout.request_start_timeout, timeoutCall);
+ // Also reset receivedFirstByte_ flag to allow this timeout work in the case we have
+ // a bumbed "connect" request on non transparent port.
+ receivedFirstByte_ = false;
+ // Get more data to peek at Tls
+ atTlsPeek = true;
+ readSomeData();
return;
}
return false;
}
-/** negotiate an SSL connection */
-static void
-clientPeekAndSpliceSSL(int fd, void *data)
-{
- ConnStateData *conn = (ConnStateData *)data;
- auto ssl = fd_table[fd].ssl.get();
-
- debugs(83, 5, "Start peek and splice on FD " << fd);
- 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();
+void ConnStateData::startPeekAndSplice()
+{
+ if (tlsParser.parseError) {
+ if (!spliceOnError(ERR_PROTOCOL_UNKNOWN))
+ clientConnection->close();
return;
}
+ receivedFirstByte();
- if (!bio->rBufData().isEmpty() > 0)
- conn->receivedFirstByte();
-
- if (bio->gotHello()) {
- if (conn->serverBump()) {
- Security::TlsDetails::Pointer const &details = bio->receivedHelloDetails();
- if (!details->serverName.isEmpty()) {
- conn->serverBump()->clientSni = details->serverName;
- conn->resetSslCommonName(details->serverName.c_str());
- }
+ if (serverBump()) {
+ Security::TlsDetails::Pointer const &details = tlsParser.details;
+ if (!details->serverName.isEmpty()) {
+ serverBump()->clientSni = details->serverName;
+ resetSslCommonName(details->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;
- if (!httpsCreate(clientConnection, unConfiguredCTX))
- return;
-
- // commSetConnTimeout() was called for this request before we switched.
- // Fix timeout to request_start_timeout
- typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
- AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
- TimeoutDialer, this, ConnStateData::requestTimeout);
- commSetConnTimeout(clientConnection, Config.Timeout.request_start_timeout, timeoutCall);
- // Also reset receivedFirstByte_ flag to allow this timeout work in the case we have
- // a bumbed "connect" request on non transparent port.
- receivedFirstByte_ = false;
-
- // Disable the client read handler until CachePeer selection is complete
+ // We should disable read/write handlers
Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
- Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientPeekAndSpliceSSL, this, 0);
- switchedToHttps_ = true;
+ Comm::SetSelect(clientConnection->fd, COMM_SELECT_WRITE, NULL, NULL, 0);
- auto ssl = fd_table[clientConnection->fd].ssl.get();
- BIO *b = SSL_get_rbio(ssl);
- Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
- bio->hold(true);
+ startPeekAndSpliceDone();
}
void httpsSslBumpStep2AccessCheckDone(allow_t answer, void *data)
ConnStateData::splice()
{
// normally we can splice here, because we just got client hello message
- auto ssl = fd_table[clientConnection->fd].ssl.get();
- //retrieve received TLS client information
- clientConnection->tlsNegotiations()->fillWith(ssl);
+ if (auto ssl = fd_table[clientConnection->fd].ssl.get()) {
+ // We built
+ //retrieve received TLS client information
+ clientConnection->tlsNegotiations()->fillWith(ssl);
- BIO *b = SSL_get_rbio(ssl);
- Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
- SBuf const &rbuf = bio->rBufData();
- debugs(83,5, "Bio for " << clientConnection << " read " << rbuf.length() << " helo bytes");
- // Do splice:
- fd_table[clientConnection->fd].read_method = &default_read_method;
- fd_table[clientConnection->fd].write_method = &default_write_method;
+ // BIO *b = SSL_get_rbio(ssl);
+ // Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
+ // SBuf const &rbuf = bio->rBufData();
+ // // The following do not needed, inBuf and rbuf has the same content.
+ // inBuf.assign(rbuf);
+ // debugs(83,5, "Bio for " << clientConnection << " read " << rbuf.length() << " helo bytes");
+
+ // Do splice:
+ fd_table[clientConnection->fd].read_method = &default_read_method;
+ fd_table[clientConnection->fd].write_method = &default_write_method;
+
+ } else {
+ clientConnection->tlsNegotiations()->fillWith(tlsParser.details);
+ }
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
- fakeAConnectRequest("intercepted TLS spliced", rbuf);
+ fakeAConnectRequest("intercepted TLS spliced", inBuf);
} 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)
transferProtocol = Http::ProtocolVersion();
- // inBuf still has the "CONNECT ..." request data, reset it to SSL hello message
- inBuf.append(rbuf);
Http::StreamPointer context = pipeline.front();
ClientHttpRequest *http = context->http;
tunnelStart(http);
return;
}
+ // will call httpsPeeked() with certificate and connection, eventually
+ auto unConfiguredCTX = Ssl::createSSLContext(port->signingCert, port->signPkey, *port);
+ fd_table[clientConnection->fd].dynamicSslContext = unConfiguredCTX;
+
+ if (!httpsCreate(clientConnection, unConfiguredCTX))
+ return;
+
+ switchedToHttps_ = true;
+
+ auto ssl = fd_table[clientConnection->fd].ssl.get();
+ BIO *b = SSL_get_rbio(ssl);
+ Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
+ bio->setReadBufData(inBuf);
+ bio->parsedDetails(tlsParser.details);
+ bio->hold(true);
+
+ // Here squid should have all of the client hello message so the
+ // Squid_SSL_accept should return 0;
+ // This block exist only to force openSSL parse client hello and detect
+ // ERR_SECURE_ACCEPT_FAIL error, which should be checked and splice if required.
+ int ret = 0;
+ if ((ret = Squid_SSL_accept(this, NULL)) < 0) {
+ debugs(83, 2, "SSL_accept failed.");
+ const err_type err = ERR_SECURE_ACCEPT_FAIL;
+ if (!spliceOnError(err))
+ clientConnection->close();
+ return;
+ }
+
+ // Do we need to reset inBuf here?
+ // inBuf.clear();
+
+ debugs(83, 5, "Peek and splice at step2 done. Start forwarding the request!!! ");
FwdState::Start(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw(), http ? http->al : NULL);
}
/// appears, or an error occurs. See SSL_set_info_callback().
virtual void stateChanged(const SSL *ssl, int where, int ret);
+ /// Return the TLS Details advertised by TLS server.
+ virtual const Security::TlsDetails::Pointer &receivedHelloDetails() const = 0;
+
/// 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);
/// Reads data from socket and record them to a buffer
int readAndBuffer(BIO *table, const char *description);
- /// Return the TLS Details requested/advirised by TLS client or server.
- const Security::TlsDetails::Pointer &receivedHelloDetails() const {return parser_.details;}
-
const SBuf &rBufData() {return rbuf;}
protected:
const int fd_; ///< the SSL socket we are reading and writing
SBuf rbuf; ///< Used to buffer input data.
- /// The features retrieved from client or Server TLS hello message
- //Security::TlsDetails::Pointer receivedHelloDetails_;
- Security::HandshakeParser parser_; ///< The SSL messages parser.
};
/// BIO node to handle socket IO for squid client side
/// 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() const { return (parser_.parseDone && !parser_.parseError); }
+ //bool gotHello() const { return (parser_.parseDone && !parser_.parseError); }
/// Prevents or allow writting on socket.
void hold(bool h) {holdRead_ = holdWrite_ = h;}
- /// True if client does not looks like an SSL client
- bool noSslClient() {return parser_.parseError;}
+ void setReadBufData(SBuf &data) {rbuf = data;}
+ const Security::TlsDetails::Pointer &receivedHelloDetails() const {return details;}
+ void parsedDetails(Security::TlsDetails::Pointer &someDetails) {details = someDetails;}
private:
/// True if the SSL state corresponds to a hello message
bool isClientHello(int state);
bool holdRead_; ///< The read hold state of the bio.
bool holdWrite_; ///< The write hold state of the bio.
int helloSize; ///< The SSL hello message sent by client size
- //bool wrongProtocol; ///< true if client SSL hello parsing failed
+
+ /// SSL client features extracted from ClientHello message or SSL object
+ Security::TlsDetails::Pointer details;
};
/// BIO node to handle socket IO for squid server side
void mode(Ssl::BumpMode m) {bumpMode_ = m;}
Ssl::BumpMode bumpMode() {return bumpMode_;} ///< return the bumping mode
+ /// Return the TLS Details advertised by TLS server.
+ const Security::TlsDetails::Pointer &receivedHelloDetails() const {return parser_.details;}
/// Return true if the Server hello message received
bool gotHello() const { return (parser_.parseDone && !parser_.parseError); }
///< The size of data stored in rbuf which passed to the openSSL
size_t rbufConsumePos;
+ Security::HandshakeParser parser_; ///< The SSL messages parser.
};
} // namespace Ssl