]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Re-enabled support for bump-client-first mode using enhanced ssl_bump option.
authorAlex Rousskov <rousskov@measurement-factory.com>
Fri, 4 May 2012 22:21:44 +0000 (16:21 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Fri, 4 May 2012 22:21:44 +0000 (16:21 -0600)
Even though bump-server-first is an overall better method, bumping the client
first is useful for backward compatibility and possibly for serving internal
Squid objects such as icons.

The code path implementing bump-client-first approach was preserved during the
bump-server-first changes, so we just needed to add a configuration option to
allow the admin to pick between two modes. We did that by using custom "mode"
keywords with the existing ssl_bump option. The old allow/deny pair of
standard keywords could not be used to select one of the two modes for an
"allowed" connection.

src/cache_cf.cc
src/cf.data.depend
src/cf.data.pre
src/client_side.cc
src/client_side.h
src/client_side_request.cc
src/client_side_request.h
src/ssl/support.cc
src/ssl/support.h

index 49f09077d86c94fe05960b4d375d16acd9206fd5..a9781c4765f73de0685b38a7a0f26aded66536bd 100644 (file)
@@ -206,6 +206,9 @@ static void free_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign);
 static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt);
 static void dump_sslproxy_cert_adapt(StoreEntry *entry, const char *name, sslproxy_cert_adapt *cert_adapt);
 static void free_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt);
+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);
 #endif /* USE_SSL */
 
 static void parse_b_size_t(size_t * var);
@@ -4593,4 +4596,58 @@ static void free_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign)
     }
 }
 
+static void parse_sslproxy_ssl_bump(acl_access **ssl_bump)
+{
+    char *bm;
+    if ((bm = strtok(NULL, w_space)) == NULL) {
+        self_destruct();
+        return;
+    }
+
+    acl_access *A = new acl_access;
+    A->allow = allow_t(ACCESS_ALLOWED);
+
+    if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpClientFirst]) == 0)
+        A->allow.kind = Ssl::bumpClientFirst;
+    else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0)
+        A->allow.kind = Ssl::bumpServerFirst;
+    else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpNone]) == 0)
+        A->allow.kind = Ssl::bumpNone;
+    else if (strcmp(bm, "allow") == 0 || strcmp(bm, "deny") == 0) {
+        // allow/deny rule sets may rely on an implicit "negate the last one"
+        // rule which we cannot support due to multuple "allow" keywords
+        debugs(3, DBG_CRITICAL, "FATAL: ssl_bump allow/deny rule(s) " <<
+               "must be CAREFULLY converted to specify bump mode(s).");
+        self_destruct();
+        return;
+    } else {
+        debugs(3, DBG_CRITICAL, "FATAL: unknown ssl_bump mode: " << bm);
+        self_destruct();
+        return;
+    }
+
+    aclParseAclList(LegacyParser, &A->aclList);
+
+    acl_access *B, **T;
+    for (B = *ssl_bump, T = ssl_bump; B; T = &B->next, B = B->next);
+    *T = A;
+}
+
+static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump)
+{
+    acl_access *sb;
+    for (sb = ssl_bump; sb != NULL; sb = sb->next) {
+        storeAppendPrintf(entry, "%s ", name);
+        storeAppendPrintf(entry, "%s ", Ssl::bumpMode(sb->allow.kind));
+        if (sb->aclList)
+            dump_acl_list(entry, sb->aclList);
+        storeAppendPrintf(entry, "\n");
+    }
+}
+
+static void free_sslproxy_ssl_bump(acl_access **ssl_bump)
+{
+    free_acl_access(ssl_bump);
+}
+
 #endif
index 6250d0bf6e62885825aab0da34361530b0124834..779ae02e2e4ece7cd1fabb919d4bd8351b51b2ba 100644 (file)
@@ -69,5 +69,6 @@ wccp2_amethod
 wccp2_service
 wccp2_service_info
 wordlist
+sslproxy_ssl_bump      acl
 sslproxy_cert_sign    acl
 sslproxy_cert_adapt    acl
index 6058898b167a5bf72091671a6150ddf65cec0fc1..052f7650c8d42d70eb931e35351bd040dc5e8ee5 100644 (file)
@@ -1347,14 +1347,14 @@ DOC_START
 
           accel        Accelerator / reverse proxy mode
 
-          ssl-bump     Intercept each CONNECT request matching ssl_bump ACL,
+          ssl-bump     For each CONNECT request allowed by ssl_bump ACLs,
                        establish secure connection with the client and with
-                       the server, decrypt HTTP messages as they pass through
+                       the server, decrypt HTTPS messages as they pass through
                        Squid, and treat them as unencrypted HTTP messages,
                        becoming the man-in-the-middle.
 
