AsyncCall::Pointer callback = asyncCall(17,4,
"FwdState::ConnectedToPeer",
FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this));
+ // Use positive timeout when less than one second is left.
+ const time_t sslNegotiationTimeout = max(static_cast<time_t>(1), timeLeft());
Ssl::PeerConnector *connector =
- new Ssl::PeerConnector(requestPointer, serverConnection(), clientConn, callback);
- new Ssl::PeerConnector(requestPointer, serverConnection(), callback, sslNegotiationTimeout);
++ new Ssl::PeerConnector(requestPointer, serverConnection(), clientConn, callback, sslNegotiationTimeout);
AsyncJob::Start(connector); // will call our callback
return;
}
securer = asyncCall(48, 4, "PeerPoolMgr::handleSecuredPeer",
MyAnswerDialer(this, &PeerPoolMgr::handleSecuredPeer));
+
+ const int peerTimeout = peer->connect_timeout > 0 ?
+ peer->connect_timeout : Config.Timeout.peer_connect;
+ const int timeUsed = squid_curtime - params.conn->startTime();
+ // Use positive timeout when less than one second is left for conn.
+ const int timeLeft = max(1, (peerTimeout - timeUsed));
Ssl::PeerConnector *connector =
- new Ssl::PeerConnector(request, params.conn, NULL, securer);
- new Ssl::PeerConnector(request, params.conn, securer, timeLeft);
++ new Ssl::PeerConnector(request, params.conn, NULL, securer, timeLeft);
AsyncJob::Start(connector); // will call our callback
return;
}
%USER_CERT SSL User certificate in PEM format
%USER_CERTCHAIN SSL User certificate chain in PEM format
%USER_CERT_xx SSL User certificate subject attribute xx
- %USER_CA_xx SSL User certificate issuer attribute xx
+ %USER_CA_CERT_xx SSL User certificate issuer attribute xx
+ %ssl::>sni SSL client SNI sent to Squid
+ %ssl::<cert_subject SSL server certificate DN
+ %ssl::<cert_issuer SSL server certificate issuer DN
%>{Header} HTTP request header "Header"
%>{Hdr:member}
getSslContextStart();
}
- bool ret = connState->handleReadData(&connState->in.buf);
+/** negotiate an SSL connection */
+static void
+clientPeekAndSpliceSSL(int fd, void *data)
+{
+ ConnStateData *conn = (ConnStateData *)data;
+ SSL *ssl = fd_table[fd].ssl;
+
+ debugs(83, 2, "Start peek and splice on " << fd);
+
+ if (!Squid_SSL_accept(conn, clientPeekAndSpliceSSL))
+ debugs(83, 2, "SSL_accept failed.");
+
+ BIO *b = SSL_get_rbio(ssl);
+ assert(b);
+ Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
+ if (bio->gotHello()) {
+ if (conn->serverBump()) {
+ Ssl::Bio::sslFeatures const &features = bio->getFeatures();
+ if (!features.serverName.empty())
+ conn->serverBump()->clientSni = features.serverName.c_str();
+ }
+
+ debugs(83, 2, "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
+ SSL_CTX *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.
+
+ // Disable the client read handler until CachePeer selection is complete
+ Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
+ Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientPeekAndSpliceSSL, this, 0);
+ switchedToHttps_ = true;
+
+ SSL *ssl = fd_table[clientConnection->fd].ssl;
+ BIO *b = SSL_get_rbio(ssl);
+ Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
+ 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;
+ fd_table[connState->clientConnection->fd].read_method = &default_read_method;
+ fd_table[connState->clientConnection->fd].write_method = &default_write_method;
+
+ if (connState->transparent()) {
+ // 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();
+ 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();
+ }
+ } else {
+ // in.buf still has the "CONNECT ..." request data, reset it to SSL hello message
+ connState->in.buf.append(rbuf.content(), rbuf.contentSize());
+ 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());
+}
+
+void
+ConnStateData::doPeekAndSpliceStep()
+{
+ SSL *ssl = fd_table[clientConnection->fd].ssl;
+ BIO *b = SSL_get_rbio(ssl);
+ assert(b);
+ Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
+
+ debugs(33, 5, HERE << "PeekAndSplice mode, proceed with client negotiation. Currrent state:" << SSL_state_string_long(ssl));
+ bio->hold(false);
+
+ Comm::SetSelect(clientConnection->fd, COMM_SELECT_WRITE, clientNegotiateSSL, this, 0);
+ switchedToHttps_ = true;
+}
+
void
ConnStateData::httpsPeeked(Comm::ConnectionPointer serverConnection)
{
else if (strncmp(token, "%USER_CERT_", 11) == 0) {
format->type = Format::LFT_EXT_ACL_USER_CERT;
format->header = xstrdup(token + 11);
- } else if (strncmp(token, "%USER_CA_CERT_", 11) == 0) {
+ } else if (strncmp(token, "%USER_CA_CERT_", 14) == 0) {
format->type = Format::LFT_EXT_ACL_USER_CA_CERT;
- format->header = xstrdup(token + 11);
- } else if (strncmp(token, "%CA_CERT_", 11) == 0) {
+ format->header = xstrdup(token + 14);
+ } else if (strncmp(token, "%CA_CERT_", 9) == 0) {
debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type %CA_CERT_* code is obsolete. Use %USER_CA_CERT_* instead");
format->type = Format::LFT_EXT_ACL_USER_CA_CERT;
- format->header = xstrdup(token + 11);
+ format->header = xstrdup(token + 9);
- }
+ } else if (strcmp(token, "%ssl::>sni") == 0)
+ format->type = Format::LFT_SSL_CLIENT_SNI;
+ else if (strcmp(token, "%ssl::<cert_subject") == 0)
+ format->type = Format::LFT_SSL_SERVER_CERT_SUBJECT;
+ else if (strcmp(token, "%ssl::<cert_issuer") == 0)
+ format->type = Format::LFT_SSL_SERVER_CERT_ISSUER;
#endif
#if USE_AUTH
else if (strcmp(token, "%EXT_USER") == 0 || strcmp(token, "%ue") == 0)
Ssl::PeerConnector::PeerConnector(
HttpRequestPointer &aRequest,
const Comm::ConnectionPointer &aServerConn,
- AsyncCall::Pointer &aCallback):
+ const Comm::ConnectionPointer &aClientConn,
+ AsyncCall::Pointer &aCallback,
+ const time_t timeout):
AsyncJob("Ssl::PeerConnector"),
request(aRequest),
serverConn(aServerConn),
- callback(aCallback)
+ clientConn(aClientConn),
+ callback(aCallback),
+ negotiationTimeout(timeout),
+ startTime(squid_curtime)
{
// if this throws, the caller's cb dialer is not our CbDialer
Must(dynamic_cast<CbDialer*>(callback->getDialer()));
CRYPTO_add(&(peeked_cert->references),1,CRYPTO_LOCK_X509);
SSL_set_ex_data(ssl, ssl_ex_index_ssl_peeked_cert, peeked_cert);
}
-
- fd_table[fd].ssl = ssl;
- fd_table[fd].read_method = &ssl_read_method;
- fd_table[fd].write_method = &ssl_write_method;
}
+ void
+ Ssl::PeerConnector::setReadTimeout()
+ {
+ int timeToRead;
+ if (negotiationTimeout) {
+ const int timeUsed = squid_curtime - startTime;
+ const int timeLeft = max(0, static_cast<int>(negotiationTimeout - timeUsed));
+ timeToRead = min(static_cast<int>(::Config.Timeout.read), timeLeft);
+ } else
+ timeToRead = ::Config.Timeout.read;
+ AsyncCall::Pointer nil;
+ commSetConnTimeout(serverConnection(), timeToRead, nil);
+ }
+
void
Ssl::PeerConnector::negotiateSsl()
{
public:
PeerConnector(HttpRequestPointer &aRequest,
const Comm::ConnectionPointer &aServerConn,
- AsyncCall::Pointer &aCallback);
+ const Comm::ConnectionPointer &aClientConn,
+ AsyncCall::Pointer &aCallback, const time_t timeout = 0);
virtual ~PeerConnector();
protected:
/// A wrapper function for negotiateSsl for use with Comm::SetSelect
static void NegotiateSsl(int fd, void *data);
+ /// A wrapper function for checkForPeekAndSplice for use with acl
+ static void cbCheckForPeekAndSplice(allow_t answer, void *data);
+
HttpRequestPointer request; ///< peer connection trigger or cause
Comm::ConnectionPointer serverConn; ///< TCP connection to the peer
+ 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
+ time_t negotiationTimeout; ///< the ssl connection timeout to use
+ time_t startTime; ///< when the peer connector negotiation started
CBDATA_CLASS2(PeerConnector);
};