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 */