]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Peek and Splice: New ACL lists configuration
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Thu, 22 May 2014 12:00:50 +0000 (15:00 +0300)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Thu, 22 May 2014 12:00:50 +0000 (15:00 +0300)
This patch:
  - removes ssl_bump_peeked access list

  - Defines three steps of the SSL bumping processing:
       step1: Get TCP-level and CONNECT info. Evaluate ssl_bump and perform
              the first matching action (splice, bump, peek, stare, terminate,
              or err)
       step2: Get SSL Client Hello info. Evaluate ssl_bump and perform the
              first matching action (splice, bump, peek, stare, terminate,
              or err). Peeking usually prevents future bumping. Staring
              usually prevents future splicing.
       step3: Get SSL Server Hello info. Evaluate ssl_bump and perform the
              first matching action (splice, bump, terminate, or err).
              In most cases, the only remaining choice at this step is
              whether to terminate the connection. The splicing or bumping
              decision is usually dictated by either peeking or staring at the
              previous step.

  - The ssl_bump ACLs list may evaluated in all SSL Bumping processing steps to
    take a decision for the next step:
       splice or none: 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.
       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.
       terminate or err: Close client and server connections.
    All actions except peek and stare correspond to final decisions: Once an
    ssl_bump directive with a final action matches, no further ssl_bump
    evaluations will take place, regardless of the current processing step.

  - Add the atstep acl to match against SSL bumping step: "step1", "step2" or
    "step3"

Current Implementation details:
---------------------------------

 1) If the "peek" mode selected in step2 then the client hello message
    forwarded to server. If this mode selected in step2 the splice is always
    possible and bump maybe is not possible (in most cases where the client uses
    different SSL client library implementation)

 2) If the "stare" mode selected in step2 then the squid builds a new
    hello message, which try to mimic, if it is possible , client hello message.
    If stare selected in step2 the bump is always possible, but splice maybe is
    not possible any more.

 3) In step3 if bump decided, and bump is not possible any more then squid
    is always splicing.

 4) In step3 if splice decided but splice is not possible any more then
    squid is always bumping.

 5) Because of (3) and (4), in practice, if firefox browser used with
    peek mode, squid always splice the connection, because squid/openSSL
    does not support the firefox SSL features reported in client hello message.

 6) In step2 if ACL list evaluation result to terminate or err then we just
    close client connection. If the check result to ssl-bump then just bump.
    If check result to client-first, server-first, then bump the connection
    else do peek/stare.

 7) In step3 the ssl_bump ACL list evakuation result client-first, server-first,
    bump or peek result to bumping (if bumping is possible).

Example configurations:

acl step1 atstep  Step1
acl step2 atstep  Step2
acl step3 atstep  Step3

ssl_bump peek step1 all
ssl_bump peek step2 all
ssl_bump splice step3 all

19 files changed:
src/AclRegs.cc
src/SquidConfig.h
src/acl/AtBumpStep.cc [new file with mode: 0644]
src/acl/AtBumpStep.h [new file with mode: 0644]
src/acl/AtBumpStepData.cc [new file with mode: 0644]
src/acl/AtBumpStepData.h [new file with mode: 0644]
src/acl/Makefile.am
src/cache_cf.cc
src/cf.data.pre
src/client_side.cc
src/client_side_request.h
src/ssl/PeerConnector.cc
src/ssl/ServerBump.cc
src/ssl/ServerBump.h
src/ssl/bio.cc
src/ssl/bio.h
src/ssl/support.cc
src/ssl/support.h
src/tunnel.cc

index b0700b939a0b7d2bbeb5c5fa295abe74172a3c89..3a7cd16f5e211b75e3c85b4b3127762332f1dbb1 100644 (file)
 #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"
