#include "acl/Arp.h"
#include "acl/Eui64.h"
#endif
+#if USE_OPENSSL
+#include "acl/AtBumpStep.h"
+#include "acl/AtBumpStepData.h"
+#endif
#include "acl/Asn.h"
#include "acl/Browser.h"
#include "acl/Checklist.h"
ACLStrategised<X509 *> ACLCertificate::CARegistryEntry_(new ACLCertificateData (Ssl::GetX509CAAttribute, "*"), ACLCertificateStrategy::Instance(), "ca_cert");
ACL::Prototype ACLServerCertificate::X509FingerprintRegistryProtoype(&ACLServerCertificate::X509FingerprintRegistryEntry_, "server_cert_fingerprint");
ACLStrategised<X509 *> ACLServerCertificate::X509FingerprintRegistryEntry_(new ACLCertificateData(Ssl::GetX509Fingerprint, "-sha1", true), ACLServerCertificateStrategy::Instance(), "server_cert_fingerprint");
+
+ACL::Prototype ACLAtStep::RegistryProtoype(&ACLAtStep::RegistryEntry_, "atstep");
+ACLStrategised<Ssl::BumpStep> ACLAtStep::RegistryEntry_(new ACLAtStepData, ACLAtStepStrategy::Instance(), "atstep");
#endif
#if USE_SQUID_EUI
#if USE_OPENSSL
acl_access *ssl_bump;
- acl_access *ssl_bump_peeked;
#endif
#if FOLLOW_X_FORWARDED_FOR
acl_access *followXFF;
--- /dev/null
+#include "squid.h"
+#include "acl/Checklist.h"
+#include "acl/AtBumpStep.h"
+#include "acl/AtBumpStepData.h"
+#include "client_side.h"
+#include "ssl/ServerBump.h"
+//#include "ssl/support.h"
+
+int
+ACLAtStepStrategy::match (ACLData<Ssl::BumpStep> * &data, ACLFilledChecklist *checklist, ACLFlags &)
+{
+ if (checklist->conn() != NULL) {
+ if (Ssl::ServerBump *bump = checklist->conn()->serverBump())
+ return data->match(bump->step);
+ else
+ return data->match(Ssl::bumpStep1);
+ }
+ return 0;
+}
+
+ACLAtStepStrategy *
+ACLAtStepStrategy::Instance()
+{
+ return &Instance_;
+}
+
+ACLAtStepStrategy ACLAtStepStrategy::Instance_;
--- /dev/null
+#ifndef SQUID_ACLATSTEP_H
+#define SQUID_ACLATSTEP_H
+#include "acl/Strategised.h"
+#include "acl/Strategy.h"
+#include "ssl/support.h"
+
+class ACLAtStepStrategy : public ACLStrategy<Ssl::BumpStep>
+{
+
+public:
+ virtual int match (ACLData<MatchType> * &, ACLFilledChecklist *, ACLFlags &);
+ static ACLAtStepStrategy *Instance();
+ /* Not implemented to prevent copies of the instance. */
+ /* Not private to prevent brain dead g+++ warnings about
+ * private constructors with no friends */
+ ACLAtStepStrategy(ACLAtStepStrategy const &);
+
+private:
+ static ACLAtStepStrategy Instance_;
+ ACLAtStepStrategy() {}
+
+ ACLAtStepStrategy&operator=(ACLAtStepStrategy const &);
+};
+
+class ACLAtStep
+{
+
+private:
+ static ACL::Prototype RegistryProtoype;
+ static ACLStrategised<Ssl::BumpStep> RegistryEntry_;
+};
+
+#endif /* SQUID_ACLATSTEP_H */
--- /dev/null
+#include "squid.h"
+#include "acl/Checklist.h"
+#include "acl/AtBumpStepData.h"
+#include "cache_cf.h"
+#include "Debug.h"
+#include "wordlist.h"
+
+ACLAtStepData::ACLAtStepData()
+{}
+
+ACLAtStepData::ACLAtStepData(ACLAtStepData const &old)
+{
+ values.assign(old.values.begin(), old.values.end());
+}
+
+ACLAtStepData::~ACLAtStepData()
+{
+}
+
+bool
+ACLAtStepData::match(Ssl::BumpStep toFind)
+{
+ for (std::list<Ssl::BumpStep>::const_iterator it = values.begin(); it != values.end(); ++it) {
+ if (*it == toFind)
+ return true;
+ }
+ return false;
+}
+
+SBufList
+ACLAtStepData::dump() const
+{
+ SBufList sl;
+ for (std::list<Ssl::BumpStep>::const_iterator it = values.begin(); it != values.end(); ++it) {
+ sl.push_back(SBuf(*it == Ssl::bumpStep1 ? "step1" :
+ *it == Ssl::bumpStep2 ? "step2" :
+ *it == Ssl::bumpStep3 ? "step3" : "???"));
+ }
+ return sl;
+}
+
+void
+ACLAtStepData::parse()
+{
+ while (const char *t = strtokFile()) {
+ if (strcasecmp(t, "step1") == 0) {
+ values.push_back(Ssl::bumpStep1);
+ } else if (strcasecmp(t, "step2") == 0) {
+ values.push_back(Ssl::bumpStep2);
+ } else if (strcasecmp(t, "step3") == 0) {
+ values.push_back(Ssl::bumpStep3);
+ } else {
+ debugs(28, DBG_CRITICAL, "FATAL: invalid AtStep step: " << t);
+ self_destruct();
+ }
+ }
+}
+
+bool
+ACLAtStepData::empty() const
+{
+ return values.empty();
+}
+
+ACLAtStepData *
+ACLAtStepData::clone() const
+{
+ return new ACLAtStepData(*this);
+}
--- /dev/null
+#ifndef SQUID_ACLATSTEPDATA_H
+#define SQUID_ACLATSTEPDATA_H
+#include "acl/Acl.h"
+#include "acl/Data.h"
+#include "CbDataList.h"
+#include "ssl/support.h"
+#include <list>
+
+class ACLAtStepData : public ACLData<Ssl::BumpStep>
+{
+
+public:
+ MEMPROXY_CLASS(ACLAtStepData);
+
+ ACLAtStepData();
+ ACLAtStepData(ACLAtStepData const &);
+ ACLAtStepData &operator= (ACLAtStepData const &);
+ virtual ~ACLAtStepData();
+ bool match(Ssl::BumpStep);
+ virtual SBufList dump() const;
+ void parse();
+ bool empty() const;
+ virtual ACLAtStepData *clone() const;
+
+ std::list<Ssl::BumpStep> values;
+};
+
+MEMPROXY_CLASS_INLINE(ACLAtStepData);
+
+#endif /* SQUID_ACLSSL_ERRORDATA_H */
## data-specific ACLs
libacls_la_SOURCES = \
+ AtBumpStep.cc \
+ AtBumpStep.h \
+ AtBumpStepData.cc \
+ AtBumpStepData.h \
IntRange.cc \
IntRange.h \
RegexData.cc \
static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump);
static void free_sslproxy_ssl_bump(acl_access **ssl_bump);
-static void parse_sslproxy_ssl_bump_peeked(acl_access **ssl_bump);
-static void dump_sslproxy_ssl_bump_peeked(StoreEntry *entry, const char *name, acl_access *ssl_bump);
-static void free_sslproxy_ssl_bump_peeked(acl_access **ssl_bump);
#endif /* USE_OPENSSL */
static void parse_ftp_epsv(acl_access **ftp_epsv);
} else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) {
action.kind = Ssl::bumpServerFirst;
bumpCfgStyleNow = bcsNew;
- } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpPeekAndSplice]) == 0) {
- action.kind = Ssl::bumpPeekAndSplice;
+ } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpPeek]) == 0) {
+ action.kind = Ssl::bumpPeek;
+ bumpCfgStyleNow = bcsNew;
+ } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpStare]) == 0) {
+ action.kind = Ssl::bumpStare;
+ bumpCfgStyleNow = bcsNew;
+ } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpSplice]) == 0) {
+ action.kind = Ssl::bumpSplice;
+ bumpCfgStyleNow = bcsNew;
+ } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpBump]) == 0) {
+ action.kind = Ssl::bumpBump;
+ bumpCfgStyleNow = bcsNew;
+ } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpTerminate]) == 0) {
+ action.kind = Ssl::bumpTerminate;
+ bumpCfgStyleNow = bcsNew;
+ } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpErr]) == 0) {
+ action.kind = Ssl::bumpErr;
bumpCfgStyleNow = bcsNew;
} else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpNone]) == 0) {
action.kind = Ssl::bumpNone;
free_acl_access(ssl_bump);
}
-static void
-parse_sslproxy_ssl_bump_peeked(acl_access **ssl_bump_peeked)
-{
- char *bm;
- if ((bm = ConfigParser::NextToken()) == NULL) {
- self_destruct();
- return;
- }
-
- allow_t action = allow_t(ACCESS_ALLOWED);
-
- if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) {
- action.kind = Ssl::bumpServerFirst;
- } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpSplice]) == 0) {
- action.kind = Ssl::bumpSplice;
- } else {
- debugs(3, DBG_CRITICAL, "FATAL: unknown ssl_bump_peeked mode: " << bm);
- self_destruct();
- return;
- }
-
- Acl::AndNode *rule = new Acl::AndNode;
- rule->context("(ssl_bump_peeked rule)", config_input_line);
- rule->lineParse();
-
- assert(ssl_bump_peeked);
- if (!*ssl_bump_peeked) {
- *ssl_bump_peeked = new Acl::Tree;
- (*ssl_bump_peeked)->context("(ssl_bump_peeked rules)", config_input_line);
- }
-
- (*ssl_bump_peeked)->add(rule, action);
-}
-
-static void
-dump_sslproxy_ssl_bump_peeked(StoreEntry *entry, const char *name, acl_access *ssl_bump_peeked)
-{
- if (ssl_bump_peeked)
- dump_SBufList(entry, ssl_bump_peeked->treeDump(name, Ssl::BumpModeStr));
-}
-
-static void
-free_sslproxy_ssl_bump_peeked(acl_access **ssl_bump_peeked)
-{
- free_acl_access(ssl_bump_peeked);
-}
-
#endif
static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers)
# Optional argument specifies the digest algorithm to use.
# The SHA1 digest algorithm is the default and is currently
# the only algorithm supported (-sha1).
+
+ acl aclname atstep step
+ # match against SSL bumping step. Valid SSL bumping step values:
+ # step1: Get TCP-level and CONNECT info.
+ # step2: Get SSL Client Hello info.
+ # step3: Get SSL Server Hello info.
ENDIF
acl aclname any-of acl1 acl2 ...
# match any one of the acls [fast or slow]
ssl_bump <mode> [!]acl ...
The following bumping modes are supported:
+ splice
+ Become a TCP tunnel without decoding the connection.
+
+ bump
+ Establish a secure connection with the server and, using a
+ mimicked server certificate, with the client.
+
+ peek
+ Receive client (step1) or server (step2) certificate while
+ preserving the possibility of splicing the connection. Peeking
+ at the server certificate usually precludes future bumping of
+ the connection. This action is the focus of this project.
+
+ stare
+ Receive client (step1) or server (step2) certificate while
+ preserving the possibility of bumping the connection. Staring
+ at the server certificate usually precludes future splicing of
+ the connection. Currently, we are not aware of any work being
+ done to support this action.
+
+ terminate
+ Close client and server connections.
+
+ Compatibility modes:
client-first
Allow bumping of the connection. Establish a secure connection
ssl_bump server-first all
DOC_END
-NAME: ssl_bump_peeked
-IFDEF: USE_OPENSSL
-TYPE: sslproxy_ssl_bump_peeked
-LOC: Config.accessList.ssl_bump_peeked
-DEFAULT: none
-DOC_START
- This option used to control splicing decision after we peek at the
- certificates
-
- ssl_bump_peeked <mode> [!]acl ...
-
- The following peeked modes are supported:
-
- server-first
- Allow bumping the connection
-
- splice
- Do not bump the connection if possible.
-
- ssl_bump_peeked server-first safeToBump
- ssl_bump_peeked splice all
-DOC_END
-
NAME: sslproxy_flags
IFDEF: USE_OPENSSL
DEFAULT: none
// Require both a match and a positive bump mode to work around exceptional
// cases where ACL code may return ACCESS_ALLOWED with zero answer.kind.
- if (answer == ACCESS_ALLOWED && answer.kind != Ssl::bumpNone) {
+ if (answer == ACCESS_ALLOWED && (answer.kind != Ssl::bumpNone && answer.kind != Ssl::bumpSplice)) {
debugs(33, 2, HERE << "sslBump needed for " << connState->clientConnection);
connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
httpsEstablish(connState, NULL, (Ssl::BumpMode)answer.kind);
debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
} else {
debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " was successfully recieved from ssl_crtd");
- if (sslServerBump && sslServerBump->mode == Ssl::bumpPeekAndSplice) {
+ if (sslServerBump && (sslServerBump->mode == Ssl::bumpPeek || sslServerBump->mode == Ssl::bumpStare)) {
doPeekAndSpliceStep();
SSL *ssl = fd_table[clientConnection->fd].ssl;
bool ret = Ssl::configureSSLUsingPkeyAndCertFromMemory(ssl, reply_message.getBody().c_str(), *port);
assert(sslBumpCertKey.size() > 0 && sslBumpCertKey[0] != '\0');
// Disable caching for bumpPeekAndSplice mode
- if (!(sslServerBump && sslServerBump->mode == Ssl::bumpPeekAndSplice)) {
+ if (!(sslServerBump && (sslServerBump->mode == Ssl::bumpPeek || sslServerBump->mode == Ssl::bumpStare))) {
debugs(33, 5, HERE << "Finding SSL certificate for " << sslBumpCertKey << " in cache");
Ssl::LocalContextStorage * ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s);
SSL_CTX * dynCtx = NULL;
#endif // USE_SSL_CRTD
debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName);
- if (sslServerBump && sslServerBump->mode == Ssl::bumpPeekAndSplice) {
+ if (sslServerBump && (sslServerBump->mode == Ssl::bumpPeek || sslServerBump->mode == Ssl::bumpStare)) {
doPeekAndSpliceStep();
SSL *ssl = fd_table[clientConnection->fd].ssl;
if (!Ssl::configureSSL(ssl, certProperties, *port))
FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
return;
}
- else if (bumpServerMode == Ssl::bumpPeekAndSplice) {
+ else if (bumpServerMode == Ssl::bumpPeek || bumpServerMode == Ssl::bumpStare) {
request->flags.sslPeek = true;
- sslServerBump = new Ssl::ServerBump(request, NULL, Ssl::bumpPeekAndSplice);
+ sslServerBump = new Ssl::ServerBump(request, NULL, bumpServerMode);
startPeekAndSplice();
return;
}
bio->hold(true);
}
+int default_read_method(int, char *, int);
+int default_write_method(int, const char *, int);
+void httpsSslBumpStep2AccessCheckDone(allow_t answer, void *data)
+{
+ ConnStateData *connState = (ConnStateData *) data;
+
+ // if the connection is closed or closing, just return.
+ if (!connState->isOpen())
+ return;
+
+ debugs(33, 5, HERE << "Answer: " << answer << " kind:" << answer.kind);
+ if (answer == ACCESS_ALLOWED && answer.kind != Ssl::bumpNone && answer.kind != Ssl::bumpSplice) {
+ if (answer.kind == Ssl::bumpTerminate || answer.kind == Ssl::bumpErr)
+ comm_close(connState->clientConnection->fd);
+ else {
+ if (answer.kind != Ssl::bumpPeek || answer.kind == Ssl::bumpStare)
+ connState->sslBumpMode = Ssl::bumpBump;
+ else
+ connState->sslBumpMode = (Ssl::BumpMode)answer.kind;
+ connState->startPeekAndSpliceDone();
+ }
+ } else {
+ //Normally we can splice here, because we just got client hello message
+ SSL *ssl = fd_table[connState->clientConnection->fd].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 " << connState->clientConnection->fd << " read " << rbuf.contentSize() << " helo bytes");
+ // Do splice:
+
+ connState->sslBumpMode = Ssl::bumpSplice;
+
+ if (connState->transparent()) {
+#if 0 && HANDLE_TRANSPARENT_PEEK_AND_SLPICE
+ // fake a CONNECT request to force connState to tunnel
+ static char ip[MAX_IPSTRLEN];
+ connState->clientConnection->local.toUrl(ip, sizeof(ip));
+ connState->in.buf.assign("CONNECT ").append(ip).append(" HTTP/1.1\r\nHost: ").append(ip).append("\r\n\r\n").append(rbuf.content(), rbuf.contentSize());
+ bool ret = connState->handleReadData(&in.buf);
+ if (ret)
+ ret = connState->clientParseRequests();
+
+ if (!ret) {
+ debugs(33, 2, HERE << "Failed to start fake CONNECT request for ssl spliced connection: " << connState->clientConnection);
+ connState->clientConnection->close();
+ }
+#endif
+ } else {
+ // in.buf still has the "CONNECT ..." request data, reset it to SSL hello message
+ connState->in.buf.append(rbuf.content(), rbuf.contentSize());
+ fd_table[connState->clientConnection->fd].read_method = &default_read_method;
+ fd_table[connState->clientConnection->fd].write_method = &default_write_method;
+ ClientSocketContext::Pointer context = connState->getCurrentContext();
+ ClientHttpRequest *http = context->http;
+ tunnelStart(http, &http->out.size, &http->al->http.code, http->al);
+ }
+ }
+}
+
void
ConnStateData::startPeekAndSpliceDone()
{
+ // This is the Step2 of the SSL bumping
+ assert(sslServerBump);
+ if (sslServerBump->step == Ssl::bumpStep1) {
+ sslServerBump->step = Ssl::bumpStep2;
+ // Run a accessList check to check if want to splice or continue bumping
+
+ ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, sslServerBump->request.getRaw(), NULL);
+ //acl_checklist->src_addr = params.conn->remote;
+ //acl_checklist->my_addr = s->s;
+ acl_checklist->nonBlockingCheck(httpsSslBumpStep2AccessCheckDone, this);
+ return;
+ }
+
FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
}
/// returns raw sslBump mode value
Ssl::BumpMode sslBumpNeed() const { return sslBumpNeed_; }
/// returns true if and only if the request needs to be bumped
- bool sslBumpNeeded() const { return sslBumpNeed_ == Ssl::bumpServerFirst || sslBumpNeed_ == Ssl::bumpClientFirst || sslBumpNeed_ == Ssl::bumpPeekAndSplice; }
+ bool sslBumpNeeded() const { return sslBumpNeed_ == Ssl::bumpServerFirst || sslBumpNeed_ == Ssl::bumpClientFirst || sslBumpNeed_ == Ssl::bumpBump || sslBumpNeed_ == Ssl::bumpPeek || sslBumpNeed_ == Ssl::bumpStare; }
/// set the sslBumpNeeded state
void sslBumpNeed(Ssl::BumpMode mode);
void sslBumpStart();
if (peer->sslSession)
SSL_set_session(ssl, peer->sslSession);
- } else if (request->clientConnectionManager->sslBumpMode == Ssl::bumpPeekAndSplice) {
+ } else if (request->clientConnectionManager->sslBumpMode == Ssl::bumpPeek || request->clientConnectionManager->sslBumpMode == Ssl::bumpStare) {
SSL *clientSsl = fd_table[request->clientConnectionManager->clientConnection->fd].ssl;
BIO *b = SSL_get_rbio(clientSsl);
Ssl::ClientBio *clnBio = static_cast<Ssl::ClientBio *>(b->ptr);
if (features.compressMethod == 0)
SSL_set_options(ssl, SSL_OP_NO_COMPRESSION);
#endif
+ // Should we allow it for all protocols?
if (features.sslVersion >= 3) {
b = SSL_get_rbio(ssl);
Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
srvBio->setClientFeatures(features);
srvBio->recordInput(true);
+ srvBio->mode(request->clientConnectionManager->sslBumpMode);
}
}
} else {
bool
Ssl::PeerConnector::checkForPeekAndSplice(bool checkDone, Ssl::BumpMode peekMode)
{
+ // Mark Step3 of bumping
+ if (request->clientConnectionManager.valid()) {
+ if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
+ serverBump->step = Ssl::bumpStep3;
+ }
+ }
+
+ if (!checkDone) {
+ ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(
+ ::Config.accessList.ssl_bump,
+ request.getRaw(), NULL);
+ acl_checklist->nonBlockingCheck(Ssl::PeerConnector::cbCheckForPeekAndSplice, this);
+ return false;
+ }
+
SSL *ssl = fd_table[serverConn->fd].ssl;
BIO *b = SSL_get_rbio(ssl);
Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
debugs(83,5, "Will check for peek and splice on fd " << serverConn->fd);
- bool splice;
- if (!srvBio->canSplice())
- splice = false;
- else if (!checkDone) {
- ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(
- ::Config.accessList.ssl_bump_peeked,
- request.getRaw(), NULL);
- acl_checklist->nonBlockingCheck(Ssl::PeerConnector::cbCheckForPeekAndSplice, this);
- return false;
- } else
- splice = (peekMode == Ssl::bumpSplice);
- if (!splice) {
+ // bump, peek, stare, server-first,client-first are all mean bump the connection
+ if (peekMode < Ssl::bumpSplice)
+ peekMode = Ssl::bumpBump;
+
+#if 1 || SSL_BUMP_FORCE_PEEK_OR_SPLICE
+ if (peekMode == Ssl::bumpSplice && !srvBio->canSplice())
+ peekMode = Ssl::bumpPeek;
+ else if (peekMode == Ssl::bumpBump && !srvBio->canBump())
+ peekMode = Ssl::bumpSplice;
+#endif
+
+ if (peekMode == Ssl::bumpTerminate || peekMode == Ssl::bumpErr) {
+ comm_close(serverConn->fd);
+ comm_close(clientConn->fd);
+ } else if (peekMode != Ssl::bumpSplice) {
//Allow write, proceed with the connection
srvBio->holdWrite(false);
srvBio->recordInput(false);
switchToTunnel(request.getRaw(), &status_code, clientConn, serverConn);
return false;
}
+ return false;
}
void
return;
case SSL_ERROR_WANT_WRITE:
- if (request->clientConnectionManager->sslBumpMode == Ssl::bumpPeekAndSplice && srvBio->holdWrite()) {
+ if ((request->clientConnectionManager->sslBumpMode == Ssl::bumpPeek || request->clientConnectionManager->sslBumpMode == Ssl::bumpStare) && srvBio->holdWrite()) {
debugs(81, DBG_IMPORTANT, "hold write on SSL connection on FD " << fd);
checkForPeekAndSplice(false, Ssl::bumpNone);
return;
// If the checklist decision is do not splice a new error will
// occure in the next SSL_connect call, and we will fail again.
#if 1
- if (request->clientConnectionManager->sslBumpMode == Ssl::bumpPeekAndSplice && srvBio->holdWrite()) {
+ if ((request->clientConnectionManager->sslBumpMode == Ssl::bumpPeek || request->clientConnectionManager->sslBumpMode == Ssl::bumpStare) && srvBio->holdWrite()) {
debugs(81, DBG_IMPORTANT, "fwdNegotiateSSL: Error but, hold write on SSL connection on FD " << fd);
checkForPeekAndSplice(false, Ssl::bumpNone);
return;
Ssl::ServerBump::ServerBump(HttpRequest *fakeRequest, StoreEntry *e, Ssl::BumpMode md):
request(fakeRequest),
sslErrors(NULL),
- mode(md)
+ mode(md),
+ step(bumpStep1)
{
debugs(33, 4, HERE << "will peek at " << request->GetHost() << ':' << request->port);
const char *uri = urlCanonical(request.getRaw());
Ssl::X509_Pointer serverCert; ///< HTTPS server certificate
Ssl::CertErrors *sslErrors; ///< SSL [certificate validation] errors
Ssl::BumpMode mode; ///< The SSL server bump mode
+ Ssl::BumpStep step; ///< The SSL server bumping step
private:
store_client *sc; ///< dummy client to prevent entry trimming
}
bool
-adjustSSL(SSL *ssl, Ssl::Bio::sslFeatures &features)
+adjustSSL(SSL *ssl, Ssl::Bio::sslFeatures &features, bool force)
{
+ bool fail = false;
// If the client supports compression but our context does not support
// we can not adjust.
if (features.compressMethod && ssl->ctx->comp_methods == NULL) {
debugs(83, 5, "Client Hello Data supports compression, but we do not!");
- return false;
+ fail = true;
}
//Check ciphers list
}
if (!found) {
debugs(83, 5, "Client Hello Data supports cipher '"<< cipher <<"' but we do not support it!");
- return false;
+ fail = true;
}
}
#if !defined(SSL_TLSEXT_HB_ENABLED)
if (features.doHeartBeats) {
debugs(83, 5, "Client Hello Data supports HeartBeats but we do not support!");
- return false;
+ fail = true;
}
#endif
}
if (!found) {
debugs(83, 5, "Extension " << *it << " does not supported!");
- return false;
+ fail = true ;
}
}
+ if (fail && !force)
+ return false;
- debugs(83, 5, "Hello Data are OK and can be mimicked!");
+ if (fail) {
+ debugs(83, 5, "Hello Data are OK but can not be bumped any more!");
+ }
+
+ debugs(83, 5, "Hello Data will be mimicked!");
//Adjust ssl structure data.
ssl->init_num = mainHelloSize;
ssl->s3->wpend_ret = mainHelloSize;
ssl->s3->wpend_tot = mainHelloSize;
- return true;
+ return !fail;
}
int
return -1;
}
- if (!helloBuild) {
+ if (!helloBuild && (bumpMode_ == Ssl::bumpPeek || bumpMode_ == Ssl::bumpStare)) {
if (helloMsg.isNull())
helloMsg.init(1024, 16384);
SSL *ssl = fd_table[fd_].ssl;
if (featuresSet && ssl && ssl->s3) {
- if (adjustSSL(ssl, clientFeatures)) {
+ if (bumpMode_ == Ssl::bumpPeek) {
+ if (adjustSSL(ssl, clientFeatures, true))
+ allowBump = true;
allowSplice = true;
- helloMsg.append(clientFeatures.helloMessage.content(), clientFeatures.helloMessage.contentSize());
- debugs(83, 7, "SSL HELLO message for FD " << fd_ << ": Random number is adjusted");
+ } else { /*Ssl::bumpStare*/
+ allowBump = true;
+ if (adjustSSL(ssl, clientFeatures, false))
+ allowSplice = true;
}
+ helloMsg.append(clientFeatures.helloMessage.content(), clientFeatures.helloMessage.contentSize());
+ debugs(83, 7, "SSL HELLO message for FD " << fd_ << ": Random number is adjusted");
}
}
// If we do not build any hello message, copy the current
helloBuild = true;
helloMsgSize = helloMsg.contentSize();
+ //allowBump = true;
if (allowSplice) {
// Do not write yet.....
MemBuf helloMessage;
};
explicit Bio(const int anFd);
- ~Bio();
+ virtual ~Bio();
/// Writes the given data to socket
virtual int write(const char *buf, int size, BIO *table);
/// Tells ssl connection to use BIO and monitor state via stateChanged()
static void Link(SSL *ssl, BIO *bio);
+ const MemBuf &rBufData() {return rbuf;}
protected:
const int fd_; ///< the SSL socket we are reading and writing
+ MemBuf rbuf; ///< Used to buffer input data.
};
/// BIO node to handle socket IO for squid client side
Bio::sslFeatures features;
bool holdRead_; ///< The read hold state of the bio.
bool holdWrite_; ///< The write hold state of the bio.
- MemBuf rbuf; ///< Used to buffer input data.
int headerState;
int headerBytes;
};
/// BIO node to handle socket IO for squid server side
class ServerBio: public Bio {
public:
- explicit ServerBio(const int anFd): Bio(anFd), featuresSet(false), helloMsgSize(0), helloBuild(false), allowSplice(false), holdWrite_(false), record_(false) {}
+ explicit ServerBio(const int anFd): Bio(anFd), featuresSet(false), helloMsgSize(0), helloBuild(false), allowSplice(false), allowBump(false), holdWrite_(false), record_(false) {}
/// 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
bool holdWrite() const {return holdWrite_;}
void holdWrite(bool h) {holdWrite_ = h;}
void recordInput(bool r) {record_ = r;}
- const MemBuf &rBufData() {return rbuf;}
bool canSplice() {return allowSplice;}
+ bool canBump() {return allowBump;}
+ void mode(Ssl::BumpMode m) {bumpMode_ = m;}
private:
/// A random number to use as "client random" in client hello message
sslFeatures clientFeatures;
int helloMsgSize;
bool helloBuild; ///< True if the client hello message sent to the server
bool allowSplice;
+ bool allowBump;
bool holdWrite_; ///< The write hold state of the bio.
bool record_;
- MemBuf rbuf; ///< Used to buffer input data.
+ Ssl::BumpMode bumpMode_;
};
inline
"none",
"client-first",
"server-first",
- "peek-and-splice",
+ "peek",
+ "stare",
+ "bump",
"splice",
+ "terminate",
+ "err",
NULL
};
\ingroup ServerProtocolSSLAPI
* Supported ssl-bump modes
*/
-enum BumpMode {bumpNone = 0, bumpClientFirst, bumpServerFirst, bumpPeekAndSplice, bumpSplice, bumpEnd};
+enum BumpMode {bumpNone = 0, bumpClientFirst, bumpServerFirst, bumpPeek, bumpStare, bumpBump, bumpSplice, bumpTerminate, bumpErr, bumpEnd};
+
+enum BumpStep {bumpStep1, bumpStep2, bumpStep3};
/**
\ingroup ServerProtocolSSLAPI
#if USE_OPENSSL
#include "ssl/bio.h"
#include "ssl/PeerConnector.h"
+#include "ssl/ServerBump.h"
#endif
#include "tools.h"
#if USE_DELAY_POOLS
/// Whether the client sent a CONNECT request to us.
bool clientExpectsConnectResponse() const {
+#if USE_OPENSSL
+ // We are bumping and we had already send "OK CONNECTED"
+ if (http.valid() && http->getConn() && http->getConn()->serverBump() && http->getConn()->serverBump()->step > Ssl::bumpStep1)
+ return false;
+#endif
return !(request != NULL &&
(request->flags.interceptTproxy || request->flags.intercepted));
}
fd_table[srvConn->fd].read_method = &default_read_method;
fd_table[srvConn->fd].write_method = &default_write_method;
- SSL *ssl = fd_table[srvConn->fd].ssl;
- assert(ssl);
- BIO *b = SSL_get_rbio(ssl);
- Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
- const MemBuf &buf = srvBio->rBufData();
+ ConnStateData *conn;
+ if ((conn = request->pinnedConnection()) && conn->serverBump() && conn->serverBump()->step == Ssl::bumpStep2) {
+ SSL *ssl = fd_table[clientConn->fd].ssl;
+ assert(ssl);
+ BIO *b = SSL_get_rbio(ssl);
+ Ssl::ClientBio *srvBio = static_cast<Ssl::ClientBio *>(b->ptr);
+ const MemBuf &buf = srvBio->rBufData();
- AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone",
- CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
- Comm::Write(tunnelState->client.conn, buf.content(), buf.contentSize(), call, NULL);
+ AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone",
+ CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
+ Comm::Write(tunnelState->server.conn, buf.content(), buf.contentSize(), call, NULL);
+ } else {
+ SSL *ssl = fd_table[srvConn->fd].ssl;
+ assert(ssl);
+ BIO *b = SSL_get_rbio(ssl);
+ Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+ const MemBuf &buf = srvBio->rBufData();
+
+ AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone",
+ CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
+ Comm::Write(tunnelState->client.conn, buf.content(), buf.contentSize(), call, NULL);
+ }
}