--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/squid/patches/CVE-2016-3948.patch Wed Apr 13 10:14:18 2016 -0700
@@ -0,0 +1,416 @@
+Fix for CVE-2016-3948. See:
+
+ http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-3948
+
+for more details. Based on the squid version 3.5.X patch at:
+
+ http://www.squid-cache.org/Versions/v3/3.5/changesets/squid-3.5-14016.patch
+
+--- squid-3.5.5/src/HttpRequest.cc.orig 2016-04-12 11:32:45.105453713 -0700
++++ squid-3.5.5/src/HttpRequest.cc 2016-04-12 11:34:54.820733440 -0700
+@@ -92,7 +92,7 @@
+ peer_login = NULL; // not allocated/deallocated by this class
+ peer_domain = NULL; // not allocated/deallocated by this class
+ peer_host = NULL;
+- vary_headers = NULL;
++ vary_headers = SBuf();
+ myportname = null_string;
+ tag = null_string;
+ #if USE_AUTH
+@@ -124,9 +124,7 @@
+ auth_user_request = NULL;
+ #endif
+ safe_free(canonical);
+-
+- safe_free(vary_headers);
+-
++ vary_headers.clear();
+ url.clear();
+ urlpath.clean();
+
+@@ -203,7 +201,7 @@
+
+ copy->lastmod = lastmod;
+ copy->etag = etag;
+- copy->vary_headers = vary_headers ? xstrdup(vary_headers) : NULL;
++ copy->vary_headers = vary_headers;
+ // XXX: what to do with copy->peer_domain?
+
+ copy->tag = tag;
+--- squid-3.5.5/src/HttpRequest.h.orig 2016-04-12 11:32:45.107463536 -0700
++++ squid-3.5.5/src/HttpRequest.h 2016-04-12 11:35:03.221096498 -0700
+@@ -179,7 +179,8 @@
+
+ time_t lastmod; /* Used on refreshes */
+
+- const char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */
++ /// The variant second-stage cache key. Generated from Vary header pattern for this request.
++ SBuf vary_headers;
+
+ char *peer_domain; /* Configured peer forceddomain */
+
+--- squid-3.5.5/src/MemObject.cc.orig 2016-04-12 11:32:45.109364872 -0700
++++ squid-3.5.5/src/MemObject.cc 2016-04-12 11:35:07.861297412 -0700
+@@ -136,8 +136,6 @@
+ HTTPMSGUNLOCK(request);
+
+ ctx_exit(ctx); /* must exit before we free mem->url */
+-
+- safe_free(vary_headers);
+ }
+
+ void
+@@ -221,8 +219,8 @@
+ MemObject::stat(MemBuf * mb) const
+ {
+ mb->Printf("\t" SQUIDSBUFPH " %s\n", SQUIDSBUFPRINT(method.image()), logUri());
+- if (vary_headers)
+- mb->Printf("\tvary_headers: %s\n", vary_headers);
++ if (!vary_headers.isEmpty())
++ mb->Printf("\tvary_headers: " SQUIDSBUFPH "\n", SQUIDSBUFPRINT(vary_headers));
+ mb->Printf("\tinmem_lo: %" PRId64 "\n", inmem_lo);
+ mb->Printf("\tinmem_hi: %" PRId64 "\n", data_hdr.endOffset());
+ mb->Printf("\tswapout: %" PRId64 " bytes queued\n",
+--- squid-3.5.5/src/MemObject.h.orig 2016-04-12 11:32:45.111075168 -0700
++++ squid-3.5.5/src/MemObject.h 2016-04-12 11:35:10.909852018 -0700
+@@ -13,6 +13,7 @@
+ #include "dlink.h"
+ #include "HttpRequestMethod.h"
+ #include "RemovalPolicy.h"
++#include "SBuf.h"
+ #include "stmem.h"
+ #include "StoreIOBuffer.h"
+ #include "StoreIOState.h"
+@@ -167,7 +168,7 @@
+ unsigned int chksum;
+ #endif
+
+- const char *vary_headers;
++ SBuf vary_headers;
+
+ void delayRead(DeferredRead const &);
+ void kickReads();
+--- squid-3.5.5/src/MemStore.cc.orig 2016-04-12 11:32:45.112589100 -0700
++++ squid-3.5.5/src/MemStore.cc 2016-04-12 11:35:13.878529885 -0700
+@@ -443,7 +443,7 @@
+
+ assert(e.mem_obj);
+
+- if (e.mem_obj->vary_headers) {
++ if (!e.mem_obj->vary_headers.isEmpty()) {
+ // XXX: We must store/load SerialisedMetaData to cache Vary in RAM
+ debugs(20, 5, "Vary not yet supported: " << e.mem_obj->vary_headers);
+ return false;
+--- squid-3.5.5/src/StoreMetaVary.cc.orig 2016-04-12 11:32:45.114054766 -0700
++++ squid-3.5.5/src/StoreMetaVary.cc 2016-04-12 11:35:17.350257452 -0700
+@@ -18,14 +18,14 @@
+ {
+ assert (getType() == STORE_META_VARY_HEADERS);
+
+- if (!e->mem_obj->vary_headers) {
++ if (e->mem_obj->vary_headers.isEmpty()) {
+ /* XXX separate this mutator from the query */
+ /* Assume the object is OK.. remember the vary request headers */
+- e->mem_obj->vary_headers = xstrdup((char *)value);
++ e->mem_obj->vary_headers.assign(static_cast<const char *>(value), length);
+ return true;
+ }
+
+- if (strcmp(e->mem_obj->vary_headers, (char *)value) != 0)
++ if (e->mem_obj->vary_headers.cmp(static_cast<const char *>(value), length) != 0)
+ return false;
+
+ return true;
+--- squid-3.5.5/src/client_side.cc.orig 2016-04-12 11:30:22.544117692 -0700
++++ squid-3.5.5/src/client_side.cc 2016-04-12 11:35:20.775116943 -0700
+@@ -4627,20 +4627,19 @@
+ int
+ varyEvaluateMatch(StoreEntry * entry, HttpRequest * request)
+ {
+- const char *vary = request->vary_headers;
++ SBuf vary(request->vary_headers);
+ int has_vary = entry->getReply()->header.has(HDR_VARY);
+ #if X_ACCELERATOR_VARY
+-
+ has_vary |=
+ entry->getReply()->header.has(HDR_X_ACCELERATOR_VARY);
+ #endif
+
+- if (!has_vary || !entry->mem_obj->vary_headers) {
+- if (vary) {
++ if (!has_vary || entry->mem_obj->vary_headers.isEmpty()) {
++ if (!vary.isEmpty()) {
+ /* Oops... something odd is going on here.. */
+ debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
+ entry->mem_obj->urlXXX() << "' '" << vary << "'");
+- safe_free(request->vary_headers);
++ request->vary_headers.clear();
+ return VARY_CANCEL;
+ }
+
+@@ -4654,8 +4653,8 @@
+ */
+ vary = httpMakeVaryMark(request, entry->getReply());
+
+- if (vary) {
+- request->vary_headers = xstrdup(vary);
++ if (!vary.isEmpty()) {
++ request->vary_headers = vary;
+ return VARY_OTHER;
+ } else {
+ /* Ouch.. we cannot handle this kind of variance */
+@@ -4663,18 +4662,18 @@
+ return VARY_CANCEL;
+ }
+ } else {
+- if (!vary) {
++ if (vary.isEmpty()) {
+ vary = httpMakeVaryMark(request, entry->getReply());
+
+- if (vary)
+- request->vary_headers = xstrdup(vary);
++ if (!vary.isEmpty())
++ request->vary_headers = vary;
+ }
+
+- if (!vary) {
++ if (vary.isEmpty()) {
+ /* Ouch.. we cannot handle this kind of variance */
+ /* XXX This cannot really happen, but just to be complete */
+ return VARY_CANCEL;
+- } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) {
++ } else if (vary.cmp(entry->mem_obj->vary_headers) == 0) {
+ return VARY_MATCH;
+ } else {
+ /* Oops.. we have already been here and still haven't
+--- squid-3.5.5/src/client_side_reply.cc.orig 2016-04-12 11:30:24.187260295 -0700
++++ squid-3.5.5/src/client_side_reply.cc 2016-04-12 11:35:25.478369424 -0700
+@@ -988,9 +988,8 @@
+ }
+
+ /* And for Vary, release the base URI if none of the headers was included in the request */
+-
+- if (http->request->vary_headers
+- && !strstr(http->request->vary_headers, "=")) {
++ if (!http->request->vary_headers.isEmpty()
++ && http->request->vary_headers.find('=') != SBuf::npos) {
+ StoreEntry *entry = storeGetPublic(urlCanonical(http->request), Http::METHOD_GET);
+
+ if (entry) {
+--- squid-3.5.5/src/http.cc.orig 2016-04-12 11:30:25.907567935 -0700
++++ squid-3.5.5/src/http.cc 2016-04-12 11:35:29.150761600 -0700
+@@ -573,9 +573,9 @@
+ /*
+ * For Vary, store the relevant request headers as
+ * virtual headers in the reply
+- * Returns false if the variance cannot be stored
++ * Returns an empty SBuf if the variance cannot be stored
+ */
+-const char *
++SBuf
+ httpMakeVaryMark(HttpRequest * request, HttpReply const * reply)
+ {
+ String vary, hdr;
+@@ -583,9 +583,9 @@
+ const char *item;
+ const char *value;
+ int ilen;
+- static String vstr;
++ SBuf vstr;
++ static const SBuf asterisk("*");
+
+- vstr.clean();
+ vary = reply->header.getList(HDR_VARY);
+
+ while (strListGetItem(&vary, ',', &item, &ilen, &pos)) {
+@@ -596,11 +596,13 @@
+ if (strcmp(name, "*") == 0) {
+ /* Can not handle "Vary: *" withtout ETag support */
+ safe_free(name);
+- vstr.clean();
++ vstr.clear();
+ break;
+ }
+
+- strListAdd(&vstr, name, ',');
++ if (!vstr.isEmpty())
++ vstr.append(", ", 2);
++ vstr.append(name);
+ hdr = request->header.getByName(name);
+ safe_free(name);
+ value = hdr.termedBuf();
+@@ -625,7 +627,17 @@
+ char *name = (char *)xmalloc(ilen + 1);
+ xstrncpy(name, item, ilen + 1);
+ Tolower(name);
+- strListAdd(&vstr, name, ',');
++
++ if (strcmp(name, "*") == 0) {
++ /* Can not handle "Vary: *" withtout ETag support */
++ safe_free(name);
++ vstr.clear();
++ break;
++ }
++
++ if (!vstr.isEmpty())
++ vstr.append(", ", 2);
++ vstr.append(name);
+ hdr = request->header.getByName(name);
+ safe_free(name);
+ value = hdr.termedBuf();
+@@ -643,8 +655,8 @@
+ vary.clean();
+ #endif
+
+- debugs(11, 3, "httpMakeVaryMark: " << vstr);
+- return vstr.termedBuf();
++ debugs(11, 3, vstr);
++ return vstr;
+ }
+
+ void
+@@ -917,15 +929,15 @@
+ || rep->header.has(HDR_X_ACCELERATOR_VARY)
+ #endif
+ ) {
+- const char *vary = httpMakeVaryMark(request, rep);
++ const SBuf vary(httpMakeVaryMark(request, rep));
+
+- if (!vary) {
++ if (vary.isEmpty()) {
+ entry->makePrivate();
+ if (!fwd->reforwardableStatus(rep->sline.status()))
+ EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
+ varyFailure = true;
+ } else {
+- entry->mem_obj->vary_headers = xstrdup(vary);
++ entry->mem_obj->vary_headers = vary;
+ }
+ }
+
+--- squid-3.5.5/src/http.h.orig 2016-04-12 11:30:26.674812741 -0700
++++ squid-3.5.5/src/http.h 2016-04-12 11:35:34.119393224 -0700
+@@ -12,6 +12,7 @@
+ #include "clients/Client.h"
+ #include "comm.h"
+ #include "HttpStateFlags.h"
++#include "SBuf.h"
+
+ class ChunkedCodingParser;
+ class FwdState;
+@@ -116,7 +117,7 @@
+
+ int httpCachable(const HttpRequestMethod&);
+ void httpStart(FwdState *);
+-const char *httpMakeVaryMark(HttpRequest * request, HttpReply const * reply);
++SBuf httpMakeVaryMark(HttpRequest * request, HttpReply const * reply);
+
+ #endif /* SQUID_HTTP_H */
+
+--- squid-3.5.5/src/store.cc.orig 2016-04-12 11:30:28.336891365 -0700
++++ squid-3.5.5/src/store.cc 2016-04-12 11:35:38.983063089 -0700
+@@ -685,31 +685,27 @@
+ if (mem_obj->request) {
+ HttpRequest *request = mem_obj->request;
+
+- if (!mem_obj->vary_headers) {
++ if (mem_obj->vary_headers.isEmpty()) {
+ /* First handle the case where the object no longer varies */
+- safe_free(request->vary_headers);
++ request->vary_headers.clear();
+ } else {
+- if (request->vary_headers && strcmp(request->vary_headers, mem_obj->vary_headers) != 0) {
++ if (!request->vary_headers.isEmpty() && request->vary_headers.cmp(mem_obj->vary_headers) != 0) {
+ /* Oops.. the variance has changed. Kill the base object
+ * to record the new variance key
+ */
+- safe_free(request->vary_headers); /* free old "bad" variance key */
++ request->vary_headers.clear(); /* free old "bad" variance key */
+ if (StoreEntry *pe = storeGetPublic(mem_obj->storeId(), mem_obj->method))
+ pe->release();
+ }
+
+ /* Make sure the request knows the variance status */
+- if (!request->vary_headers) {
+- const char *vary = httpMakeVaryMark(request, mem_obj->getReply());
+-
+- if (vary)
+- request->vary_headers = xstrdup(vary);
+- }
++ if (request->vary_headers.isEmpty())
++ request->vary_headers = httpMakeVaryMark(request, mem_obj->getReply());
+ }
+
+ // TODO: storeGetPublic() calls below may create unlocked entries.
+ // We should add/use storeHas() API or lock/unlock those entries.
+- if (mem_obj->vary_headers && !storeGetPublic(mem_obj->storeId(), mem_obj->method)) {
++ if (!mem_obj->vary_headers.isEmpty() && !storeGetPublic(mem_obj->storeId(), mem_obj->method)) {
+ /* Create "vary" base object */
+ String vary;
+ StoreEntry *pe = storeCreateEntry(mem_obj->storeId(), mem_obj->logUri(), request->flags, request->method);
+--- squid-3.5.5/src/store_key_md5.cc.orig 2016-04-12 11:32:45.123264698 -0700
++++ squid-3.5.5/src/store_key_md5.cc 2016-04-12 11:35:42.319388524 -0700
+@@ -125,8 +125,8 @@
+ SquidMD5Update(&M, &m, sizeof(m));
+ SquidMD5Update(&M, (unsigned char *) url, strlen(url));
+
+- if (request->vary_headers) {
+- SquidMD5Update(&M, (unsigned char *) request->vary_headers, strlen(request->vary_headers));
++ if (!request->vary_headers.isEmpty()) {
++ SquidMD5Update(&M, request->vary_headers.rawContent(), request->vary_headers.length());
+ debugs(20, 3, "updating public key by vary headers: " << request->vary_headers << " for: " << url);
+ }
+
+--- squid-3.5.5/src/store_swapmeta.cc.orig 2016-04-12 11:32:45.124659332 -0700
++++ squid-3.5.5/src/store_swapmeta.cc 2016-04-12 11:35:45.927230596 -0700
+@@ -40,7 +40,6 @@
+ tlv *TLV = NULL; /* we'll return this */
+ tlv **T = &TLV;
+ const char *url;
+- const char *vary;
+ assert(e->mem_obj != NULL);
+ const int64_t objsize = e->mem_obj->expectedReplySize();
+ assert(e->swap_status == SWAPOUT_WRITING);
+@@ -87,10 +86,12 @@
+ }
+
+ T = StoreMeta::Add(T, t);
+- vary = e->mem_obj->vary_headers;
++ SBuf vary(e->mem_obj->vary_headers);
+
+- if (vary) {
+- t =StoreMeta::Factory(STORE_META_VARY_HEADERS, strlen(vary) + 1, vary);
++ if (!vary.isEmpty()) {
++ // TODO: do we still need +1 here? StoreMetaVary::checkConsistency
++ // no longer relies on nul-termination, but other things might.
++ t = StoreMeta::Factory(STORE_META_VARY_HEADERS, vary.length() + 1, vary.c_str());
+
+ if (!t) {
+ storeSwapTLVFree(TLV);
+--- squid-3.5.5/src/tests/stub_MemObject.cc.orig 2016-04-12 11:34:21.726285840 -0700
++++ squid-3.5.5/src/tests/stub_MemObject.cc 2016-04-12 11:35:49.383455312 -0700
+@@ -38,7 +38,6 @@
+ id(0),
+ object_sz(-1),
+ swap_hdr_sz(0),
+- vary_headers(NULL),
+ _reply(NULL)
+ {
+ memset(&clients, 0, sizeof(clients));
+--- squid-3.5.5/src/tests/stub_http.cc.orig 2016-04-12 11:34:21.728074407 -0700
++++ squid-3.5.5/src/tests/stub_http.cc 2016-04-12 11:35:52.784226579 -0700
+@@ -7,12 +7,12 @@
+ */
+
+ #include "squid.h"
+-
+ #include "HttpReply.h"
+ #include "HttpRequest.h"
++#include "SBuf.h"
+
+ #define STUB_API "http.cc"
+ #include "tests/STUB.h"
+
+-const char * httpMakeVaryMark(HttpRequest * request, HttpReply const * reply) STUB_RETVAL(NULL)
++SBuf httpMakeVaryMark(HttpRequest *, HttpReply const *) STUB_RETVAL(SBuf())
+