-                       The ssl_bump option is required to fully enable
-                       the SslBump feature.
+                       The "ssl_bump" option is required to fully enable
+                       bumping of CONNECT requests.
 
        Omitting the mode flag causes default forward proxy mode to be used.
 
@@ -1564,14 +1564,14 @@ DOC_START
                        connections using the client IP address.
                        NP: disables authentication and maybe IPv6 on the port.
 
-          ssl-bump     Intercept each SSL request matching ssl_bump ACL,
-                       establish secure connection with the client and with
-                       the server, decrypt HTTP messages as they pass through
+          ssl-bump     For each intercepted connection allowed by ssl_bump
+                   ACLs, establish a secure connection with the client and with
+                       the server, decrypt HTTPS messages as they pass through
                        Squid, and treat them as unencrypted HTTP messages,
                        becoming the man-in-the-middle.
 
-                       The ssl_bump option is required to fully enable
-                       the SslBump feature.
+                       An "ssl_bump server-first" match is required to
+                       fully enable bumping of intercepted SSL connections.
 
                        Requires tproxy.
 
@@ -2068,32 +2068,60 @@ DOC_END
 
 NAME: ssl_bump
 IFDEF: USE_SSL
-TYPE: acl_access
+TYPE: sslproxy_ssl_bump
 LOC: Config.accessList.ssl_bump
 DEFAULT: none
 DOC_START
-       This ACL controls which CONNECT requests to an http_port
-       marked with an sslBump flag are actually "bumped". Please 
-       see the sslBump flag of an http_port option for more details
-       about decoding proxied SSL connections.
+       This option is consulted when a CONNECT request is received on
+       an http_port (or a new connection is intercepted at an
+       https_port), provided that port was configured with an ssl-bump
+       flag. The subsequent data on the connection is either treated as
+       HTTPS and decrypted OR tunneled at TCP level without decryption,
+       depending on the first bumping "mode" which ACLs match.
+
+       ssl_bump <mode> [!]acl ...
+
+       The following bumping modes are supported:
+
+           client-first
+               Allow bumping of the connection. Establish a secure connection
+               with the client first, then connect to the server. This old mode
+               does not allow Squid to mimic server SSL certificate and does
+               not work with intercepted SSL connections.
 
-       By default, no requests are bumped.
+           server-first
+               Allow bumping of the connection. Establish a secure connection
+               with the server first, then establish a secure connection with
+               the client, using a mimicked server certificate. Works with both
+               CONNECT requests and intercepted SSL connections.
+
+           none
+               Become a TCP tunnel without decoding the connection.
+               Works with both CONNECT requests and intercepted SSL
+               connections. This is the default behavior when no
+               ssl_bump option is given or no ssl_bump ACLs match.
+
+       By default, no connections are bumped.
+
+       The first matching ssl_bump option wins. If no ACLs match, the
+       connection is not bumped. Unlike most allow/deny ACL lists, ssl_bump
+       does not have an implicit "negate the last given option" rule. You
+       must make that rule explicit if you convert old ssl_bump allow/deny
+       rules that rely on such an implicit rule.
 
-       See also: http_port ssl-bump
-   
        This clause supports both fast and slow acl types.
        See http://wiki.squid-cache.org/SquidFaq/SquidAcl for details.
 
+       See also: http_port ssl-bump, https_port ssl-bump
+
 
-       # Example: Bump all requests except those originating from localhost and 
-       # those going to webax.com or example.com sites.
+       # Example: Bump all requests except those originating from
+       # localhost and those going to example.com.
 
-       acl localhost src 127.0.0.1/32
-       acl broken_sites dstdomain .webax.com
        acl broken_sites dstdomain .example.com
-       ssl_bump deny localhost
-       ssl_bump deny broken_sites
-       ssl_bump allow all
+       ssl_bump none localhost
+       ssl_bump none broken_sites
+       ssl_bump server-first all
 DOC_END
 
 NAME: sslproxy_flags
index 1a6c6c50c7d581d3912f37bf0ce84e56814016cc..15a38c5a841a2574dd19052e4785a9e525f57a02 100644 (file)
@@ -3534,7 +3534,7 @@ clientNegotiateSSL(int fd, void *data)
  * Otherwise, calls switchToHttps to generate a dynamic SSL_CTX.
  */
 static void
