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 \ #define NS_ERROR_INVALID_CONTENT_ENCODING \
NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 27) 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: * Connectivity error codes:
*/ */

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

@ -137,7 +137,8 @@ nsHttpChunkedDecoder::ParseChunkRemaining(char *buf,
LOG(("got trailer: %s\n", buf)); LOG(("got trailer: %s\n", buf));
// allocate a header array for the trailers on demand // allocate a header array for the trailers on demand
if (!mTrailers) { if (!mTrailers) {
mTrailers = new nsHttpHeaderArray(); mTrailers = new nsHttpHeaderArray
(nsHttpHeaderArray::HTTP_RESPONSE_HEADERS);
if (!mTrailers) if (!mTrailers)
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
} }

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

@ -85,8 +85,11 @@ nsHttpHeaderArray::SetHeader(nsHttpAtom header,
entry->value.Append(value); entry->value.Append(value);
} }
// Replace the existing string with the new value // Replace the existing string with the new value
else else if (CanOverwriteHeader(header))
entry->value = value; entry->value = value;
else if (!entry->value.Equals(value))
return NS_ERROR_CORRUPTED_CONTENT;
return NS_OK; return NS_OK;
} }
@ -129,7 +132,7 @@ nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor)
return NS_OK; return NS_OK;
} }
void nsresult
nsHttpHeaderArray::ParseHeaderLine(const char *line, nsHttpHeaderArray::ParseHeaderLine(const char *line,
nsHttpAtom *hdr, nsHttpAtom *hdr,
char **val) char **val)
@ -151,13 +154,13 @@ nsHttpHeaderArray::ParseHeaderLine(const char *line,
char *p = (char *) strchr(line, ':'); char *p = (char *) strchr(line, ':');
if (!p) { if (!p) {
LOG(("malformed header [%s]: no colon\n", line)); LOG(("malformed header [%s]: no colon\n", line));
return; return NS_OK;
} }
// make sure we have a valid token for the field-name // make sure we have a valid token for the field-name
if (!nsHttp::IsValidToken(line, p)) { if (!nsHttp::IsValidToken(line, p)) {
LOG(("malformed header [%s]: field-name not a token\n", line)); LOG(("malformed header [%s]: field-name not a token\n", line));
return; return NS_OK;
} }
*p = 0; // null terminate field-name *p = 0; // null terminate field-name
@ -165,7 +168,7 @@ nsHttpHeaderArray::ParseHeaderLine(const char *line,
nsHttpAtom atom = nsHttp::ResolveAtom(line); nsHttpAtom atom = nsHttp::ResolveAtom(line);
if (!atom) { if (!atom) {
LOG(("failed to resolve atom [%s]\n", line)); LOG(("failed to resolve atom [%s]\n", line));
return; return NS_OK;
} }
// skip over whitespace // skip over whitespace
@ -183,7 +186,7 @@ nsHttpHeaderArray::ParseHeaderLine(const char *line,
if (val) *val = p; if (val) *val = p;
// assign response header // assign response header
SetHeader(atom, nsDependentCString(p, p2 - p), PR_TRUE); return SetHeader(atom, nsDependentCString(p, p2 - p), PR_TRUE);
} }
void void
@ -247,3 +250,11 @@ nsHttpHeaderArray::CanAppendToHeader(nsHttpAtom header)
header != nsHttp::Location && header != nsHttp::Location &&
header != nsHttp::Max_Forwards; 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 class nsHttpHeaderArray
{ {
public: public:
nsHttpHeaderArray() {} enum nsHttpHeaderType {
HTTP_REQUEST_HEADERS,
HTTP_RESPONSE_HEADERS
};
nsHttpHeaderArray(nsHttpHeaderType headerType) : mType(headerType) {}
~nsHttpHeaderArray() { Clear(); } ~nsHttpHeaderArray() { Clear(); }
const char *PeekHeader(nsHttpAtom header); const char *PeekHeader(nsHttpAtom header);
@ -73,9 +78,9 @@ public:
// parse a header line, return the header atom and a pointer to the // parse a header line, return the header atom and a pointer to the
// header value (the substring of the header line -- do not free). // header value (the substring of the header line -- do not free).
void ParseHeaderLine(const char *line, nsresult ParseHeaderLine(const char *line,
nsHttpAtom *header=nsnull, nsHttpAtom *header=nsnull,
char **value=nsnull); char **value=nsnull);
void Flatten(nsACString &, PRBool pruneProxyHeaders=PR_FALSE); void Flatten(nsACString &, PRBool pruneProxyHeaders=PR_FALSE);
@ -104,8 +109,10 @@ public:
private: private:
PRInt32 LookupEntry(nsHttpAtom header, nsEntry **); PRInt32 LookupEntry(nsHttpAtom header, nsEntry **);
PRBool CanAppendToHeader(nsHttpAtom header); PRBool CanAppendToHeader(nsHttpAtom header);
PRBool CanOverwriteHeader(nsHttpAtom header);
nsTArray<nsEntry> mHeaders; nsTArray<nsEntry> mHeaders;
nsHttpHeaderType mType;
}; };
#endif #endif

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

@ -52,8 +52,10 @@
class nsHttpRequestHead class nsHttpRequestHead
{ {
public: public:
nsHttpRequestHead() : mMethod(nsHttp::Get), mVersion(NS_HTTP_VERSION_1_1) {} nsHttpRequestHead() : mHeaders(nsHttpHeaderArray::HTTP_REQUEST_HEADERS)
~nsHttpRequestHead() {} , mMethod(nsHttp::Get)
, mVersion(NS_HTTP_VERSION_1_1) {}
~nsHttpRequestHead() {}
void SetMethod(nsHttpAtom method) { mMethod = method; } void SetMethod(nsHttpAtom method) { mMethod = method; }
void SetVersion(nsHttpVersion version) { mVersion = version; } void SetVersion(nsHttpVersion version) { mVersion = version; }

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

@ -197,23 +197,30 @@ nsHttpResponseHead::ParseStatusLine(const char *line)
PRUintn(mVersion), PRUintn(mStatus), mStatusText.get())); PRUintn(mVersion), PRUintn(mStatus), mStatusText.get()));
} }
void nsresult
nsHttpResponseHead::ParseHeaderLine(const char *line) nsHttpResponseHead::ParseHeaderLine(const char *line)
{ {
nsHttpAtom hdr = {0}; nsHttpAtom hdr = {0};
char *val; char *val;
nsresult rv;
mHeaders.ParseHeaderLine(line, &hdr, &val);
rv = mHeaders.ParseHeaderLine(line, &hdr, &val);
if (NS_FAILED(rv))
return rv;
// leading and trailing LWS has been removed from |val| // leading and trailing LWS has been removed from |val|
// handle some special case headers... // handle some special case headers...
if (hdr == nsHttp::Content_Length) { if (hdr == nsHttp::Content_Length) {
PRInt64 len; PRInt64 len;
// permit only a single value here. // permit only a single value here.
if (nsHttp::ParseInt64(val, &len)) if (nsHttp::ParseInt64(val, &len)) {
mContentLength = len; mContentLength = len;
else }
else {
LOG(("invalid content-length!\n")); LOG(("invalid content-length!\n"));
return NS_ERROR_CORRUPTED_CONTENT;
}
} }
else if (hdr == nsHttp::Content_Type) { else if (hdr == nsHttp::Content_Type) {
LOG(("ParseContentType [type=%s]\n", val)); LOG(("ParseContentType [type=%s]\n", val));
@ -225,6 +232,7 @@ nsHttpResponseHead::ParseHeaderLine(const char *line)
ParseCacheControl(val); ParseCacheControl(val);
else if (hdr == nsHttp::Pragma) else if (hdr == nsHttp::Pragma)
ParsePragma(val); ParsePragma(val);
return NS_OK;
} }
// From section 13.2.3 of RFC2616, we compute the current age of a cached // From section 13.2.3 of RFC2616, we compute the current age of a cached

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

@ -51,13 +51,14 @@
class nsHttpResponseHead class nsHttpResponseHead
{ {
public: public:
nsHttpResponseHead() : mVersion(NS_HTTP_VERSION_1_1) nsHttpResponseHead() : mHeaders(nsHttpHeaderArray::HTTP_RESPONSE_HEADERS)
, mVersion(NS_HTTP_VERSION_1_1)
, mStatus(200) , mStatus(200)
, mContentLength(LL_MAXUINT) , mContentLength(LL_MAXUINT)
, mCacheControlNoStore(PR_FALSE) , mCacheControlNoStore(PR_FALSE)
, mCacheControlNoCache(PR_FALSE) , mCacheControlNoCache(PR_FALSE)
, mPragmaNoCache(PR_FALSE) {} , mPragmaNoCache(PR_FALSE) {}
~nsHttpResponseHead() ~nsHttpResponseHead()
{ {
Reset(); Reset();
} }
@ -104,7 +105,7 @@ public:
void ParseStatusLine(const char *line); void ParseStatusLine(const char *line);
// parse a header line. line must be null terminated. parsing is destructive. // 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 // cache validation support methods
nsresult ComputeFreshnessLifetime(PRUint32 *); nsresult ComputeFreshnessLifetime(PRUint32 *);

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

@ -784,12 +784,12 @@ nsHttpTransaction::LocateHttpStart(char *buf, PRUint32 len,
return 0; return 0;
} }
nsresult
void
nsHttpTransaction::ParseLine(char *line) nsHttpTransaction::ParseLine(char *line)
{ {
LOG(("nsHttpTransaction::ParseLine [%s]\n", line)); LOG(("nsHttpTransaction::ParseLine [%s]\n", line));
nsresult rv = NS_OK;
if (!mHaveStatusLine) { if (!mHaveStatusLine) {
mResponseHead->ParseStatusLine(line); mResponseHead->ParseStatusLine(line);
mHaveStatusLine = PR_TRUE; mHaveStatusLine = PR_TRUE;
@ -797,8 +797,10 @@ nsHttpTransaction::ParseLine(char *line)
if (mResponseHead->Version() == NS_HTTP_VERSION_0_9) if (mResponseHead->Version() == NS_HTTP_VERSION_0_9)
mHaveAllHeaders = PR_TRUE; mHaveAllHeaders = PR_TRUE;
} }
else else {
mResponseHead->ParseHeaderLine(line); rv = mResponseHead->ParseHeaderLine(line);
}
return rv;
} }
nsresult nsresult
@ -813,8 +815,11 @@ nsHttpTransaction::ParseLineSegment(char *segment, PRUint32 len)
// of mLineBuf. // of mLineBuf.
mLineBuf.Truncate(mLineBuf.Length() - 1); mLineBuf.Truncate(mLineBuf.Length() - 1);
if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) { if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
ParseLine(mLineBuf.BeginWriting()); nsresult rv = ParseLine(mLineBuf.BeginWriting());
mLineBuf.Truncate(); mLineBuf.Truncate();
if (NS_FAILED(rv)) {
return rv;
}
} }
} }

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

@ -141,7 +141,7 @@ private:
nsresult Restart(); nsresult Restart();
char *LocateHttpStart(char *buf, PRUint32 len, char *LocateHttpStart(char *buf, PRUint32 len,
PRBool aAllowPartialMatch); PRBool aAllowPartialMatch);
void ParseLine(char *line); nsresult ParseLine(char *line);
nsresult ParseLineSegment(char *seg, PRUint32 len); nsresult ParseLineSegment(char *seg, PRUint32 len);
nsresult ParseHead(char *, PRUint32 count, PRUint32 *countRead); nsresult ParseHead(char *, PRUint32 count, PRUint32 *countRead);
nsresult HandleContentStart(); nsresult HandleContentStart();