@@ -160,6 +164,9 @@ ACL::Prototype ACLCertificate::CARegistryProtoype(&ACLCertificate::CARegistryEnt
 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
index 30a6d53f105e999d1871f64b30ef1ce361bcab12..97d09a31002ec3c081b6d5fd44b91ef2403f8a24 100644 (file)
@@ -390,7 +390,6 @@ public:
 
 #if USE_OPENSSL
         acl_access *ssl_bump;
-        acl_access *ssl_bump_peeked;
 #endif
 #if FOLLOW_X_FORWARDED_FOR
         acl_access *followXFF;
diff --git a/src/acl/AtBumpStep.cc b/src/acl/AtBumpStep.cc
new file mode 100644 (file)
index 0000000..0c85291
--- /dev/null
@@ -0,0 +1,27 @@
+#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_;
diff --git a/src/acl/AtBumpStep.h b/src/acl/AtBumpStep.h
new file mode 100644 (file)
index 0000000..26385a6
--- /dev/null
@@ -0,0 +1,33 @@
+#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 */
diff --git a/src/acl/AtBumpStepData.cc b/src/acl/AtBumpStepData.cc
new file mode 100644 (file)
index 0000000..4aadb32
--- /dev/null
@@ -0,0 +1,69 @@
+#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);
+}
diff --git a/src/acl/AtBumpStepData.h b/src/acl/AtBumpStepData.h
new file mode 100644 (file)
index 0000000..2effeb2
--- /dev/null
@@ -0,0 +1,30 @@
+#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 */
index 03bb6cb7b5631438310d8795567953a3cac8183b..e5051b35dc390e127e1e984451e6a7ec9ba2fc80 100644 (file)
@@ -31,6 +31,10 @@ libstate_la_SOURCES = \
 
 ## data-specific ACLs
 libacls_la_SOURCES = \
+       AtBumpStep.cc \
+       AtBumpStep.h \
+       AtBumpStepData.cc \
+       AtBumpStepData.h \
        IntRange.cc \
        IntRange.h \
        RegexData.cc \
index dd70b946db6215e85196232e4f10ffeff6938243..3edc4ab3ac6f156b037079e3dc8d62acab1022a9 100644 (file)
@@ -243,9 +243,6 @@ static void parse_sslproxy_ssl_bump(acl_access **ssl_bump);
 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);
@@ -4672,8 +4669,23 @@ static void parse_sslproxy_ssl_bump(acl_access **ssl_bump)
     } 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;
@@ -4733,53 +4745,6 @@ static void free_sslproxy_ssl_bump(acl_access **ssl_bump)
     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)
index 191c5fe319033848a60127d7974b1493cf4a351a..f5306810ec13b1dc7c0bfa97bfd4db11fee0ebc7 100644 (file)
@@ -1148,6 +1148,12 @@ IF USE_OPENSSL
          # 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]
@@ -2445,6 +2451,30 @@ DOC_START
        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
@@ -2491,29 +2521,6 @@ DOC_START
        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
index 4831e42b6780f4fa52b3b290c8615c481654e5e4..8630c4bdadbb3cc9a759fbc2623497c43668268b 100644 (file)
@@ -3621,7 +3621,7 @@ httpsSslBumpAccessCheckDone(allow_t answer, void *data)
 
     // 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);
@@ -3727,7 +3727,7 @@ ConnStateData::sslCrtdHandleReply(const HelperReply &reply)
                 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);
@@ -3844,7 +3844,7 @@ ConnStateData::getSslContextStart()
         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;
@@ -3884,7 +3884,7 @@ ConnStateData::getSslContextStart()
 #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))
@@ -3976,9 +3976,9 @@ ConnStateData::switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode)
         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;        
     }
@@ -4033,9 +4033,81 @@ void ConnStateData::startPeekAndSplice()
     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());
 }
 
index ce5c2720f898bab7f13c00dd6bc1aa3005db1250..5315e94d229d169bc641dbd2e94ca48bd50c5ec7 100644 (file)
@@ -150,7 +150,7 @@ public:
     /// 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();
index 8103198774e7f4bdd5395158513f3fa645bd0afb..339d127a311a2b2b98c9c7f5f38ccb3c8c413c34 100644 (file)
@@ -136,7 +136,7 @@ Ssl::PeerConnector::initializeSsl()
         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);
@@ -151,11 +151,13 @@ Ssl::PeerConnector::initializeSsl()
             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 {
@@ -277,23 +279,41 @@ Ssl::PeerConnector::cbCheckForPeekAndSplice(allow_t answer, void *data)
 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);
@@ -306,6 +326,7 @@ Ssl::PeerConnector::checkForPeekAndSplice(bool checkDone, Ssl::BumpMode peekMode
         switchToTunnel(request.getRaw(), &status_code, clientConn, serverConn);
         return false;
     }
+    return false;
 }
 
 void
@@ -455,7 +476,7 @@ Ssl::PeerConnector::handleNegotiateError(const int ret)
             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;
