]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Author: Christos Tsantilas <chtsanti@users.sourceforge.net>
authorAmos Jeffries <squid3@treenet.co.nz>
Sat, 4 Oct 2008 12:46:16 +0000 (01:46 +1300)
committerAmos Jeffries <squid3@treenet.co.nz>
Sat, 4 Oct 2008 12:46:16 +0000 (01:46 +1300)
Port Connection Pinning from 2.6

25 files changed:
src/HttpHeader.cc
src/HttpHeader.h
src/HttpMsg.h
src/HttpReply.cc
src/HttpReply.h
src/HttpRequest.cc
src/HttpRequest.h
src/ICAP/ICAPModXact.cc
src/ICAP/ICAPModXact.h
src/ProtoPort.h
src/cache_cf.cc
src/cf.data.pre
src/client_side.cc
src/client_side.h
src/client_side_reply.cc
src/client_side_request.cc
src/enums.h
src/forward.cc
src/http.cc
src/http.h
src/neighbors.cc
src/peer_select.cc
src/structs.h
src/tests/stub_HttpReply.cc
src/tests/stub_HttpRequest.cc

index 9d457c2db50b66f82f5e975ccdcac520ecc0065b..c77ebdcbb85fbe158c54c684e4911b6c90b792ac 100644 (file)
@@ -112,6 +112,7 @@ static const HttpHeaderFieldAttrs HeadersAttrs[] =
         {"Proxy-Authentication-Info", HDR_PROXY_AUTHENTICATION_INFO, ftStr},
         {"Proxy-Authorization", HDR_PROXY_AUTHORIZATION, ftStr},
         {"Proxy-Connection", HDR_PROXY_CONNECTION, ftStr},
+       {"Proxy-support", HDR_PROXY_SUPPORT, ftStr},
         {"Public", HDR_PUBLIC, ftStr},
         {"Range", HDR_RANGE, ftPRange},
         {"Referer", HDR_REFERER, ftStr},
@@ -172,6 +173,7 @@ static http_hdr_type ListHeadersArr[] =
         HDR_IF_MATCH, HDR_IF_NONE_MATCH,
         HDR_LINK, HDR_PRAGMA,
         HDR_PROXY_CONNECTION,
+       HDR_PROXY_SUPPORT,
         HDR_TRANSFER_ENCODING,
         HDR_UPGRADE,
         HDR_VARY,
index 74225a9949e8b41821b44e80bd5f3381dc830a4e..4eba065dba1106c9e470b4f5f7ff4ebc0c0583b1 100644 (file)
@@ -83,6 +83,7 @@ typedef enum {
     HDR_PROXY_AUTHENTICATION_INFO,
     HDR_PROXY_AUTHORIZATION,
     HDR_PROXY_CONNECTION,
+    HDR_PROXY_SUPPORT,
     HDR_PUBLIC,
     HDR_RANGE,
     HDR_REQUEST_RANGE,         /**< some clients use this, sigh */
index 7ecb1ed0f9a55fa354e74673b9c1b34e0aed6b19..184850a8f23d63c40ac324d42a561c5834d2fb30 100644 (file)
@@ -92,6 +92,8 @@ public:
     virtual bool expectingBody(const HttpRequestMethod&, int64_t&) const = 0;
 
     void firstLineBuf(MemBuf&);
+    
+    virtual bool inheritProperties(const HttpMsg *aMsg) = 0;
 
 protected:
     virtual bool sanityCheckStartLine(MemBuf *buf, http_status *error) = 0;
index bfbd0b06fe709602d2cb7e6b7efdd5fdd851e3a5..6326350e1dacee786c1eba360d8b7ae402329c5a 100644 (file)
@@ -566,5 +566,16 @@ HttpReply::clone() const
 
     rep->protocol = protocol;
     rep->sline = sline;
+    rep->keep_alive = keep_alive;
     return rep;
 }
+
+
+bool HttpReply::inheritProperties(const HttpMsg *aMsg)
+{
+    const HttpReply *aRep = dynamic_cast<const HttpReply*>(aMsg);
+    if(!aRep)
+       return false;
+    keep_alive = aRep->keep_alive;
+    return true;
+}
index 2fe554cd29d5589bee90327edae84613b7c91013..baf595fc2a1b6d43bb42d36b709367347e089bb3 100644 (file)
@@ -99,6 +99,8 @@ public:
 
     virtual bool expectingBody(const HttpRequestMethod&, int64_t&) const;
 
+    virtual bool inheritProperties(const HttpMsg *aMsg);
+
     void updateOnNotModified(HttpReply const *other);
 
     /** set commonly used info with one call */
index 3f17df12ec53dbb26f31fb993458ee045aa59cf8..73e57a1204c1f4834f7e36eab1ccd94302a5e6ce 100644 (file)
@@ -74,6 +74,7 @@ HttpRequest::init()
     login[0] = '\0';
     host[0] = '\0';
     auth_user_request = NULL;
+    pinned_connection = NULL;
     port = 0;
     canonical = NULL;
     memset(&flags, '\0', sizeof(flags));
@@ -127,6 +128,9 @@ HttpRequest::clean()
         range = NULL;
     }
 
+    if(pinned_connection)
+       cbdataReferenceDone(pinned_connection);
+
     tag.clean();
 
     extacl_user.clean();
