зеркало из 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 \
|
#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();
|
||||||
|
|
Загрузка…
Ссылка в новой задаче