-httpsEstablish(ConnStateData *connState,  SSL_CTX *sslContext)
+httpsEstablish(ConnStateData *connState,  SSL_CTX *sslContext, Ssl::BumpMode bumpMode)
 {
     SSL *ssl = NULL;
     assert(connState);
@@ -3552,6 +3552,7 @@ httpsEstablish(ConnStateData *connState,  SSL_CTX *sslContext)
         Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
     else {
         char buf[MAX_IPSTRLEN];
+        assert(bumpMode != Ssl::bumpNone && bumpMode != Ssl::bumpEnd);
         HttpRequest *fakeRequest = new HttpRequest;
         fakeRequest->SetHost(details->local.NtoA(buf, sizeof(buf)));
         fakeRequest->port = details->local.GetPort();
@@ -3563,7 +3564,7 @@ httpsEstablish(ConnStateData *connState,  SSL_CTX *sslContext)
         fakeRequest->my_addr = connState->clientConnection->local;
 
         debugs(33, 4, HERE << details << " try to generate a Dynamic SSL CTX");
-        connState->switchToHttps(fakeRequest);
+        connState->switchToHttps(fakeRequest, bumpMode);
     }
 }
 
@@ -3581,9 +3582,11 @@ httpsSslBumpAccessCheckDone(allow_t answer, void *data)
     if (!connState->isOpen())
         return;
 
-    if (answer == ACCESS_ALLOWED) {
+    // Require both a match and a positive 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) {
         debugs(33, 2, HERE << " sslBump done data: " << connState->clientConnection);
-        httpsEstablish(connState, NULL);
+        httpsEstablish(connState, NULL, (Ssl::BumpMode)answer.kind);
     } else {
         // fake a CONNECT request to force connState to tunnel
 
@@ -3651,7 +3654,7 @@ httpsAccept(const CommAcceptCbParams &params)
         return;
     } else {
         SSL_CTX *sslContext = s->staticSslContext.get();
-        httpsEstablish(connState, sslContext);
+        httpsEstablish(connState, sslContext, Ssl::bumpNone);
     }
 }
 
@@ -3690,8 +3693,14 @@ void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &cer
     certProperties.commonName =  sslCommonName.defined() ? sslCommonName.termedBuf() : sslConnectHostOrIp.termedBuf();
 
     // fake certificate adaptation requires bump-server-first mode
-    if (!sslServerBump)
+    if (!sslServerBump) {
+        assert(port->signingCert.get());
+        certProperties.signWithX509.resetAndLock(port->signingCert.get());
+        if (port->signPkey.get())
+            certProperties.signWithPkey.resetAndLock(port->signPkey.get());
+        certProperties.signAlgorithm = Ssl::algSignTrusted;
         return;
+    }
 
     // In the case of error while connecting to secure server, use a fake trusted certificate,
     // with no mimicked fields and no adaptation algorithms
@@ -3870,7 +3879,7 @@ ConnStateData::getSslContextDone(SSL_CTX * sslContext, bool isNew)
 }
 
 void
-ConnStateData::switchToHttps(HttpRequest *request)
+ConnStateData::switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode)
 {
     assert(!switchedToHttps_);
 
@@ -3881,11 +3890,10 @@ ConnStateData::switchToHttps(HttpRequest *request)
     flags.readMore = true;
     debugs(33, 5, HERE << "converting " << clientConnection << " to SSL");
 
-    const bool alwaysBumpServerFirst = true;
     // If sslServerBump is set, then we have decided to deny CONNECT
     // and now want to switch to SSL to send the error to the client
     // without even peeking at the origin server certificate.
-    if (alwaysBumpServerFirst && !sslServerBump) {
+    if (bumpServerMode == Ssl::bumpServerFirst && !sslServerBump) {
         request->flags.sslPeek = 1;
         sslServerBump = new Ssl::ServerBump(request);
 
index 3efb5aa593489cb7db4fb268e66c6c3e6d10f479..840755e5dd9e99128bd33d3f186cb0de177909a7 100644 (file)
@@ -340,7 +340,7 @@ public:
     /// Proccess response from ssl_crtd.
     void sslCrtdHandleReply(const char * reply);
 
-    void switchToHttps(HttpRequest *request);
+    void switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode);
     bool switchedToHttps() const { return switchedToHttps_; }
     Ssl::ServerBump *serverBump() {return sslServerBump;}
     void setServerBump(Ssl::ServerBump *srvBump) {if (!sslServerBump) sslServerBump = srvBump;}
index 75975a4f94683bd2963762695ecb4d3bb2d29899..40cab052090a932b0f356ae68118046437602dc9 100644 (file)
@@ -187,7 +187,7 @@ ClientHttpRequest::ClientHttpRequest(ConnStateData * aConn) :
     request_satisfaction_mode = false;
 #endif
 #if USE_SSL
-    sslBumpNeed = needUnknown;
+    sslBumpNeed = Ssl::bumpEnd;
 #endif
 }
 
@@ -1305,7 +1305,7 @@ ClientRequestContext::sslBumpAccessCheck()
         acl_checklist->nonBlockingCheck(sslBumpAccessCheckDoneWrapper, this);
         return true;
     } else {
-        http->sslBumpNeeded(false);
+        http->sslBumpNeeded(Ssl::bumpNone);
         return false;
     }
 }