@@ -510,3 +514,27 @@ HttpRequest::cacheable() const
 
     return true;
 }
+
+bool HttpRequest::inheritProperties(const HttpMsg *aMsg)
+{
+    const HttpRequest* aReq = dynamic_cast<const HttpRequest*>(aMsg);
+    if(!aReq)
+       return false;
+    
+    client_addr = aReq->client_addr;
+    my_addr = aReq->my_addr;
+
+    // This may be too conservative for the 204 No Content case
+    // may eventually need cloneNullAdaptationImmune() for that.
+    flags = aReq->flags.cloneAdaptationImmune();
+
+    if (aReq->auth_user_request) {
+        auth_user_request = aReq->auth_user_request;
+       AUTHUSERREQUESTLOCK(auth_user_request, "inheritProperties");
+    }
+
+    if(aReq->pinned_connection) {
+       pinned_connection = cbdataReference(aReq->pinned_connection);
+    }
+    return true;
+}
index 2dcec900a3fd9ea86fe30a96839113a5818f07a3..4d71f0e1238b2de1f7b3c9d6abda2028e2621065 100644 (file)
@@ -100,6 +100,12 @@ public:
 
 private:
     char host[SQUIDHOSTNAMELEN];
+    
+    /***
+     * The client side connection data of pinned connections for the client side 
+     * request related objects 
+     */
+    ConnStateData *pinned_connection;
 
 public:
     IPAddress host_addr;
@@ -176,6 +182,18 @@ public:
     static HttpRequest * CreateFromUrlAndMethod(char * url, const HttpRequestMethod& method);
 
     static HttpRequest * CreateFromUrl(char * url);
+    
+    void setPinnedConnection(ConnStateData *conn) {
+       pinned_connection = cbdataReference(conn);
+    }
+
+    ConnStateData *pinnedConnection() {
+       return pinned_connection;
+    }
+
+    void releasePinnedConnection() {
+       cbdataReferenceDone(pinned_connection);
+    }
 
 private:
     const char *packableURI(bool full_uri) const;
@@ -186,7 +204,8 @@ protected:
     virtual bool sanityCheckStartLine(MemBuf *buf, http_status *error);
 
     virtual void hdrCacheInit();
-
+    
+    virtual bool inheritProperties(const HttpMsg *aMsg);
 };
 
 MEMPROXY_CLASS_INLINE(HttpRequest)          /**DOCS_NOSEMI*/
index ff0412efdc0088e48105dcf7f78eb7bd7d19b69a..4bfdb9c41df9b6983734626a76a004d77725e9a9 100644 (file)
@@ -782,14 +782,16 @@ void ICAPModXact::prepEchoing()
     // allocate the adapted message and copy metainfo
     Must(!adapted.header);
     HttpMsg *newHead = NULL;
-    if (const HttpRequest *oldR = dynamic_cast<const HttpRequest*>(oldHead)) {
+    if (dynamic_cast<const HttpRequest*>(oldHead)) {
         HttpRequest *newR = new HttpRequest;
-        inheritVirginProperties(*newR, *oldR);
         newHead = newR;
-    } else
-    if (dynamic_cast<const HttpReply*>(oldHead))
-        newHead = new HttpReply;
+    } 
+    else if (dynamic_cast<const HttpReply*>(oldHead)) {
+       HttpReply *newRep = new HttpReply;
+       newHead = newRep;
+    }
     Must(newHead);
+    newHead->inheritProperties(oldHead);
 
     adapted.setHeader(newHead);
 
@@ -845,15 +847,18 @@ void ICAPModXact::parseHttpHead()
         if (!parseHead(adapted.header))
             return; // need more header data
 
-        if (HttpRequest *newHead = dynamic_cast<HttpRequest*>(adapted.header)) {
+        if (dynamic_cast<HttpRequest*>(adapted.header)) {
             const HttpRequest *oldR = dynamic_cast<const HttpRequest*>(virgin.header);
             Must(oldR);
             // TODO: the adapted request did not really originate from the 
             // client; give proxy admin an option to prevent copying of 
             // sensitive client information here. See the following thread:
             // http://www.squid-cache.org/mail-archive/squid-dev/200703/0040.html
-            inheritVirginProperties(*newHead, *oldR);
         }
+
+       // Maybe adapted.header==NULL if HttpReply and have Http 0.9 ....
+       if(adapted.header) 
+           adapted.header->inheritProperties(virgin.header);
     }
 
     decideOnParsingBody();
@@ -881,22 +886,6 @@ bool ICAPModXact::parseHead(HttpMsg *head)
     return true;
 }
 
