]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Add connections_encrypted ACL
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Wed, 13 Jan 2016 10:10:20 +0000 (12:10 +0200)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Wed, 13 Jan 2016 10:10:20 +0000 (12:10 +0200)
The new connections_encrypted ACL matches transactions where all HTTP
messages were received over TLS transport connections, including messages
received from ICAP servers.

Some ICAP/eCAP services receive data from unencrypted sources. Some ICAP/eCAP
services are "secure". By default we assume that all eCAP services and all
ICAP services on TLS transport connections  are "secure" unless the user
uses the "connection_encryption" option in service configuration line.

This is a Measurement Factory project.

22 files changed:
src/AclRegs.cc
src/HttpMsg.cc
src/HttpMsg.h
src/HttpReply.cc
src/HttpRequest.cc
src/acl/Makefile.am
src/adaptation/Config.cc
src/adaptation/ServiceConfig.cc
src/adaptation/ServiceConfig.h
src/adaptation/ecap/ServiceRep.cc
src/adaptation/ecap/XactionRep.cc
src/adaptation/ecap/XactionRep.h
src/adaptation/icap/ModXact.cc
src/adaptation/icap/ModXact.h
src/adaptation/icap/ServiceRep.cc
src/cf.data.pre
src/client_side.cc
src/clients/FtpGateway.cc
src/clients/FtpRelay.cc
src/gopher.cc
src/http.cc
src/whois.cc

index e4ad9c0dc5519ebcb8b0f8d2b9d09a94d40fd4d6..583acfa3c937ce5c98cbac79e350c6c5ca9c942e 100644 (file)
@@ -30,6 +30,7 @@
 #include "acl/Asn.h"
 #include "acl/Browser.h"
 #include "acl/Checklist.h"
+#include "acl/ConnectionsEncrypted.h"
 #include "acl/Data.h"
 #include "acl/DestinationAsn.h"
 #include "acl/DestinationDomain.h"
@@ -231,3 +232,5 @@ ACLStrategised<const char *> ACLAdaptationService::RegistryEntry_(new ACLAdaptat
 ACL::Prototype ACLSquidError::RegistryProtoype(&ACLSquidError::RegistryEntry_, "squid_error");
 ACLStrategised<err_type> ACLSquidError::RegistryEntry_(new ACLSquidErrorData, ACLSquidErrorStrategy::Instance(), "squid_error");
 
+ACL::Prototype Acl::ConnectionsEncrypted::RegistryProtoype(&Acl::ConnectionsEncrypted::RegistryEntry_, "connections_encrypted");
+Acl::ConnectionsEncrypted Acl::ConnectionsEncrypted::RegistryEntry_("connections_encrypted");
index 5ba16e9f804858988e7bd6836805d1511bc42ef8..fea8bb9ebd6815471fd1f192e96e85571b9347b0 100644 (file)
@@ -24,7 +24,8 @@ HttpMsg::HttpMsg(http_hdr_owner_type owner):
     cache_control(NULL),
     hdr_sz(0),
     content_length(0),
-    pstate(psReadyToParseStartLine)
+    pstate(psReadyToParseStartLine),
+    sources(0)
 {}
 
 HttpMsg::~HttpMsg()
index 2a67db9b61ecc6e6f0abdeaba2589f7d570b7375..42c313642f5a1690f9c16627d950997ff4ec4e9e 100644 (file)
@@ -23,6 +23,26 @@ class HttpMsg : public RefCountable
 
 public:
     typedef RefCount<HttpMsg> Pointer;
+    /// Who may have created or modified this message?
+    enum Sources {
+        srcUnknown = 0,
+
+        /* flags in 0xFFFF zone are for "secure" or "encrypted" sources */
+        srcHttps = 1 << 0, ///< https_port or bumped http_port tunnel; HTTPS server
+        srcFtps = 1 << 1, ///< ftps_port or SFTP server; currently unused
+        srcIcaps = 1 << 2, ///< Secure ICAP service
+        srcEcaps = 1 << 3, ///< eCAP service that is considered secure; currently unused
+
+        /* these flags "taint" the message: it may have been observed or mangled outside Squid */
+        srcHttp = 1 << (16 + 0), ///< http_port or HTTP server
+        srcFtp = 1 << (16 + 1), ///< ftp_port or FTP server
+        srcIcap = 1 << (16 + 2), ///< traditional ICAP service without encryption
+        srcEcap = 1 << (16 + 3), ///< eCAP service that uses insecure libraries/daemons
+        srcGopher = 1 << (16 + 14), ///< Gopher server
+        srcWhois = 1 << (16 + 15), ///< Whois server
+        srcUnsafe = 0xFFFF0000,  ///< Unsafe sources mask
+        srcSafe = 0x0000FFFF ///< Safe sources mask
+    };
 
     HttpMsg(http_hdr_owner_type owner);
     virtual ~HttpMsg();
@@ -65,6 +85,8 @@ public:
 
     BodyPipe::Pointer body_pipe; // optional pipeline to receive message body
 
+    uint32_t sources; ///< The message sources 
+
     // returns true and sets hdr_sz on success
     // returns false and sets *error to zero when needs more data
     // returns false and sets *error to a positive Http::StatusCode on error
index 69962f01106a38c083a51f9393d68aaf0b6f6dea..f2409cac49b7513ed212cbcbd267e9a0c9b579fc 100644 (file)
@@ -588,6 +588,7 @@ bool HttpReply::inheritProperties(const HttpMsg *aMsg)
     if (!aRep)
         return false;
     keep_alive = aRep->keep_alive;
+    sources = aRep->sources;
     return true;
 }
 
