File squid-3.3.8-bnc867533-CVE-2014-0128.diff of Package squid.openSUSE_13.1_Update

diff -rNU 20 ../squid-3.3.8-o/src/client_side.cc ./src/client_side.cc
--- ../squid-3.3.8-o/src/client_side.cc	2013-07-13 15:25:14.000000000 +0200
+++ ./src/client_side.cc	2014-04-09 15:28:15.000000000 +0200
@@ -1264,43 +1264,41 @@
 
     /* hits only - upstream CachePeer determines correct behaviour on misses, and client_side_reply determines
      * hits candidates
      */
     else if (logTypeIsATcpHit(http->logType) && http->request->header.has(HDR_IF_RANGE) && !clientIfRangeMatch(http, rep))
         range_err = "If-Range match failed";
     else if (!http->request->range->canonize(rep))
         range_err = "canonization failed";
     else if (http->request->range->isComplex())
         range_err = "too complex range header";
     else if (!logTypeIsATcpHit(http->logType) && http->request->range->offsetLimitExceeded(roffLimit))
         range_err = "range outside range_offset_limit";
 
     /* get rid of our range specs on error */
     if (range_err) {
         /* XXX We do this here because we need canonisation etc. However, this current
          * code will lead to incorrect store offset requests - the store will have the
          * offset data, but we won't be requesting it.
          * So, we can either re-request, or generate an error
          */
-        debugs(33, 3, "clientBuildRangeHeader: will not do ranges: " << range_err << ".");
-        delete http->request->range;
-        http->request->range = NULL;
+        http->request->ignoreRange(range_err);
     } else {
         /* XXX: TODO: Review, this unconditional set may be wrong. - TODO: review. */
         httpStatusLineSet(&rep->sline, rep->sline.version,
                           HTTP_PARTIAL_CONTENT, NULL);
         // web server responded with a valid, but unexpected range.
         // will (try-to) forward as-is.
         //TODO: we should cope with multirange request/responses
         bool replyMatchRequest = rep->content_range != NULL ?
                                  request->range->contains(rep->content_range->spec) :
                                  true;
         const int spec_count = http->request->range->specs.count;
         int64_t actual_clen = -1;
 
         debugs(33, 3, "clientBuildRangeHeader: range spec count: " <<
                spec_count << " virgin clen: " << rep->content_length);
         assert(spec_count > 0);
         /* append appropriate header(s) */
 
         if (spec_count == 1) {
             if (!replyMatchRequest) {
@@ -1645,80 +1643,87 @@
     if (!http->range_iter.debt()) {
         debugs(33, 5, HERE << "At end of current range spec for " << clientConnection);
 
         if (http->range_iter.pos.incrementable())
             ++http->range_iter.pos;
 
         http->range_iter.updateSpec();
     }
 
     assert(!http->range_iter.debt() == !http->range_iter.currentSpec());
 
     /* paranoid sync condition */
     /* continue condition: need_more_data */
     debugs(33, 5, "ClientSocketContext::canPackMoreRanges: returning " << (http->range_iter.currentSpec() ? true : false));
     return http->range_iter.currentSpec() ? true : false;
 }
 
 int64_t
 ClientSocketContext::getNextRangeOffset() const
 {
+    debugs (33, 5, "range: " << http->request->range <<
+            "; http offset " << http->out.offset <<
+            "; reply " << reply);
+
+    // XXX: This method is called from many places, including pullData() which
+    // may be called before prepareReply() [on some Squid-generated errors].
+    // Hence, we may not even know yet whether we should honor/do ranges.
+
     if (http->request->range) {
         /* offset in range specs does not count the prefix of an http msg */
-        debugs (33, 5, "ClientSocketContext::getNextRangeOffset: http offset " << http->out.offset);
         /* check: reply was parsed and range iterator was initialized */
         assert(http->range_iter.valid);
         /* filter out data according to range specs */
         assert (canPackMoreRanges());
         {
             int64_t start;		/* offset of still missing data */
             assert(http->range_iter.currentSpec());
             start = http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length - http->range_iter.debt();
             debugs(33, 3, "clientPackMoreRanges: in:  offset: " << http->out.offset);
             debugs(33, 3, "clientPackMoreRanges: out:"
                    " start: " << start <<
                    " spec[" << http->range_iter.pos - http->request->range->begin() << "]:" <<
                    " [" << http->range_iter.currentSpec()->offset <<
                    ", " << http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length << "),"
                    " len: " << http->range_iter.currentSpec()->length <<
                    " debt: " << http->range_iter.debt());
             if (http->range_iter.currentSpec()->length != -1)
                 assert(http->out.offset <= start);	/* we did not miss it */
 
             return start;
         }
 
     } else if (reply && reply->content_range) {
         /* request does not have ranges, but reply does */
         /** \todo FIXME: should use range_iter_pos on reply, as soon as reply->content_range
          *        becomes HttpHdrRange rather than HttpHdrRangeSpec.
          */
         return http->out.offset + reply->content_range->spec.offset;
     }
 
     return http->out.offset;
 }
 
 void
 ClientSocketContext::pullData()
 {
-    debugs(33, 5, HERE << clientConnection << " attempting to pull upstream data");
+    debugs(33, 5, reply << " written " << http->out.size << " into " << clientConnection);
 
     /* More data will be coming from the stream. */
     StoreIOBuffer readBuffer;
     /* XXX: Next requested byte in the range sequence */
     /* XXX: length = getmaximumrangelenfgth */
     readBuffer.offset = getNextRangeOffset();
     readBuffer.length = HTTP_REQBUF_SZ;
     readBuffer.data = reqbuf;
     /* we may note we have reached the end of the wanted ranges */
     clientStreamRead(getTail(), http, readBuffer);
 }
 
 clientStream_status_t
 ClientSocketContext::socketState()
 {
     switch (clientStreamStatus(getTail(), http)) {
 
     case STREAM_NONE:
         /* check for range support ending */
 
@@ -2475,41 +2480,41 @@
 
 #if USE_SSL
 bool ConnStateData::serveDelayedError(ClientSocketContext *context)
 {
     ClientHttpRequest *http = context->http;
 
     if (!sslServerBump)
         return false;
 
     assert(sslServerBump->entry);
     // Did we create an error entry while processing CONNECT?
     if (!sslServerBump->entry->isEmpty()) {
         quitAfterError(http->request);
 
         // Get the saved error entry and send it to the client by replacing the
         // ClientHttpRequest store entry with it.
         clientStreamNode *node = context->getClientReplyContext();
         clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
         assert(repContext);
         debugs(33, 5, "Responding with delated error for " << http->uri);
-        repContext->setReplyToStoreEntry(sslServerBump->entry);
+        repContext->setReplyToStoreEntry(sslServerBump->entry, "delayed SslBump error");
 
         // save the original request for logging purposes
         if (!context->http->al->request)
             context->http->al->request = HTTPMSGLOCK(http->request);
 
         // Get error details from the fake certificate-peeking request.
         http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
         context->pullData();
         return true;
     }
 
     // In bump-server-first mode, we have not necessarily seen the intended
     // server name at certificate-peeking time. Check for domain mismatch now,
     // when we can extract the intended name from the bumped HTTP request.
     if (sslServerBump->serverCert.get()) {
         HttpRequest *request = http->request;
         if (!Ssl::checkX509ServerValidity(sslServerBump->serverCert.get(), request->GetHost())) {
             debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
                    "does not match domainname " << request->GetHost());
 
diff -rNU 20 ../squid-3.3.8-o/src/client_side_reply.cc ./src/client_side_reply.cc
--- ../squid-3.3.8-o/src/client_side_reply.cc	2013-07-13 15:25:14.000000000 +0200
+++ ./src/client_side_reply.cc	2014-04-09 15:28:15.000000000 +0200
@@ -116,55 +116,62 @@
 {
     ErrorState *errstate = clientBuildError(err, status, uri, addr, failedrequest);
 
     if (unparsedrequest)
         errstate->request_hdrs = xstrdup(unparsedrequest);
 
 #if USE_AUTH
     errstate->auth_user_request = auth_user_request;
 #endif
     setReplyToError(method, errstate);
 }
 
 void clientReplyContext::setReplyToError(const HttpRequestMethod& method, ErrorState *errstate)
 {
     if (errstate->httpStatus == HTTP_NOT_IMPLEMENTED && http->request)
         /* prevent confusion over whether we default to persistent or not */
         http->request->flags.proxyKeepalive = 0;
 
     http->al->http.code = errstate->httpStatus;
 
+    if (http->request)
+        http->request->ignoreRange("responding with a Squid-generated error");
+
     createStoreEntry(method, RequestFlags());
     assert(errstate->callback_data == NULL);
     errorAppendEntry(http->storeEntry(), errstate);
     /* Now the caller reads to get this */
 }
 
-void clientReplyContext::setReplyToStoreEntry(StoreEntry *entry)
+// Assumes that the entry contains an error response without Content-Range.
+// To use with regular entries, make HTTP Range header removal conditional.
+void clientReplyContext::setReplyToStoreEntry(StoreEntry *entry, const char *reason)
 {
     entry->lock(); // removeClientStoreReference() unlocks
     sc = storeClientListAdd(entry, this);
 #if USE_DELAY_POOLS
     sc->setDelayId(DelayId::DelayClient(http));
 #endif
     reqofs = 0;
     reqsize = 0;
+    if (http->request)
+        http->request->ignoreRange(reason);
     flags.storelogiccomplete = 1;
     http->storeEntry(entry);
 }
 
 void
 clientReplyContext::removeStoreReference(store_client ** scp,
         StoreEntry ** ep)
 {
     StoreEntry *e;
     store_client *sc_tmp = *scp;
 
     if ((e = *ep) != NULL) {
         *ep = NULL;
         storeUnregister(sc_tmp, e, this);
         *scp = NULL;
         e->unlock();
     }
 }
 
 void
diff -rNU 20 ../squid-3.3.8-o/src/client_side_reply.h ./src/client_side_reply.h
--- ../squid-3.3.8-o/src/client_side_reply.h	2013-07-13 15:25:14.000000000 +0200
+++ ./src/client_side_reply.h	2014-04-09 15:28:15.000000000 +0200
@@ -56,41 +56,41 @@
     clientReplyContext(ClientHttpRequest *);
     ~clientReplyContext();
 
     void saveState();
     void restoreState();
     void purgeRequest ();
     void purgeRequestFindObjectToPurge();
     void purgeDoMissPurge();
     void purgeFoundGet(StoreEntry *newEntry);
     void purgeFoundHead(StoreEntry *newEntry);
     void purgeFoundObject(StoreEntry *entry);
     void sendClientUpstreamResponse();
     void purgeDoPurgeGet(StoreEntry *entry);
     void purgeDoPurgeHead(StoreEntry *entry);
     void doGetMoreData();
     void identifyStoreObject();
     void identifyFoundObject(StoreEntry *entry);
     int storeOKTransferDone() const;
     int storeNotOKTransferDone() const;
     /// replaces current response store entry with the given one
-    void setReplyToStoreEntry(StoreEntry *e);
+    void setReplyToStoreEntry(StoreEntry *e, const char *reason);
     /// builds error using clientBuildError() and calls setReplyToError() below
     void setReplyToError(err_type, http_status, const HttpRequestMethod&, char const *, Ip::Address &, HttpRequest *, const char *,
 #if USE_AUTH
                          Auth::UserRequest::Pointer);
 #else
                          void * unused);
 #endif
     /// creates a store entry for the reply and appends err to it
     void setReplyToError(const HttpRequestMethod& method, ErrorState *err);
     void createStoreEntry(const HttpRequestMethod& m, RequestFlags flags);
     void removeStoreReference(store_client ** scp, StoreEntry ** ep);
     void removeClientStoreReference(store_client **scp, ClientHttpRequest *http);
     void startError(ErrorState * err);
     void processExpired();
     clientStream_status_t replyStatus();
     void processMiss();
     void traceReply(clientStreamNode * node);
 
     http_status purgeStatus;
 
diff -rNU 20 ../squid-3.3.8-o/src/client_side_request.cc ./src/client_side_request.cc
--- ../squid-3.3.8-o/src/client_side_request.cc	2013-07-13 15:25:14.000000000 +0200
+++ ./src/client_side_request.cc	2014-04-09 15:28:15.000000000 +0200
@@ -1113,42 +1113,41 @@
             clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
             /* XXX: This is suboptimal. We should give the stream the range set,
              * and thereby let the top of the stream set the offset when the
              * size becomes known. As it is, we will end up requesting from 0
              * for evey -X range specification.
              * RBC - this may be somewhat wrong. We should probably set the range
              * iter up at this point.
              */
             node->readBuffer.offset = request->range->lowestOffset(0);
             http->range_iter.pos = request->range->begin();
             http->range_iter.valid = true;
         }
     }
 
     /* Only HEAD and GET requests permit a Range or Request-Range header.
      * If these headers appear on any other type of request, delete them now.
      */
     else {
         req_hdr->delById(HDR_RANGE);
         req_hdr->delById(HDR_REQUEST_RANGE);
-        delete request->range;
-        request->range = NULL;
+        request->ignoreRange("neither HEAD nor GET");
     }
 
     if (req_hdr->has(HDR_AUTHORIZATION))
         request->flags.auth = 1;
 
     clientCheckPinning(http);
 
     if (request->login[0] != '\0')
         request->flags.auth = 1;
 
     if (req_hdr->has(HDR_VIA)) {
         String s = req_hdr->getList(HDR_VIA);
         /*
          * ThisCache cannot be a member of Via header, "1.1 ThisCache" can.
          * Note ThisCache2 has a space prepended to the hostname so we don't
          * accidentally match super-domains.
          */
 
         if (strListIsSubstr(&s, ThisCache2, ',')) {
             debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
@@ -1646,41 +1645,41 @@
 #endif
 
     if (calloutContext->error) {
         const char *uri = urlCanonical(request);
         StoreEntry *e= storeCreateEntry(uri, uri, request->flags, request->method);
 #if USE_SSL
         if (sslBumpNeeded()) {
             // set final error but delay sending until we bump
             Ssl::ServerBump *srvBump = new Ssl::ServerBump(request, e);
             errorAppendEntry(e, calloutContext->error);
             calloutContext->error = NULL;
             getConn()->setServerBump(srvBump);
             e->unlock();
         } else
 #endif
         {
             // send the error to the client now
             clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
             clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
             assert (repContext);
-            repContext->setReplyToStoreEntry(e);
+            repContext->setReplyToStoreEntry(e, "immediate SslBump error");
             errorAppendEntry(e, calloutContext->error);
             calloutContext->error = NULL;
             if (calloutContext->readNextRequest)
                 getConn()->flags.readMore = true; // resume any pipeline reads.
             node = (clientStreamNode *)client_stream.tail->data;
             clientStreamRead(node, this, node->readBuffer);
             e->unlock();
             return;
         }
     }
 
     cbdataReferenceDone(calloutContext->http);
     delete calloutContext;
     calloutContext = NULL;
 #if HEADERS_LOG
 
     headersLog(0, 1, request->method, request);
 #endif
 
     debugs(83, 3, HERE << "calling processRequest()");
diff -rNU 20 ../squid-3.3.8-o/src/http.cc ./src/http.cc
--- ../squid-3.3.8-o/src/http.cc	2013-07-13 15:25:14.000000000 +0200
+++ ./src/http.cc	2014-04-09 15:28:15.000000000 +0200
@@ -1716,42 +1716,41 @@
     assert (hdr_out->owner == hoRequest);
 
     /* append our IMS header */
     if (request->lastmod > -1)
         hdr_out->putTime(HDR_IF_MODIFIED_SINCE, request->lastmod);
 
     bool we_do_ranges = decideIfWeDoRanges (request);
 
     String strConnection (hdr_in->getList(HDR_CONNECTION));
 
     while ((e = hdr_in->getEntry(&pos)))
         copyOneHeaderFromClientsideRequestToUpstreamRequest(e, strConnection, request, hdr_out, we_do_ranges, flags);
 
     /* Abstraction break: We should interpret multipart/byterange responses
      * into offset-length data, and this works around our inability to do so.
      */
     if (!we_do_ranges && request->multipartRangeRequest()) {
         /* don't cache the result */
         request->flags.cachable = 0;
         /* pretend it's not a range request */
-        delete request->range;
-        request->range = NULL;
+        request->ignoreRange("want to request the whole object");
         request->flags.isRanged=false;
     }
 
     /* append Via */
     if (Config.onoff.via) {
         String strVia;
         strVia = hdr_in->getList(HDR_VIA);
         snprintf(bbuf, BBUF_SZ, "%d.%d %s",
                  request->http_ver.major,
                  request->http_ver.minor, ThisCache);
         strListAdd(&strVia, bbuf, ',');
         hdr_out->putStr(HDR_VIA, strVia.termedBuf());
         strVia.clean();
     }
 
     if (request->flags.accelerated) {
         /* Append Surrogate-Capabilities */
         String strSurrogate(hdr_in->getList(HDR_SURROGATE_CAPABILITY));
 #if USE_SQUID_ESI
         snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0 ESI/1.0\"", Config.Accel.surrogate_id);
diff -rNU 20 ../squid-3.3.8-o/src/HttpRequest.cc ./src/HttpRequest.cc
--- ../squid-3.3.8-o/src/HttpRequest.cc	2013-07-13 15:25:14.000000000 +0200
+++ ./src/HttpRequest.cc	2014-04-09 15:28:15.000000000 +0200
@@ -638,39 +638,53 @@
         return rangeOffsetLimit;
 
     rangeOffsetLimit = 0; // default value for rangeOffsetLimit
 
     ACLFilledChecklist ch(NULL, this, NULL);
     ch.src_addr = client_addr;
     ch.my_addr =  my_addr;
 
     for (AclSizeLimit *l = Config.rangeOffsetLimit; l; l = l -> next) {
         /* if there is no ACL list or if the ACLs listed match use this limit value */
         if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
             debugs(58, 4, HERE << "rangeOffsetLimit=" << rangeOffsetLimit);
             rangeOffsetLimit = l->size; // may be -1
             break;
         }
     }
 
     return rangeOffsetLimit;
 }
 
+void
+HttpRequest::ignoreRange(const char *reason)
+{
+    if (range) {
+        debugs(73, 3, static_cast<void*>(range) << " for " << reason);
+        delete range;
+        range = NULL;
+    }
+    // Some callers also reset isRanged but it may not be safe for all callers:
+    // isRanged is used to determine whether a weak ETag comparison is allowed,
+    // and that check should not ignore the Range header if it was present.
+    // TODO: Some callers also delete HDR_RANGE, HDR_REQUEST_RANGE. Should we?
+}
+
 bool
 HttpRequest::canHandle1xx() const
 {
     // old clients do not support 1xx unless they sent Expect: 100-continue
     // (we reject all other HDR_EXPECT values so just check for HDR_EXPECT)
     if (http_ver <= HttpVersion(1,0) && !header.has(HDR_EXPECT))
         return false;
 
     // others must support 1xx control messages
     return true;
 }
 
 ConnStateData *
 HttpRequest::pinnedConnection()
 {
     if (clientConnectionManager.valid() && clientConnectionManager->pinning.pinned)
         return clientConnectionManager.get();
     return NULL;
 }
diff -rNU 20 ../squid-3.3.8-o/src/HttpRequest.h ./src/HttpRequest.h
--- ../squid-3.3.8-o/src/HttpRequest.h	2013-07-13 15:25:14.000000000 +0200
+++ ./src/HttpRequest.h	2014-04-09 15:28:15.000000000 +0200
@@ -226,40 +226,42 @@
 
     void swapOut(StoreEntry * e);
 
     void pack(Packer * p);
 
     static void httpRequestPack(void *obj, Packer *p);
 
     static HttpRequest * CreateFromUrlAndMethod(char * url, const HttpRequestMethod& method);
 
     static HttpRequest * CreateFromUrl(char * url);
 
     ConnStateData *pinnedConnection();
 
     /**
      * The client connection manager, if known;
      * Used for any response actions needed directly to the client.
      * ie 1xx forwarding or connection pinning state changes
      */
     CbcPointer<ConnStateData> clientConnectionManager;
 
+    /// forgets about the cached Range header (for a reason)
+    void ignoreRange(const char *reason);
     int64_t getRangeOffsetLimit(); /* the result of this function gets cached in rangeOffsetLimit */
 
 private:
     const char *packableURI(bool full_uri) const;
 
     mutable int64_t rangeOffsetLimit;  /* caches the result of getRangeOffsetLimit */
 
 protected:
     virtual void packFirstLineInto(Packer * p, bool full_uri) const;
 
     virtual bool sanityCheckStartLine(MemBuf *buf, const size_t hdr_len, http_status *error);
 
     virtual void hdrCacheInit();
 
     virtual bool inheritProperties(const HttpMsg *aMsg);
 };
 
 MEMPROXY_CLASS_INLINE(HttpRequest);
 
 #endif /* SQUID_HTTPREQUEST_H */
openSUSE Build Service is sponsored by