-// TODO: Move this method to HttpRequest?
-void ICAPModXact::inheritVirginProperties(HttpRequest &newR, const HttpRequest &oldR) {
-
-    newR.client_addr = oldR.client_addr;
-    newR.my_addr = oldR.my_addr;
-
-    // This may be too conservative for the 204 No Content case
-    // may eventually need cloneNullAdaptationImmune() for that.
-    newR.flags = oldR.flags.cloneAdaptationImmune();
-
-    if (oldR.auth_user_request) {
-        newR.auth_user_request = oldR.auth_user_request;
-       AUTHUSERREQUESTLOCK(newR.auth_user_request, "newR in ICAPModXact");
-    }
-}
-
 void ICAPModXact::decideOnParsingBody() {
     if (gotEncapsulated("res-body") || gotEncapsulated("req-body")) {
         debugs(93, 5, HERE << "expecting a body");
@@ -1152,7 +1141,6 @@ void ICAPModXact::encapsulateHead(MemBuf &icapBuf, const char *section, MemBuf &
         HttpRequest* new_request = new HttpRequest;
         urlParse(old_request->method, old_request->canonical,new_request);
         new_request->http_ver = old_request->http_ver;
-        inheritVirginProperties(*new_request, *old_request);
         headClone = new_request;
     } 
     else if (const HttpReply *old_reply = dynamic_cast<const HttpReply*>(head)) {
@@ -1162,6 +1150,7 @@ void ICAPModXact::encapsulateHead(MemBuf &icapBuf, const char *section, MemBuf &
     }
     
     Must(headClone);
+    headClone->inheritProperties(head);
     
     HttpHeaderPos pos = HttpHeaderInitPos;
     HttpHeaderEntry* p_head_entry = NULL;
index 1c07fec3be64a9b7d39389b496fe009c05c8e892..f546bfa550476644c0036ee5b8f33becde53e307 100644 (file)
@@ -208,7 +208,6 @@ private:
     void parseIcapHead();
     void parseHttpHead();
     bool parseHead(HttpMsg *head);
-    void inheritVirginProperties(HttpRequest &newR, const HttpRequest &oldR);
 
     void decideOnParsingBody();
     void parseBody();
index a9ece3f2c2db57029a28ebaaba6c948630a7ed44..72f64bbce3bf67088d370451f3f62243f5db1c91 100644 (file)
@@ -26,6 +26,7 @@ struct http_port_list
     unsigned int sslBump:1;            /**< intercepts CONNECT requests */
 
     int vport;                 /* virtual port support, -1 for dynamic, >0 static*/
+    bool connection_auth_disabled;     /* Don't support connection oriented auth */
     int disable_pmtu_discovery;
 
     struct {
index 1cd44b7094dae4d5bc92c202ec38413924333442..ad85e549ea4c1ee42906df626239efeddb7a4bb7 100644 (file)
@@ -1690,6 +1690,7 @@ parse_peer(peer ** head)
         self_destruct();
 
     p->icp.port = GetUdpService();
+    p->connection_auth = 2;    /* auto */
 
     while ((token = strtok(NULL, w_space))) {
         if (!strcasecmp(token, "proxy-only")) {
@@ -1843,6 +1844,14 @@ parse_peer(peer ** head)
             p->front_end_https = 1;
         } else if (strcmp(token, "front-end-https=auto") == 0) {
             p->front_end_https = 2;
+        }else if (strcmp(token, "connection-auth=off") == 0) {
+            p->connection_auth = 0;
+        } else if (strcmp(token, "connection-auth") == 0) {
+            p->connection_auth = 1;
+        } else if (strcmp(token, "connection-auth=on") == 0) {
+            p->connection_auth = 1;
+        } else if (strcmp(token, "connection-auth=auto") == 0) {
+            p->connection_auth = 2;
         } else {
             debugs(3, 0, "parse_peer: token='" << token << "'");
             self_destruct();
@@ -2867,6 +2876,7 @@ parse_http_port_specification(http_port_list * s, char *token)
 
     s->disable_pmtu_discovery = DISABLE_PMTU_OFF;
     s->name = xstrdup(token);
+    s->connection_auth_disabled = false;
 
 #if USE_IPV6
     if (*token == '[') {
@@ -2950,6 +2960,14 @@ parse_http_port_option(http_port_list * s, char *token)
         s->accel = 1;
     } else if (strcmp(token, "accel") == 0) {
         s->accel = 1;
+    } else if (strcmp(token, "no-connection-auth") == 0) {
+        s->connection_auth_disabled = true;
+    } else if (strcmp(token, "connection-auth=off") == 0) {
+          s->connection_auth_disabled = true;
+    } else if (strcmp(token, "connection-auth") == 0) {
+          s->connection_auth_disabled = false;
+    } else if (strcmp(token, "connection-auth=on") == 0) {
+          s->connection_auth_disabled = false;
     } else if (strncmp(token, "disable-pmtu-discovery=", 23) == 0) {
         if (!strcasecmp(token + 23, "off"))
             s->disable_pmtu_discovery = DISABLE_PMTU_OFF;
@@ -3131,6 +3149,11 @@ dump_generic_http_port(StoreEntry * e, const char *n, const http_port_list * s)
     if (s->vport)
         storeAppendPrintf(e, " vport");
 
+    if (s->connection_auth_disabled)
+        storeAppendPrintf(e, " connection-auth=off");
+    else
+        storeAppendPrintf(e, " connection-auth=on");
+
     if (s->disable_pmtu_discovery != DISABLE_PMTU_OFF) {
         const char *pmtu;
 
index 9c485b51611e134d03403438150c0cc22815c42e..452e0c591090b437ab35c5f66eeefbef3f1ee339 100644 (file)
@@ -1033,6 +1033,11 @@ DOC_START
           protocol=    Protocol to reconstruct accelerated requests with.
                        Defaults to http.
 
+           connection-auth[=on|off]
+                        use connection-auth=off to tell Squid to prevent 
+                        forwarding Microsoft connection oriented authentication
+                       (NTLM, Negotiate and Kerberos)
+
           disable-pmtu-discovery=
                        Control Path-MTU discovery usage:
                            off         lets OS decide on what to do (default).
@@ -1602,6 +1607,7 @@ DOC_START
                     sslcipher=...
                     ssloptions=...
                     front-end-https[=on|auto]
+                     connection-auth[=on|off|auto]
 
                     use 'proxy-only' to specify objects fetched
                     from this cache should not be saved locally.
@@ -1824,6 +1830,12 @@ DOC_START
                     on this header. If set to auto the header will
                     only be added if the request is forwarded as a https://
                     URL.
+                     
+                     use connection-auth=off to tell Squid that this peer does
+                     not support Microsoft connection oriented authentication,
+                     and any such challenges received from there should be
+                     ignored. Default is auto to automatically determine the
+                     status of the peer.
 DOC_END
 
 NAME: cache_peer_domain cache_host_domain
index 202a1b502e4b792f54fdc895e70aa22efd396796..789a1f2f82355775e1b2fa3becf7bc1fe28931dc 100644 (file)
@@ -621,6 +621,9 @@ ConnStateData::swanSong()
         auth_user_request->onConnectionClose(this);
     }
 
+    if (pinning.fd >= 0)
+       comm_close(pinning.fd);
+
     BodyProducer::swanSong();
     flags.swanSang = true;
 }
@@ -1368,6 +1371,12 @@ ClientSocketContext::keepaliveNextRequest()
     debugs(33, 3, "ClientSocketContext::keepaliveNextRequest: FD " << conn->fd);
     connIsFinished();
 
+    if (conn->pinning.pinned && conn->pinning.fd == -1) {
+        debugs(33, 2, "clientKeepaliveNextRequest: FD " << conn->fd << " Connection was pinned but server side gone. Terminating client connection");
+        comm_close(conn->fd);
+        return;
+    }
+
     /** \par
      * Attempt to parse a request from the request buffer.
      * If we've been fed a pipelined request it may already
@@ -3353,6 +3362,9 @@ CBDATA_CLASS_INIT(ConnStateData);
 
 ConnStateData::ConnStateData() :AsyncJob("ConnStateData"), transparent_ (false), reading_ (false), closing_ (false)
 {
+    pinning.fd = -1;
+    pinning.pinned = false;
+    pinning.auth = false;
 }
 
 bool
@@ -3432,3 +3444,96 @@ ConnStateData::In::~In()
     if (allocatedSize)
         memFreeBuf(allocatedSize, buf);
 }
+
+/* This is a comm call normally scheduled by comm_close() */
+void
+ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io)
+{
+    pinning.fd = -1;
+    if (pinning.peer) {
+       cbdataReferenceDone(pinning.peer);
+    }
+    safe_free(pinning.host);
+    /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
+     * connection has gone away */
+}
+
+void ConnStateData::pinConnection(int pinning_fd, HttpRequest *request, struct peer *peer, bool auth){
+    fde *f;
+    char desc[FD_DESC_SZ];
+
+    if (pinning.fd == pinning_fd)
+       return;
+    else if (pinning.fd != -1)
+       comm_close(pinning.fd);
+    
+    if(pinning.host)
+       safe_free(pinning.host);
+    
+    pinning.fd = pinning_fd;
+    pinning.host = xstrdup(request->GetHost());
+    pinning.port = request->port;
+    pinning.pinned = true;
+    if (pinning.peer)
+       cbdataReferenceDone(pinning.peer);
+    if (peer)
+       pinning.peer = cbdataReference(peer);
+    pinning.auth = auth;
+    f = &fd_table[fd];
+    snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s:%d (%d)",
+       (auth || !peer) ? request->GetHost() : peer->name, f->ipaddr, (int) f->remote_port, fd);
+    fd_note(pinning_fd, desc);
+    
+    typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
+    pinning.closeHandler = asyncCall(33, 5, "ConnStateData::clientPinnedConnectionClosed",
+                                       Dialer(this, &ConnStateData::clientPinnedConnectionClosed));
+    comm_add_close_handler(pinning_fd, pinning.closeHandler);
+
+}
+
+int ConnStateData::validatePinnedConnection(HttpRequest *request, const struct peer *peer)
+{
+    bool valid = true;
+    if (pinning.fd < 0)
+       return -1;
+    
+    if (pinning.auth && request && strcasecmp(pinning.host, request->GetHost()) != 0) {
+       valid = false;
+    }
+    if (request && pinning.port != request->port){
+       valid = false;
+    }
+    if (pinning.peer && !cbdataReferenceValid(pinning.peer)){
+       valid = false;
+    }
+    if (peer != pinning.peer){
+       valid = false;
+    }
+
+    if(!valid) {
+       int pinning_fd=pinning.fd;
+       /* The pinning info is not safe, remove any pinning info*/
+       unpinConnection();
+
+       /* also close the server side socket, we should not use it for invalid/unauthenticated
+          requests...
+        */
+       comm_close(pinning_fd);
+       return -1;
+    }
+
+    return pinning.fd;
+}
+
+void ConnStateData::unpinConnection()
+{
+    if(pinning.peer)
+       cbdataReferenceDone(pinning.peer);
+
+    if(pinning.closeHandler != NULL) {
+       comm_remove_close_handler(pinning.fd, pinning.closeHandler);
+       pinning.closeHandler = NULL;
+    }
+    pinning.fd = -1;
+    safe_free(pinning.host);
+}
index def37ed7cfef11679d8915d481ac0ad7848465b4..2010f131bbb61af3aa9d7931448a3b4db5d90672 100644 (file)
@@ -189,6 +189,16 @@ public:
         bool readMoreRequests;
         bool swanSang; // XXX: temporary flag to check proper cleanup
     } flags;
+    struct {
+        int fd;                 /* pinned server side connection */
+        char *host;             /* host name of pinned connection */
+        int port;               /* port of pinned connection */
+        bool pinned;             /* this connection was pinned */
+        bool auth;               /* pinned for www authentication */
+        struct peer *peer;             /* peer the connection goes via */
+       AsyncCall::Pointer closeHandler; /*The close handler for pinned server side connection*/
+     } pinning;
+
     http_port_list *port;
 
     bool transparent() const;
@@ -206,6 +216,31 @@ public:
     void handleReadData(char *buf, size_t size);
     void handleRequestBodyData();
 
+    /**
+     * Correlate the current ConnStateData object with the pinning_fd socket descriptor.
+     */
+    void pinConnection(int fd, HttpRequest *request, struct peer *peer, bool auth);
+    /**
+     * Decorrelate the ConnStateData object from its pinned peer
+     */
+    void unpinConnection();    
+    /**
+     * Checks if there is pinning info if it is valid. It can close the server side connection
+     * if pinned info is not valid.
+     \param request   if it is not NULL also checks if the pinning info refers to the request client side HttpRequest
+     \param peer      if it is not NULL also check if the peer is the pinning peer
+     \return          The fd of the server side connection or -1 if fails.
+     */
+    int validatePinnedConnection(HttpRequest *request, const struct peer *peer=NULL);
+    /**
+     * returts the pinned peer if exists, NULL otherwise
+     */
+    struct peer *pinnedPeer() const {return pinning.peer;}
+    bool pinnedAuth() const {return pinning.auth;}
+
+    // pining related comm callbacks
+    void clientPinnedConnectionClosed(const CommCloseCbParams &io);
+
     // comm callbacks
     void clientReadRequest(const CommIoCbParams &io);
     void connStateClosed(const CommCloseCbParams &io);
index e3b65e69e4607106006a4d6b04cf67bb5d0b1824..7bb1f456f671992de413ae1f5a26450db710cae8 100644 (file)
@@ -1277,24 +1277,45 @@ clientReplyContext::buildReplyHeader()
     /* Filter unproxyable authentication types */
 
     if (http->logType != LOG_TCP_DENIED &&
-           (hdr->has(HDR_WWW_AUTHENTICATE) || hdr->has(HDR_PROXY_AUTHENTICATE))) {
+            hdr->has(HDR_WWW_AUTHENTICATE)) {
         HttpHeaderPos pos = HttpHeaderInitPos;
         HttpHeaderEntry *e;
 
-        int headers_deleted = 0;
+       int connection_auth_blocked = 0;
         while ((e = hdr->getEntry(&pos))) {
-            if (e->id == HDR_WWW_AUTHENTICATE || e->id == HDR_PROXY_AUTHENTICATE) {
+            if (e->id == HDR_WWW_AUTHENTICATE) {
                 const char *value = e->value.buf();
 
                 if ((strncasecmp(value, "NTLM", 4) == 0 &&
                         (value[4] == '\0' || value[4] == ' '))
                         ||
                         (strncasecmp(value, "Negotiate", 9) == 0 &&
-                         (value[9] == '\0' || value[9] == ' ')))
-                            hdr->delAt(pos, headers_deleted);
+                         (value[9] == '\0' || value[9] == ' '))
+                       ||
+                       (strncasecmp(value, "Kerberos", 8) == 0 &&
+                         (value[8] == '\0' || value[8] == ' ')))
+               {
+                   if (request->flags.connection_auth_disabled) {
+                       hdr->delAt(pos, connection_auth_blocked);
+                        continue;
+                    }
+                   request->flags.must_keepalive = 1;
+                   if (!request->flags.accelerated && !request->flags.intercepted) {
+                        httpHeaderPutStrf(hdr, HDR_PROXY_SUPPORT, "Session-Based-Authentication");
+                       /*
+                         We send "[Proxy-]Connection: Proxy-Support" header to mark
+                         Proxy-Support as a hop-by-hop header for intermediaries that do not
+                         understand the semantics of this header. The RFC should have included
+                         this recommendation.
+                       */
+                        httpHeaderPutStrf(hdr, HDR_CONNECTION, "Proxy-support");
+                    }
+                    break;
+               }
             }
         }
-        if (headers_deleted)
+
+        if (connection_auth_blocked)
             hdr->refreshMask();
     }
 
@@ -1352,6 +1373,12 @@ clientReplyContext::buildReplyHeader()
         debugs(88, 3, "clientBuildReplyHeader: Shutting down, don't keep-alive.");
         request->flags.proxy_keepalive = 0;
     }
+    
+     if (request->flags.connection_auth && !reply->keep_alive) {
+        debugs(33, 2, "clientBuildReplyHeader: Connection oriented auth but server side non-persistent");
+        request->flags.proxy_keepalive = 0;
+    }
+
 
     /* Append VIA */
     if (Config.onoff.via) {
index 5b6ca63393ee4370f375cce2f8f19de5ff60c9ab..49a4219ae8bb24205cbcecc01d8f1912c5fae467 100644 (file)
@@ -828,6 +828,56 @@ clientInterpretRequestHeaders(ClientHttpRequest * http)
     if (req_hdr->has(HDR_AUTHORIZATION))
         request->flags.auth = 1;
 
+    ConnStateData *http_conn = http->getConn();
+    assert(http_conn);
+    request->flags.connection_auth_disabled = http_conn->port->connection_auth_disabled;
+    if (!request->flags.connection_auth_disabled) {
+       if (http_conn->pinning.fd != -1) {
+           if (http_conn->pinning.auth) {
+               request->flags.connection_auth = 1;
+               request->flags.auth = 1;
+           } else {
+               request->flags.connection_proxy_auth = 1;
+           }
+           request->setPinnedConnection(http_conn);
+       }
+    }
+
+    /* check if connection auth is used, and flag as candidate for pinning
+     * in such case.
+     * Note: we may need to set flags.connection_auth even if the connection
+     * is already pinned if it was pinned earlier due to proxy auth
+     */
+    if (!request->flags.connection_auth) {
+       if (req_hdr->has(HDR_AUTHORIZATION) || req_hdr->has(HDR_PROXY_AUTHORIZATION)) {
+           HttpHeaderPos pos = HttpHeaderInitPos;
+           HttpHeaderEntry *e;
+           int may_pin = 0;
+           while ((e = req_hdr->getEntry(&pos))) {
+               if (e->id == HDR_AUTHORIZATION || e->id == HDR_PROXY_AUTHORIZATION) {
+                   const char *value = e->value.buf();
+                   if (strncasecmp(value, "NTLM ", 5) == 0
+                       ||
+                       strncasecmp(value, "Negotiate ", 10) == 0
+                       ||
+                       strncasecmp(value, "Kerberos ", 9) == 0) {
+                       if (e->id == HDR_AUTHORIZATION) {
+                           request->flags.connection_auth = 1;
+                           may_pin = 1;
+                       } else {
+                           request->flags.connection_proxy_auth = 1;
+                           may_pin = 1;
+                       }
+                   }
+               }
+           }
+           if (may_pin && !request->pinnedConnection()) {
+               request->setPinnedConnection(http->getConn());
+           }
+       }
+    }
+
+
     if (request->login[0] != '\0')
         request->flags.auth = 1;
 
index 5ab13f8c7c6f1fdee27897b9be08e229cfe61115..0b8a16408658e1eda0e6ac9e8e096120729a5552 100644 (file)
@@ -177,7 +177,8 @@ typedef enum {
     ANY_OLD_PARENT,
     USERHASH_PARENT,
     SOURCEHASH_PARENT,
-    HIER_MAX
+    HIER_MAX,
+    PINNED
 } hier_code;
 
 /// \ingroup ServerProtocolICPAPI
index 91da4cbbaf65af803a575b3510da012a16988513..18e3fee05a08db3090853098f29938c99972ca1a 100644 (file)
@@ -804,6 +804,35 @@ FwdState::connectStart()
     if (ftimeout < ctimeout)
         ctimeout = ftimeout;
 
+
+    request->flags.pinned = 0;
+    if (fs->code == PINNED) {
+       ConnStateData *pinned_connection = request->pinnedConnection();
+       assert(pinned_connection);
+        fd = pinned_connection->validatePinnedConnection(request, fs->_peer);
+        if (fd >= 0) {
+           pinned_connection->unpinConnection();
+#if 0
+            if (!fs->_peer)
+                fs->code = HIER_DIRECT;
+#endif
+            server_fd = fd;
+            n_tries++;
+            request->flags.pinned = 1;
+            if (pinned_connection->pinnedAuth())
+                request->flags.auth = 1;
+            comm_add_close_handler(fd, fwdServerClosedWrapper, this);
+            connectDone(fd, COMM_OK, 0);
+            return;
+        }
+       /* Failure. Fall back on next path */
+        request->releasePinnedConnection();
+        servers = fs->next;
+        fwdServerFree(fs);
+        connectStart();
+        return;
+    }  
+
     fd = fwdPconnPool->pop(host, port, domain, client_addr, checkRetriable());
     if (fd >= 0) {
         debugs(17, 3, "fwdConnectStart: reusing pconn FD " << fd);
index c940a4927d3996d6f273ce9d730729d62a3ba134..f20c942c9efd18ee8b600165df9be2ab841674aa 100644 (file)
@@ -383,7 +383,7 @@ HttpStateData::cacheableReply()
         }
     }
 
-    if (request->flags.auth) {
+    if (request->flags.auth || request->flags.auth_sent) {
         /*
          * Responses to requests with authorization may be cached
          * only if a Cache-Control: public reply header is present.
@@ -715,6 +715,9 @@ HttpStateData::processReplyHeader()
         httpChunkDecoder = new ChunkedCodingParser;
     }
 
+    if(!peerSupportsConnectionPinning())
+       orig_request->flags.connection_auth_disabled = 1;
+
     HttpReply *vrep = setVirginReply(newrep);
     flags.headers_parsed = 1;
 
@@ -732,6 +735,67 @@ HttpStateData::processReplyHeader()
 
 }
 
+/**
+ * returns true if the peer can support connection pinning
+*/
+bool HttpStateData::peerSupportsConnectionPinning() const
+{
+    const HttpReply *rep = entry->mem_obj->getReply();
+    const HttpHeader *hdr = &rep->header;
+    bool rc;
+    String header;
+
+    if (!_peer)
+       return true;
+    
+    /*If this peer does not support connection pinning (authenticated 
+      connections) return false
+     */
+    if (!_peer->connection_auth)
+       return false;
+
+    /*The peer supports connection pinning and the http reply status 
+      is not unauthorized, so the related connection can be pinned
+     */
+    if (rep->sline.status != HTTP_UNAUTHORIZED)
+       return true;
+    
+    /*The server respond with HTTP_UNAUTHORIZED and the peer configured 
+      with "connection-auth=on" we know that the peer supports pinned 
+      connections
+    */
+    if (_peer->connection_auth == 1)
+       return true;
+
+    /*At this point peer has configured with "connection-auth=auto" 
+      parameter so we need some extra checks to decide if we are going 
+      to allow pinned connections or not
+    */
+
+    /*if the peer configured with originserver just allow connection 
+        pinning (squid 2.6 behaviour)
+     */
+    if (_peer->options.originserver)
+       return true;
+
+    /*if the connections it is already pinned it is OK*/
+    if (request->flags.pinned)
+       return true;
+    
+    /*Allow pinned connections only if the Proxy-support header exists in 
+      reply and has in its list the "Session-Based-Authentication" 
+      which means that the peer supports connection pinning.
+     */
+    if (!hdr->has(HDR_PROXY_SUPPORT))
+       return false;
+
+    header = hdr->getStrOrList(HDR_PROXY_SUPPORT);
+    /* XXX This ought to be done in a case-insensitive manner */
+    rc = (strstr(header.buf(), "Session-Based-Authentication") != NULL);
+
+    return rc;
+}
+
 // Called when we parsed (and possibly adapted) the headers but
 // had not starting storing (a.k.a., sending) the body yet.
 void
@@ -1141,6 +1205,7 @@ HttpStateData::processReplyBody()
 {
     AsyncCall::Pointer call;
     IPAddress client_addr;
+    bool ispinned = false;
 
     if (!flags.headers_parsed) {
         flags.do_next_read = 1;
@@ -1206,7 +1271,17 @@ HttpStateData::processReplyBody()
             if (orig_request->flags.spoof_client_ip)
                 client_addr = orig_request->client_addr;
 
-            if (_peer) {
+
+           if (request->flags.pinned) {
+               ispinned = true;
+           } else if (request->flags.connection_auth && request->flags.auth_sent) {
+               ispinned = true;
+           }
+          
+           if (orig_request->pinnedConnection() && ispinned) {
+               orig_request->pinnedConnection()->pinConnection(fd, orig_request, _peer, 
+                                                               (request->flags.connection_auth != 0));
+           } else if (_peer) {
                 if (_peer->options.originserver)
                     fwd->pconnPush(fd, _peer->name, orig_request->port, orig_request->GetHost(), client_addr);
                 else
@@ -1715,7 +1790,7 @@ HttpStateData::decideIfWeDoRanges (HttpRequest * orig_request)
      */
 
     if (NULL == orig_request->range || !orig_request->flags.cachable
-            || orig_request->range->offsetLimitExceeded())
+            || orig_request->range->offsetLimitExceeded() || orig_request->flags.connection_auth)
         result = false;
 
         debugs(11, 8, "decideIfWeDoRanges: range specs: " <<
@@ -1745,6 +1820,12 @@ HttpStateData::buildRequestPrefix(HttpRequest * request,
         HttpHeader hdr(hoRequest);
         Packer p;
         httpBuildRequestHeader(request, orig_request, entry, &hdr, flags);
+       
+       if (request->flags.pinned && request->flags.connection_auth)
+            request->flags.auth_sent = 1;
+        else if (hdr.has(HDR_AUTHORIZATION))
+            request->flags.auth_sent = 1;
+
         packerToMemInit(&p, mb);
         hdr.packInto(&p);
         hdr.clean();
@@ -1798,7 +1879,9 @@ HttpStateData::sendRequest()
     /*
      * Is keep-alive okay for all request methods?
      */
-    if (!Config.onoff.server_pconns)
+    if (orig_request->flags.must_keepalive)
+       flags.keepalive = 1;
+    else if (!Config.onoff.server_pconns)
         flags.keepalive = 0;
     else if (_peer == NULL)
         flags.keepalive = 1;
index 59bc5d71d1aafe61359928db8178b62fc3cdd631..7c1ee2daa983e76183a9792f478c877f2afc7855 100644 (file)
@@ -118,6 +118,7 @@ private:
                                  MemBuf * mb,
                                  http_state_flags flags);
     static bool decideIfWeDoRanges (HttpRequest * orig_request);
+    bool peerSupportsConnectionPinning() const;
 
     ChunkedCodingParser *httpChunkDecoder;
 private:
index c01498c8321d192f4f8608314c446bd8b9209f6c..cb8bd183adb4c3ca3eeeb8d092bccfc17b749fe8 100644 (file)
@@ -51,7 +51,7 @@
 /* count mcast group peers every 15 minutes */
 #define MCAST_COUNT_RATE 900
 
-static int peerAllowedToUse(const peer *, HttpRequest *);
+int peerAllowedToUse(const peer *, HttpRequest *);
 static int peerWouldBePinged(const peer *, HttpRequest *);
 static void neighborRemove(peer *);
 static void neighborAlive(peer *, const MemObject *, const icp_common_t *);
@@ -136,7 +136,7 @@ neighborType(const peer * p, const HttpRequest * request)
  * this function figures out if it is appropriate to fetch REQUEST
  * from PEER.
  */
-static int
+int
 peerAllowedToUse(const peer * p, HttpRequest * request)
 {
 
@@ -1653,6 +1653,13 @@ dump_peer_options(StoreEntry * sentry, peer * p)
     if (p->domain)
         storeAppendPrintf(sentry, " forceddomain=%s", p->domain);
 
+    if(p->connection_auth == 0)
+       storeAppendPrintf(sentry, " connection-auth=off");
+    else if(p->connection_auth == 1)
+       storeAppendPrintf(sentry, " connection-auth=on");
+    else if(p->connection_auth == 2)
+       storeAppendPrintf(sentry, " connection-auth=auto");
+
     storeAppendPrintf(sentry, "\n");
 }
 
index 952f2341c93331b3906bec80356631cd72d2dcce..b0e5008d9e11ceb6fb349cd2314b96b8f69649f2 100644 (file)
@@ -100,6 +100,7 @@ static void peerGetSomeDirect(ps_state *);
 static void peerGetSomeParent(ps_state *);
 static void peerGetAllParents(ps_state *);
 static void peerAddFwdServer(FwdServer **, peer *, hier_code);
+static void peerSelectPinned(ps_state * ps);
 
 CBDATA_CLASS_INIT(ps_state);
 
@@ -322,6 +323,8 @@ peerSelectFoo(ps_state * ps)
         debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]);
     }
 
+    if (!entry || entry->ping_status == PING_NONE)
+        peerSelectPinned(ps);
     if (entry == NULL) {
         (void) 0;
     } else if (entry->ping_status == PING_NONE) {
@@ -362,6 +365,33 @@ peerSelectFoo(ps_state * ps)
     peerSelectCallback(ps);
 }
 
+/*
+ * peerSelectPinned
+ *
+ * Selects a pinned connection
+ */
+int peerAllowedToUse(const peer * p, HttpRequest * request);
+static void
+peerSelectPinned(ps_state * ps)
+{
+    HttpRequest *request = ps->request;
+    peer *peer;
+    if (!request->pinnedConnection())
+        return;
+    if (request->pinnedConnection()->validatePinnedConnection(request) != -1) {
+       peer = request->pinnedConnection()->pinnedPeer();
+        if (peer && peerAllowedToUse(peer, request)) {
+            peerAddFwdServer(&ps->servers, peer, PINNED);
+            if (ps->entry)
+                ps->entry->ping_status = PING_DONE;     /* Skip ICP */
+        } else if (!peer && ps->direct != DIRECT_NO) {
+            peerAddFwdServer(&ps->servers, NULL, PINNED);
+            if (ps->entry)
+                ps->entry->ping_status = PING_DONE;     /* Skip ICP */
+        }
+    }
+}
+
 /*
  * peerGetSomeNeighbor
  * 
index d47164f6d97b0acdfd9f3da740e05cbae95dc0ad..6057b4ba425b965916ed1c174bf8177df175a4c7 100644 (file)
@@ -1016,6 +1016,7 @@ struct peer
 #endif
 
     int front_end_https;
+    int connection_auth;
 };
 
 struct _net_db_name
@@ -1100,6 +1101,11 @@ struct request_flags
     unsigned int internal:1;
     unsigned int internalclient:1;
     unsigned int must_keepalive:1;
+    unsigned int connection_auth:1; /** Request wants connection oriented auth */
+    unsigned int connection_auth_disabled:1; /** Connection oriented auth can not be supported */
+    unsigned int connection_proxy_auth:1; /** Request wants connection oriented auth */
+    unsigned int pinned:1;      /* Request sent on a pinned connection */
+    unsigned int auth_sent:1;   /* Authentication forwarded */
 
     // When adding new flags, please update cloneAdaptationImmune() as needed.
 
index 1e183284282b331693f5c9c2794e1148a2530d75..8c3b9552261ae7937f79ae16c1f789de98242354 100644 (file)
@@ -115,3 +115,10 @@ HttpReply::clone() const
     fatal("Not implemented");
     return NULL;
 }
+
+bool
+HttpReply::inheritProperties(const HttpMsg *aMsg)
+{
+    fatal("Not implemented");
+    return false;
+}
index a3f65ce7a8cd13ed00d6bef8208ff0d47aa88882..f4bc033c9d9a0dcd1a3cff6566023adb68695eb7 100644 (file)
@@ -101,6 +101,13 @@ HttpRequest::clone() const
     return NULL;
 }
 
+bool
+HttpRequest::inheritProperties(const HttpMsg *aMsg)
+{
+    fatal("Not implemented");
+    return false;
+}
+
 /*
  * DO NOT MODIFY:
  * arch-tag: dd894aa8-63cc-4543-92d9-1079a18bee11