bug 597706 - response header smuggling r=honzab

reject responses with multiple non identical or invalid
content-length headers
This commit is contained in:
Patrick McManus 2011-05-31 19:51:51 -04:00
Родитель 2e6061489a
Коммит 9647e6891f
9 изменённых файлов: 71 добавлений и 28 удалений

Просмотреть файл

@ -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();