зеркало из https://github.com/mozilla/gecko-dev.git
bug 597706 - response header smuggling r=honzab
reject responses with multiple non identical or invalid content-length headers
This commit is contained in:
Родитель
2e6061489a
Коммит
9647e6891f
|
@ -144,6 +144,14 @@
|
|||
#define NS_ERROR_INVALID_CONTENT_ENCODING \
|
||||
NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 27)
|
||||
|
||||
/**
|
||||
* A transport level corruption was found in the source document. for example
|
||||
* a document with a calculated checksum that does not match the Content-MD5
|
||||
* http header.
|
||||
*/
|
||||
#define NS_ERROR_CORRUPTED_CONTENT \
|
||||
NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 29)
|
||||
|
||||
/******************************************************************************
|
||||
* Connectivity error codes:
|
||||
*/
|
||||
|
|
|
@ -137,7 +137,8 @@ nsHttpChunkedDecoder::ParseChunkRemaining(char *buf,
|
|||
LOG(("got trailer: %s\n", buf));
|
||||
// allocate a header array for the trailers on demand
|
||||
if (!mTrailers) {
|
||||
mTrailers = new nsHttpHeaderArray();
|
||||
mTrailers = new nsHttpHeaderArray
|
||||
(nsHttpHeaderArray::HTTP_RESPONSE_HEADERS);
|
||||
if (!mTrailers)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
|
|
@ -85,8 +85,11 @@ nsHttpHeaderArray::SetHeader(nsHttpAtom header,
|
|||
entry->value.Append(value);
|
||||
}
|
||||
// Replace the existing string with the new value
|
||||
else
|
||||
else if (CanOverwriteHeader(header))
|
||||
entry->value = value;
|
||||
else if (!entry->value.Equals(value))
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -129,7 +132,7 @@ nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsresult
|
||||
nsHttpHeaderArray::ParseHeaderLine(const char *line,
|
||||
nsHttpAtom *hdr,
|
||||
char **val)
|
||||
|
@ -151,13 +154,13 @@ nsHttpHeaderArray::ParseHeaderLine(const char *line,
|
|||
char *p = (char *) strchr(line, ':');
|
||||
if (!p) {
|
||||
LOG(("malformed header [%s]: no colon\n", line));
|
||||
return;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// make sure we have a valid token for the field-name
|
||||
if (!nsHttp::IsValidToken(line, p)) {
|
||||
LOG(("malformed header [%s]: field-name not a token\n", line));
|
||||
return;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*p = 0; // null terminate field-name
|
||||
|
@ -165,7 +168,7 @@ nsHttpHeaderArray::ParseHeaderLine(const char *line,
|
|||
nsHttpAtom atom = nsHttp::ResolveAtom(line);
|
||||
if (!atom) {
|
||||
LOG(("failed to resolve atom [%s]\n", line));
|
||||
return;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// skip over whitespace
|
||||
|
@ -183,7 +186,7 @@ nsHttpHeaderArray::ParseHeaderLine(const char *line,
|
|||
if (val) *val = p;
|
||||
|
||||
// assign response header
|
||||
SetHeader(atom, nsDependentCString(p, p2 - p), PR_TRUE);
|
||||
return SetHeader(atom, nsDependentCString(p, p2 - p), PR_TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -247,3 +250,11 @@ nsHttpHeaderArray::CanAppendToHeader(nsHttpAtom header)
|
|||
header != nsHttp::Location &&
|
||||
header != nsHttp::Max_Forwards;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHttpHeaderArray::CanOverwriteHeader(nsHttpAtom header)
|
||||
{
|
||||
if (mType != HTTP_RESPONSE_HEADERS)
|
||||
return PR_TRUE;
|
||||
return header != nsHttp::Content_Length;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,12 @@
|
|||
class nsHttpHeaderArray
|
||||
{
|
||||
public:
|
||||
nsHttpHeaderArray() {}
|
||||
enum nsHttpHeaderType {
|
||||
HTTP_REQUEST_HEADERS,
|
||||
HTTP_RESPONSE_HEADERS
|
||||
};
|
||||
|
||||
nsHttpHeaderArray(nsHttpHeaderType headerType) : mType(headerType) {}
|
||||
~nsHttpHeaderArray() { Clear(); }
|
||||
|
||||
const char *PeekHeader(nsHttpAtom header);
|
||||
|
@ -73,9 +78,9 @@ public:
|
|||
|
||||
// parse a header line, return the header atom and a pointer to the
|
||||
// header value (the substring of the header line -- do not free).
|
||||
void ParseHeaderLine(const char *line,
|
||||
nsHttpAtom *header=nsnull,
|
||||
char **value=nsnull);
|
||||
nsresult ParseHeaderLine(const char *line,
|
||||
nsHttpAtom *header=nsnull,
|
||||
char **value=nsnull);
|
||||
|
||||
void Flatten(nsACString &, PRBool pruneProxyHeaders=PR_FALSE);
|
||||
|
||||
|
@ -104,8 +109,10 @@ public:
|
|||
private:
|
||||
PRInt32 LookupEntry(nsHttpAtom header, nsEntry **);
|
||||
PRBool CanAppendToHeader(nsHttpAtom header);
|
||||
PRBool CanOverwriteHeader(nsHttpAtom header);
|
||||
|
||||
nsTArray<nsEntry> mHeaders;
|
||||
nsHttpHeaderType mType;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -52,8 +52,10 @@
|
|||
class nsHttpRequestHead
|
||||
{
|
||||
public:
|
||||
nsHttpRequestHead() : mMethod(nsHttp::Get), mVersion(NS_HTTP_VERSION_1_1) {}
|
||||
~nsHttpRequestHead() {}
|
||||
nsHttpRequestHead() : mHeaders(nsHttpHeaderArray::HTTP_REQUEST_HEADERS)
|
||||
, mMethod(nsHttp::Get)
|
||||
, mVersion(NS_HTTP_VERSION_1_1) {}
|
||||
~nsHttpRequestHead() {}
|
||||
|
||||
void SetMethod(nsHttpAtom method) { mMethod = method; }
|
||||
void SetVersion(nsHttpVersion version) { mVersion = version; }
|
||||
|
|
|
@ -197,23 +197,30 @@ nsHttpResponseHead::ParseStatusLine(const char *line)
|
|||
PRUintn(mVersion), PRUintn(mStatus), mStatusText.get()));
|
||||
}
|
||||
|
||||
void
|
||||
nsresult
|
||||
nsHttpResponseHead::ParseHeaderLine(const char *line)
|
||||
{
|
||||
nsHttpAtom hdr = {0};
|
||||
char *val;
|
||||
|
||||
mHeaders.ParseHeaderLine(line, &hdr, &val);
|
||||
nsresult rv;
|
||||
|
||||
rv = mHeaders.ParseHeaderLine(line, &hdr, &val);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// leading and trailing LWS has been removed from |val|
|
||||
|
||||
// handle some special case headers...
|
||||
if (hdr == nsHttp::Content_Length) {
|
||||
PRInt64 len;
|
||||
// permit only a single value here.
|
||||
if (nsHttp::ParseInt64(val, &len))
|
||||
if (nsHttp::ParseInt64(val, &len)) {
|
||||
mContentLength = len;
|
||||
else
|
||||
}
|
||||
else {
|
||||
LOG(("invalid content-length!\n"));
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
}
|
||||
else if (hdr == nsHttp::Content_Type) {
|
||||
LOG(("ParseContentType [type=%s]\n", val));
|
||||
|
@ -225,6 +232,7 @@ nsHttpResponseHead::ParseHeaderLine(const char *line)
|
|||
ParseCacheControl(val);
|
||||
else if (hdr == nsHttp::Pragma)
|
||||
ParsePragma(val);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// From section 13.2.3 of RFC2616, we compute the current age of a cached
|
||||
|
|
|
@ -51,13 +51,14 @@
|
|||
class nsHttpResponseHead
|
||||
{
|
||||
public:
|
||||
nsHttpResponseHead() : mVersion(NS_HTTP_VERSION_1_1)
|
||||
nsHttpResponseHead() : mHeaders(nsHttpHeaderArray::HTTP_RESPONSE_HEADERS)
|
||||
, mVersion(NS_HTTP_VERSION_1_1)
|
||||
, mStatus(200)
|
||||
, mContentLength(LL_MAXUINT)
|
||||
, mCacheControlNoStore(PR_FALSE)
|
||||
, mCacheControlNoCache(PR_FALSE)
|
||||
, mPragmaNoCache(PR_FALSE) {}
|
||||
~nsHttpResponseHead()
|
||||
~nsHttpResponseHead()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
@ -104,7 +105,7 @@ public:
|
|||
void ParseStatusLine(const char *line);
|
||||
|
||||
// parse a header line. line must be null terminated. parsing is destructive.
|
||||
void ParseHeaderLine(const char *line);
|
||||
nsresult ParseHeaderLine(const char *line);
|
||||
|
||||
// cache validation support methods
|
||||
nsresult ComputeFreshnessLifetime(PRUint32 *);
|
||||
|
|
|
@ -784,12 +784,12 @@ nsHttpTransaction::LocateHttpStart(char *buf, PRUint32 len,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsresult
|
||||
nsHttpTransaction::ParseLine(char *line)
|
||||
{
|
||||
LOG(("nsHttpTransaction::ParseLine [%s]\n", line));
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (!mHaveStatusLine) {
|
||||
mResponseHead->ParseStatusLine(line);
|
||||
mHaveStatusLine = PR_TRUE;
|
||||
|
@ -797,8 +797,10 @@ nsHttpTransaction::ParseLine(char *line)
|
|||
if (mResponseHead->Version() == NS_HTTP_VERSION_0_9)
|
||||
mHaveAllHeaders = PR_TRUE;
|
||||
}
|
||||
else
|
||||
mResponseHead->ParseHeaderLine(line);
|
||||
else {
|
||||
rv = mResponseHead->ParseHeaderLine(line);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -813,8 +815,11 @@ nsHttpTransaction::ParseLineSegment(char *segment, PRUint32 len)
|
|||
// of mLineBuf.
|
||||
mLineBuf.Truncate(mLineBuf.Length() - 1);
|
||||
if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
|
||||
ParseLine(mLineBuf.BeginWriting());
|
||||
nsresult rv = ParseLine(mLineBuf.BeginWriting());
|
||||
mLineBuf.Truncate();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ private:
|
|||
nsresult Restart();
|
||||
char *LocateHttpStart(char *buf, PRUint32 len,
|
||||
PRBool aAllowPartialMatch);
|
||||
void ParseLine(char *line);
|
||||
nsresult ParseLine(char *line);
|
||||
nsresult ParseLineSegment(char *seg, PRUint32 len);
|
||||
nsresult ParseHead(char *, PRUint32 count, PRUint32 *countRead);
|
||||
nsresult HandleContentStart();
|
||||
|
|
Загрузка…
Ссылка в новой задаче