index eca0de2d8bf802331805ddfe609f8d336b7d89ed..252a314da6f41fa5b05139a81f10bfb637225e25 100644 (file)
@@ -250,6 +250,8 @@ HttpRequest::inheritProperties(const HttpMsg *aMsg)
     clientConnectionManager = aReq->clientConnectionManager;
 
     notes = aReq->notes;
+
+    sources = aReq->sources;
     return true;
 }
 
index 7e97930a37905766b52bc0d5e7d5ba6166c4ce3f..f8ac12531ddb745f96d707a9bc529a28f3da23fa 100644 (file)
@@ -153,6 +153,8 @@ SSL_ACLS = \
         CertificateData.h  \
         Certificate.cc \
         Certificate.h  \
+       ConnectionsEncrypted.cc \
+       ConnectionsEncrypted.h \
        ServerCertificate.cc \
        ServerCertificate.h \
        ServerName.cc \
index b16b9791d28aa91f6e018b96a0567341f35abdc1..721db4b18e6f8c62011061d0b19af29052d58fd4 100644 (file)
@@ -157,11 +157,24 @@ Adaptation::Config::dumpService(StoreEntry *entry, const char *name) const
     typedef Services::iterator SCI;
     for (SCI i = AllServices().begin(); i != AllServices().end(); ++i) {
         const ServiceConfig &cfg = (*i)->cfg();
-        storeAppendPrintf(entry, "%s " SQUIDSTRINGPH "_%s %s %d " SQUIDSTRINGPH "\n",
+        bool isEcap = cfg.protocol.caseCmp("ecap") == 0;
+        bool isIcap = !isEcap;
+        const char *optConnectionEncryption = "";
+        // Print connections_encrypted option if no default value is used
+        if (cfg.secure.encryptTransport && !cfg.connectionEncryption)
+            optConnectionEncryption = " connection-encryption=off";
+        else if (isEcap && !cfg.connectionEncryption)
+            optConnectionEncryption = " connection-encryption=off";
+        else if (isIcap && !cfg.secure.encryptTransport && cfg.connectionEncryption)
+            optConnectionEncryption = " connection-encryption=on";
+
+        storeAppendPrintf(entry, "%s " SQUIDSTRINGPH " %s_%s %d " SQUIDSTRINGPH "%s\n",
                           name,
                           SQUIDSTRINGPRINT(cfg.key),
                           cfg.methodStr(), cfg.vectPointStr(), cfg.bypass,
-                          SQUIDSTRINGPRINT(cfg.uri));
+                          SQUIDSTRINGPRINT(cfg.uri),
+                          
+                          optConnectionEncryption);
     }
 }
 