@@ -473,7 +494,7 @@ Ssl::PeerConnector::handleNegotiateError(const int ret)
             // 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;
index 3bcd33864175aaa356c0faf99267ab005f048d33..1485d0ffb02d49a8076883e6acc1c0f8bcf9444c 100644 (file)
@@ -17,7 +17,8 @@ CBDATA_NAMESPACED_CLASS_INIT(Ssl, ServerBump);
 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());
index 12c7185d2933c58f9c76170cc6c8fceaa905ab64..cdc94a722bb388a1a91ab5033e5ef35c1d63676a 100644 (file)
@@ -29,6 +29,7 @@ public:
     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
index 80039d018fe12c19388b278b108200b3e511f5c3..58c58a7cd3daa357243127801d0dcbd126bd8c61 100644 (file)
@@ -306,13 +306,14 @@ Ssl::ServerBio::read(char *buf, int size, BIO *table)
 }
 
 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
@@ -335,14 +336,14 @@ adjustSSL(SSL *ssl, Ssl::Bio::sslFeatures &features)
       }
       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
 
@@ -387,12 +388,18 @@ adjustSSL(SSL *ssl, Ssl::Bio::sslFeatures &features)
         }
         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.
 
@@ -412,7 +419,7 @@ adjustSSL(SSL *ssl, Ssl::Bio::sslFeatures &features)
     ssl->init_num = mainHelloSize;
     ssl->s3->wpend_ret = mainHelloSize;
     ssl->s3->wpend_tot = mainHelloSize;
-    return true;
+    return !fail;
 }
 
 int
@@ -425,7 +432,7 @@ Ssl::ServerBio::write(const char *buf, int size, BIO *table)
         return -1;
     }
 
-    if (!helloBuild) {
+    if (!helloBuild && (bumpMode_ == Ssl::bumpPeek || bumpMode_ == Ssl::bumpStare)) {
         if (helloMsg.isNull())
             helloMsg.init(1024, 16384);
 
@@ -439,11 +446,17 @@ Ssl::ServerBio::write(const char *buf, int size, BIO *table)
 
             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
@@ -452,6 +465,7 @@ Ssl::ServerBio::write(const char *buf, int size, BIO *table)
 
         helloBuild = true;
         helloMsgSize = helloMsg.contentSize();
+        //allowBump = true;
 
         if (allowSplice) {
         // Do not write yet.....
index eb8f08cd85faadf8a7c09566a59d0c15f7fe3ebd..b6d2fe9972da896f05a9fff26024ff5570ca65de 100644 (file)
@@ -49,7 +49,7 @@ public:
         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);
@@ -73,8 +73,10 @@ public:
     /// 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
@@ -106,7 +108,6 @@ private:
     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;
 };
@@ -114,7 +115,7 @@ private:
 /// 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
@@ -134,8 +135,9 @@ public:
     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;
@@ -144,9 +146,10 @@ private:
     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
index 39547235361bbc93ceee2d9633df3c376812ee11..7668c36a6b84ba1b8b6940de42a1728b46444ecd 100644 (file)
@@ -65,8 +65,12 @@ const char *Ssl::BumpModeStr[] = {
     "none",
     "client-first",
     "server-first",
-    "peek-and-splice",
+    "peek",
+    "stare",
+    "bump",
     "splice",
+    "terminate",
+    "err",
     NULL
 };
 
index 45af1afa7f3bbce7f7ced00224a0c9db250154dc..33bb0d00902f04fb84cf5670a7ea4f14389d311f 100644 (file)
@@ -158,7 +158,9 @@ GETX509ATTRIBUTE GetX509Fingerprint;
   \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
index d8a4d8f4e29455a92e586744aa745865603fa691..519d1f927e0727b87621e362d6d6e1460f7ec103 100644 (file)
@@ -56,6 +56,7 @@
 #if USE_OPENSSL
 #include "ssl/bio.h"
 #include "ssl/PeerConnector.h"
+#include "ssl/ServerBump.h"
 #endif
 #include "tools.h"
 #if USE_DELAY_POOLS
@@ -119,6 +120,11 @@ public:
 
     /// 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));
     }
@@ -1153,13 +1159,26 @@ switchToTunnel(HttpRequest *request, int *status_ptr, Comm::ConnectionPointer &c
     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);
+    }
 }