@@ -1327,7 +1327,11 @@ ClientRequestContext::sslBumpAccessCheckDone(const allow_t &answer)
     if (!httpStateIsValid())
         return;
 
-    http->sslBumpNeeded(answer == ACCESS_ALLOWED);
+    if (answer == ACCESS_ALLOWED)
+        http->sslBumpNeeded(static_cast<Ssl::BumpMode>(answer.kind));
+    else
+        http->sslBumpNeeded(Ssl::bumpNone);
+
     http->doCallouts();
 }
 #endif
@@ -1375,18 +1379,18 @@ ClientHttpRequest::httpStart()
 
 #if USE_SSL
 
-bool
+Ssl::BumpMode
 ClientHttpRequest::sslBumpNeeded() const
 {
-    assert(sslBumpNeed != needUnknown);
-    return (sslBumpNeed == needConfirmed);
+    assert(sslBumpNeed != Ssl::bumpEnd);
+    return sslBumpNeed;
 }
 
 void
-ClientHttpRequest::sslBumpNeeded(bool isNeeded)
+ClientHttpRequest::sslBumpNeeded(Ssl::BumpMode mode)
 {
-    debugs(83, 3, HERE << "sslBump required: "<< (isNeeded ? "Yes" : "No"));
-    sslBumpNeed = (isNeeded ? needConfirmed : needNot);
+    debugs(83, 3, HERE << "sslBump required: "<< Ssl::bumpMode(mode));
+    sslBumpNeed = mode;
 }
 
 // called when comm_write has completed
@@ -1423,7 +1427,7 @@ ClientHttpRequest::sslBumpEstablish(comm_err_t errflag)
         getConn()->auth_user_request = request->auth_user_request;
 #endif
 
-    getConn()->switchToHttps(request);
+    getConn()->switchToHttps(request, sslBumpNeeded());
 }
 
 void
index 5e84030a9f4692c5cff9dda273fe86bda87b273e..13dc6bbc8052ea1c5cfbab1227eab4a7b9513550 100644 (file)
@@ -155,14 +155,14 @@ private:
     ConnStateData * conn_;
 
 #if USE_SSL
-    /// whether the request needs to be bumped
-    enum { needUnknown,  needConfirmed,  needNot } sslBumpNeed;
+    /// whether (and how) the request needs to be bumped
+    Ssl::BumpMode sslBumpNeed;
 
 public:
     /// return true if the request needs to be bumped
-    bool sslBumpNeeded() const;
+    Ssl::BumpMode sslBumpNeeded() const;
     /// set the sslBumpNeeded state
-    void sslBumpNeeded(bool isNeeded);
+    void sslBumpNeeded(Ssl::BumpMode mode);
     void sslBumpStart();
     void sslBumpEstablish(comm_err_t errflag);
 #endif
index b80162076a5355e491aee0b8ddd864acd49e119f..9fa7a875fe3d950458ac508e02b050ea503669e0 100644 (file)
 #include "ssl/support.h"
 #include "ssl/gadgets.h"
 
+const char *Ssl::BumpModeStr[] = {
+    "none",
+    "client-first",
+    "server-first",
+    NULL
+};
+
 /**
  \defgroup ServerProtocolSSLInternal Server-Side SSL Internals
  \ingroup ServerProtocolSSLAPI
index 6430010e6a85250ad7eba04522e87a2edf27ef6a..4144042077dbecaa4d1353f1828984b5222719b7 100644 (file)
@@ -109,6 +109,27 @@ const char *sslGetUserCertificateChainPEM(SSL *ssl);
 
 namespace Ssl
 {
+/**
+  \ingroup ServerProtocolSSLAPI
+ * Supported ssl-bump modes
+ */
+enum BumpMode {bumpNone = 0, bumpClientFirst, bumpServerFirst, bumpEnd};
+
+/**
+ \ingroup  ServerProtocolSSLAPI
+ * Short names for ssl-bump modes
+ */
+extern const char *BumpModeStr[];
+
+/**
+ \ingroup ServerProtocolSSLAPI
+ * Return the short name of the ssl-bump mode "bm"
+ */
+inline const char *bumpMode(int bm)
+{
+    return (0 <= bm && bm < Ssl::bumpEnd) ? Ssl::BumpModeStr[bm] : NULL;
+}
+
 /**
   \ingroup ServerProtocolSSLAPI
   * Generate a certificate to be used as untrusted signing certificate, based on a trusted CA