index 7721c71d3a301f46c57df0a01f722fac2471f8a3..1aa63eefbe45dbd35b0a518cbdb0e808929ebd16 100644 (file)
@@ -127,6 +127,10 @@ Adaptation::ServiceConfig::parse()
         else if (strcmp(name, "on-overload") == 0) {
             grokked = grokOnOverload(onOverload, value);
             onOverloadSet = true;
+        } else if (strcmp(name, "connection-encryption") == 0) {
+            bool encrypt;
+            grokked = grokBool(encrypt, name, value);
+            connectionEncryption.configure(encrypt);
         } else if (strncmp(name, "ssl", 3) == 0 || strncmp(name, "tls-", 4) == 0) {
 #if !USE_OPENSSL
             debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: adaptation option '" << name << "' requires --with-openssl. ICAP service option ignored.");
index 23bd26b6c3712b116a43ea7687e47b2c20042c82..a926354e20593946fa490147fa5958ecee4c1de4 100644 (file)
@@ -13,6 +13,7 @@
 #include "base/RefCount.h"
 #include "security/PeerOptions.h"
 #include "SquidString.h"
+#include "YesNoNone.h"
 
 namespace Adaptation
 {
@@ -50,6 +51,7 @@ public:
 
     // security settings for adaptation service
     Security::PeerOptions secure;
+    YesNoNone connectionEncryption; ///< whether this service uses only secure connections
 
 protected:
     Method parseMethod(const char *buf) const;
index 973cc555f94d3fb80cf770480a884c185fb0f143..f0cd8339956e84b7b5b18191f29ac46cac04340c 100644 (file)
@@ -170,6 +170,8 @@ void
 Adaptation::Ecap::ServiceRep::finalize()
 {
     Adaptation::Service::finalize();
+    if (!cfg().connectionEncryption.configured())
+        writeableCfg().connectionEncryption.configure(true);
     theService = FindAdapterService(cfg().uri);
     if (theService) {
         try {
index 2a5d1b95d05d09250ff0cf472b3199638730a32a..46351c2841cec52b465d2942bdbe55a768e3c02e 100644 (file)
@@ -420,6 +420,7 @@ Adaptation::Ecap::XactionRep::useAdapted(const libecap::shared_ptr<libecap::Mess
     Must(proxyingAb == opUndecided);
 
     HttpMsg *msg = answer().header;
+    updateSources(msg);
     if (!theAnswerRep->body()) { // final, bodyless answer
         proxyingAb = opNever;
         updateHistory(msg);
@@ -733,3 +734,8 @@ Adaptation::Ecap::XactionRep::status() const
     return buf.content();
 }
 
+void
+Adaptation::Ecap::XactionRep::updateSources(HttpMsg *adapted)
+{
+    adapted->sources |= service().cfg().connectionEncryption ? HttpMsg::srcEcaps : HttpMsg::srcEcap;
+}
index 7efb947e2221bc86e52f8fc3dc8dc51232711650..778ebbd67a33c3ac174bc3f4740b4d71c03d77cd 100644 (file)
@@ -94,6 +94,7 @@ protected:
     void updateHistory(HttpMsg *adapted);
     void terminateMaster();
     void scheduleStop(const char *reason);
+    void updateSources(HttpMsg *adapted);
 
     const libecap::Area clientIpValue() const;
     const libecap::Area usernameValue() const;
index 220b1a0d7884be8a1b10ae3011cd4651b5ce44ac..875f8e7c36ba689fd7f66b37fc54cd8242544c31 100644 (file)
@@ -771,6 +771,16 @@ void Adaptation::Icap::ModXact::startSending()
 
     if (state.sending == State::sendingVirgin)
         echoMore();
+    else {
+        // If we are not using the virgin HTTP object update the 
+        // HttpMsg::sources flag.
+        // The state.sending may set to State::sendingVirgin in the case
+        // of 206 responses too, where we do not want to update HttpMsg::sources
+        // flag. However even for 206 responses the state.sending is 
+        // not set yet to sendingVirgin. This is done in later step 
+        // after the parseBody method called.
+        updateSources();
+    }
 }
 
 void Adaptation::Icap::ModXact::parseIcapHead()
@@ -1949,6 +1959,12 @@ void Adaptation::Icap::ModXact::clearError()
         request->clearError();
 }
 
+void Adaptation::Icap::ModXact::updateSources()
+{
+    Must(adapted.header);
+    adapted.header->sources |= (service().cfg().connectionEncryption ? HttpMsg::srcIcaps : HttpMsg::srcIcap);
+}
+
 /* Adaptation::Icap::ModXactLauncher */
 
 Adaptation::Icap::ModXactLauncher::ModXactLauncher(HttpMsg *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp, Adaptation::ServicePointer aService):
index e223d678895affd11d08ae8c8e76187ab2cbf0bb..fc051c82cc04803c3e489c3edae1968c66363fd9 100644 (file)
@@ -221,6 +221,7 @@ private:
     void prepEchoing();
     void prepPartialBodyEchoing(uint64_t pos);
     void echoMore();
+    void updateSources(); ///< Update the HttpMsg sources
 
     virtual bool doneAll() const;
     virtual void swanSong();
index 06fae2056c1a8c92e5c5303917a1a98e4242f008..c9fdf35a659497fe943a92c0e06816850b1e44a5 100644 (file)
@@ -85,7 +85,11 @@ Adaptation::Icap::ServiceRep::finalize()
     if (cfg().secure.encryptTransport) {
         debugs(3, DBG_IMPORTANT, "Initializing service " << cfg().resource << " SSL context");
         sslContext = writeableCfg().secure.createClientContext(true);
-    }
+        if (!cfg().connectionEncryption.configured())
+            writeableCfg().connectionEncryption.configure(true);
+    } else if (!cfg().connectionEncryption.configured())
+        writeableCfg().connectionEncryption.configure(false);
+   
 
     theSessionFailures.configure(TheConfig.oldest_service_failure > 0 ?
                                  TheConfig.oldest_service_failure : -1);
index 78df090c5f3cfa867f22ae92bb970beff114e26b..661021d41f5a8fb2699190899d2fdebce2fd8352 100644 (file)
@@ -1218,6 +1218,30 @@ IF USE_OPENSSL
 
        acl aclname ssl::server_name_regex [-i] \.foo\.com ...
          # regex matches server name obtained from various sources [fast]
+
+       acl aclname connections_encrypted
+         # matches transactions with all HTTP messages received over TLS
+         # transport connections. [fast]
+         #
+         # The master transaction deals with HTTP messages received from
+         # various sources. All sources used by the master transaction in the
+         # past are considered by the ACL. The following rules define whether
+         # a given message source taints the entire master transaction,
+         # resulting in ACL mismatches:
+         #
+         #  * The HTTP client transport connection is not TLS.
+         #  * An adaptation service connection-encryption flag is off.
+         #  * The peer or origin server transport connection is not TLS.
+         #
+         # Caching currently does not affect these rules. This cache ignorance
+         # implies that only the current HTTP client transport and REQMOD
+         # services status determine whether this ACL matches a from-cache
+         # transaction. The source of the cached response does not have any
+         # effect on future transaction that use the cached response without
+         # revalidation. This may change.
+         #
+         # DNS, ICP, and HTCP exchanges during the master transaction do not
+         # affect these rules.
 ENDIF
        acl aclname any-of acl1 acl2 ...
          # match any one of the acls [fast or slow]
@@ -8445,6 +8469,17 @@ DOC_START
                Use the given number as the Max-Connections limit, regardless
                of the Max-Connections value given by the service, if any.
 
+       connection-encryption=on|off
+               Determines the ICAP service effect on the connections_encrypted
+               ACL.
+
+               The default is "on" for Secure ICAP services (i.e., those
+               with the icaps:// service URIs scheme) and "off" for plain ICAP
+               services.
+
+               Does not affect ICAP connections (e.g., does not turn Secure
+               ICAP on or off).
+
        ==== ICAPS / TLS OPTIONS ====
 
        These options are used for Secure ICAP (icaps://....) services only.
@@ -8621,6 +8656,15 @@ DOC_START
 
                Routing is not allowed by default.
 
+       connection-encryption=on|off
+               Determines the eCAP service effect on the connections_encrypted
+               ACL. 
+
+               Defaults to "on", which does not taint the master transaction
+               w.r.t. that ACL.
+
+               Does not affect eCAP API calls.
+
        Older ecap_service format without optional named parameters is
        deprecated but supported for backward compatibility.
 
index 2ede121d184a6f5aec1fbb5f3a4d6fb9f18c18e9..4b5505e1b7d82b59a25925e303a56d1c23d55351 100644 (file)
@@ -2342,6 +2342,8 @@ clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp,
     // TODO: decouple http->flags.accel from request->flags.sslBumped
     request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
                               !conn->port->allow_direct : 0;
+    request->sources |= isFtp ? HttpMsg::srcFtp :
+        ((request->flags.sslBumped || conn->port->transport.protocol == AnyP::PROTO_HTTPS) ? HttpMsg::srcHttps : HttpMsg::srcHttp);
 #if USE_AUTH
     if (request->flags.sslBumped) {
         if (conn->getAuth() != NULL)
index 2a836ae1fc5d48108118390a30bfe03be9a15967..0d7ecc2729a3f6b256e6fdcb188dfd09e997a3c5 100644 (file)
@@ -2589,6 +2589,7 @@ Ftp::Gateway::appendSuccessHeader()
     if (mime_enc)
         reply->header.putStr(Http::HdrType::CONTENT_ENCODING, mime_enc);
 
+    reply->sources |= HttpMsg::srcFtp;
     setVirginReply(reply);
     adaptOrFinalizeReply();
 }
index b5483ef4f46b8ad74e32b49ba698a80620804e1d..f715c95dd9d8b3485fea420a33eff7798d4f4d1b 100644 (file)
@@ -346,6 +346,7 @@ Ftp::Relay::forwardReply()
     EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
 
     HttpReply *const reply = createHttpReply(Http::scNoContent);
+    reply->sources |= HttpMsg::srcFtp;
 
     setVirginReply(reply);
     adaptOrFinalizeReply();
@@ -418,6 +419,8 @@ Ftp::Relay::startDataDownload()
            " (" << data.conn->local << ")");
 
     HttpReply *const reply = createHttpReply(Http::scOkay, -1);
+    reply->sources |= HttpMsg::srcFtp;
+
     EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
     setVirginReply(reply);
     adaptOrFinalizeReply();
index bc8226a78c812815d47fe53fe22696b3431a0e08..35d8c41ca833f41476ff2fb16536b3b21be74ed8 100644 (file)
@@ -122,6 +122,7 @@ public:
     char *buf;          /* pts to a 4k page */
     Comm::ConnectionPointer serverConn;
     FwdState::Pointer fwd;
+    HttpReply::Pointer reply_;
     char replybuf[BUFSIZ];
 };
 
@@ -249,6 +250,7 @@ gopherMimeCreate(GopherStateData * gopherState)
         reply->header.putStr(Http::HdrType::CONTENT_ENCODING, mime_enc);
 
     entry->replaceHttpReply(reply);
+    gopherState->reply_ = reply;
 }
 
 /**
@@ -772,8 +774,11 @@ gopherReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm
         ++IOStats.Gopher.read_hist[bin];
 
         HttpRequest *req = gopherState->fwd->request;
-        if (req->hier.bodyBytesRead < 0)
+        if (req->hier.bodyBytesRead < 0) {
             req->hier.bodyBytesRead = 0;
+            // first bytes read, update Reply flags:
+            gopherState->reply_->sources |= HttpMsg::srcGopher;
+        }
 
         req->hier.bodyBytesRead += len;
     }
index 0e62471be0d07dbb92460c3ef2159bc86e4196a8..5b39187bf4af07a08212fa1797f77b8a88dcc127 100644 (file)
@@ -769,6 +769,8 @@ HttpStateData::processReplyHeader()
     // done with Parser, now process using the HttpReply
     hp = NULL;
 
+    newrep->sources |= request->url.getScheme() == AnyP::PROTO_HTTPS ? HttpMsg::srcHttps : HttpMsg::srcHttp;
+
     newrep->removeStaleWarnings();
 
     if (newrep->sline.protocol == AnyP::PROTO_HTTP && newrep->sline.status() >= 100 && newrep->sline.status() < 200) {
index 392400f71cdcba45d6f03462b36f0018709314c5..8fe78987bd882002bd31be8075e516f6b6d4d46c 100644 (file)
@@ -105,6 +105,7 @@ WhoisState::setReplyToOK(StoreEntry *sentry)
     HttpReply *reply = new HttpReply;
     sentry->buffer();
     reply->setHeaders(Http::scOkay, "Gatewaying", "text/plain", -1, -1, -2);
+    reply->sources |= HttpMsg::srcWhois;
     sentry->replaceHttpReply(reply);
 }