From: Alex Rousskov Date: Fri, 4 May 2012 22:21:44 +0000 (-0600) Subject: Re-enabled support for bump-client-first mode using enhanced ssl_bump option. X-Git-Tag: BumpSslServerFirst.take08~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=caf3666d1f57f270e2ba21ea4974b44f863c5cf2;p=thirdparty%2Fsquid.git Re-enabled support for bump-client-first mode using enhanced ssl_bump option. 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. --- diff --git a/src/cache_cf.cc b/src/cache_cf.cc index 49f09077d8..a9781c4765 100644 --- a/src/cache_cf.cc +++ b/src/cache_cf.cc @@ -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 diff --git a/src/cf.data.depend b/src/cf.data.depend index 6250d0bf6e..779ae02e2e 100644 --- a/src/cf.data.depend +++ b/src/cf.data.depend @@ -69,5 +69,6 @@ wccp2_amethod wccp2_service wccp2_service_info wordlist +sslproxy_ssl_bump acl sslproxy_cert_sign acl sslproxy_cert_adapt acl diff --git a/src/cf.data.pre b/src/cf.data.pre index 6058898b16..052f7650c8 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -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 [!]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 diff --git a/src/client_side.cc b/src/client_side.cc index 1a6c6c50c7..15a38c5a84 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -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 ¶ms) 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); diff --git a/src/client_side.h b/src/client_side.h index 3efb5aa593..840755e5dd 100644 --- a/src/client_side.h +++ b/src/client_side.h @@ -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;} diff --git a/src/client_side_request.cc b/src/client_side_request.cc index 75975a4f94..40cab05209 100644 --- a/src/client_side_request.cc +++ b/src/client_side_request.cc @@ -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(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 diff --git a/src/client_side_request.h b/src/client_side_request.h index 5e84030a9f..13dc6bbc80 100644 --- a/src/client_side_request.h +++ b/src/client_side_request.h @@ -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 diff --git a/src/ssl/support.cc b/src/ssl/support.cc index b80162076a..9fa7a875fe 100644 --- a/src/ssl/support.cc +++ b/src/ssl/support.cc @@ -46,6 +46,13 @@ #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 diff --git a/src/ssl/support.h b/src/ssl/support.h index 6430010e6a..4144042077 100644 --- a/src/ssl/support.h +++ b/src/ssl/support.h @@ -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