Skip to content

Commit

Permalink
Merge pull request #16 from cesarhernandezgt/tomcat-7.0.104-TT-patch
Browse files Browse the repository at this point in the history
Added test for invalid Content-Length header with  rejectIllegalHeader config
  • Loading branch information
cesarhernandezgt authored Nov 15, 2022
2 parents 6b69621 + 74e50de commit 05834ed
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
run: unzip -qq tomcat6-build-libs-.zip -d /home/runner/

- name: replace java.7.home
run: sed -i.bak 's:.*java.7.home.*:java.7.home=/opt/hostedtoolcache/jdk/7.0.342/x64:' build.properties.default
run: sed -i.bak 's:.*java.7.home.*:java.7.home=/opt/hostedtoolcache/jdk/7.0.352/x64:' build.properties.default

- name: ant_build
run: ant
Expand Down
2 changes: 1 addition & 1 deletion build.properties.default
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ version.major=7
version.minor=0
version.build=104
version.patch=0
version.suffix=-SP.5
version.suffix=-SP.6

# ----- Source control flags -----
git.branch=7.0.x
Expand Down
41 changes: 29 additions & 12 deletions java/org/apache/coyote/http11/InternalInputBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ private boolean parseHeader() throws IOException {
// Non-token characters are illegal in header names
// Parsing continues so the error can be reported in context
// skipLine() will handle the error
skipLine(lineStart, start);
skipLine(lineStart, start, false);
return true;
}

Expand Down Expand Up @@ -432,16 +432,12 @@ private boolean parseHeader() throws IOException {
} else if (prevChr == Constants.CR && chr == Constants.LF) {
eol = true;
} else if (prevChr == Constants.CR) {
// Invalid value
// Delete the header (it will be the most recent one)
headers.removeHeader(headers.size() - 1);
skipLine(lineStart, start);
// Invalid value - also need to delete header
skipLine(lineStart, start, true);
return true;
} else if (chr != Constants.HT && HttpParser.isControl(chr)) {
// Invalid value
// Delete the header (it will be the most recent one)
headers.removeHeader(headers.size() - 1);
skipLine(lineStart, start);
// Invalid value - also need to delete header
skipLine(lineStart, start, true);
return true;
} else if (chr == Constants.SP) {
buf[realPos] = chr;
Expand Down Expand Up @@ -506,7 +502,28 @@ protected void init(SocketWrapper<Socket> socketWrapper,



private void skipLine(int lineStart, int start) throws IOException {
private void skipLine(int lineStart, int start, boolean deleteHeader) throws IOException {

boolean rejectThisHeader = rejectIllegalHeaderName;
// Check if rejectIllegalHeader is disabled and needs to be overridden
// for this header. The header name is required to determine if this
// override is required. The header name is only available once the
// header has been created. If the header has been created then
// deleteHeader will be true.
if (!rejectThisHeader && deleteHeader) {
if (headers.getName(headers.size() - 1).equalsIgnoreCase("content-length")) {
// Malformed content-length headers must always be rejected
// RFC 9112, section 6.3, bullet 5.
rejectThisHeader = true;
} else {
// Only need to delete the header if the request isn't going to
// be rejected (it will be the most recent one)
headers.removeHeader(headers.size() - 1);
}
}



boolean eol = false;
int lastRealByte = start;
if (pos - 1 > start) {
Expand Down Expand Up @@ -534,10 +551,10 @@ private void skipLine(int lineStart, int start) throws IOException {
pos++;
}

if (rejectIllegalHeaderName || log.isDebugEnabled()) {
if (rejectThisHeader || log.isDebugEnabled()) {
String message = sm.getString("iib.invalidheader", HeaderUtil.toPrintableString(
buf, lineStart, lastRealByte - lineStart + 1));
if (rejectIllegalHeaderName) {
if (rejectThisHeader) {
throw new IllegalArgumentException(message);
}
log.debug(message);
Expand Down
42 changes: 29 additions & 13 deletions java/org/apache/coyote/http11/InternalNioInputBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ private HeaderParseStatus parseHeader()
// Parsing continues so the error can be reported in context
headerData.lastSignificantChar = pos;
// skipLine() will handle the error
return skipLine();
return skipLine(false);
}

// chr is next byte of header name. Convert to lowercase.
Expand All @@ -616,7 +616,7 @@ private HeaderParseStatus parseHeader()

// Skip the line and ignore the header
if (headerParsePos == HeaderParsePosition.HEADER_SKIPLINE) {
return skipLine();
return skipLine(false);
}

//
Expand Down Expand Up @@ -668,15 +668,11 @@ private HeaderParseStatus parseHeader()
} else if (prevChr == Constants.CR && chr == Constants.LF) {
eol = true;
} else if (prevChr == Constants.CR) {
// Invalid value
// Delete the header (it will be the most recent one)
headers.removeHeader(headers.size() - 1);
return skipLine();
// Invalid value - also need to delete header
return skipLine(true);
} else if (chr != Constants.HT && HttpParser.isControl(chr)) {
// Invalid value
// Delete the header (it will be the most recent one)
headers.removeHeader(headers.size() - 1);
return skipLine();
// Invalid value - also need to delete header
return skipLine(true);
} else if (chr == Constants.SP || chr == Constants.HT) {
buf[headerData.realPos] = chr;
headerData.realPos++;
Expand Down Expand Up @@ -730,7 +726,27 @@ public int getParsingRequestLinePhase() {
return parsingRequestLinePhase;
}

private HeaderParseStatus skipLine() throws IOException {
private HeaderParseStatus skipLine(boolean deleteHeader) throws IOException {
boolean rejectThisHeader = rejectIllegalHeaderName;
// Check if rejectIllegalHeader is disabled and needs to be overridden
// for this header. The header name is required to determine if this
// override is required. The header name is only available once the
// header has been created. If the header has been created then
// deleteHeader will be true.
if (!rejectThisHeader && deleteHeader) {
if (headers.getName(headers.size() - 1).equalsIgnoreCase("content-length")) {
// Malformed content-length headers must always be rejected
// RFC 9112, section 6.3, bullet 5.
rejectThisHeader = true;
} else {
// Only need to delete the header if the request isn't going to
// be rejected (it will be the most recent one)
headers.removeHeader(headers.size() - 1);
}
}

// Parse the rest of the invalid header so we can construct a useful
// exception and/or debug message.
headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
boolean eol = false;

Expand All @@ -757,10 +773,10 @@ private HeaderParseStatus skipLine() throws IOException {

pos++;
}
if (rejectIllegalHeaderName || log.isDebugEnabled()) {
if (rejectThisHeader || log.isDebugEnabled()) {
String message = sm.getString("iib.invalidheader", HeaderUtil.toPrintableString(
buf, headerData.lineStart, headerData.lastSignificantChar - headerData.lineStart + 1));
if (rejectIllegalHeaderName) {
if (rejectThisHeader) {
throw new IllegalArgumentException(message);
}
log.debug(message);
Expand Down
33 changes: 33 additions & 0 deletions test/org/apache/coyote/http11/TestInternalInputBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ public void testBug51557CtlInValue() throws Exception {
// TAB is allowed
continue;
}
if (i == '\n') {
// LF is the optional line terminator
continue;
}
doTestBug51557InvalidCharInValue((char) i);
tearDown();
setUp();
Expand Down Expand Up @@ -748,6 +752,35 @@ public void testInvalidHeader01() {
Assert.assertTrue(client.isResponseBodyOK());
}

@Test
public void testInvalidContentLength01() {
doTestInvalidContentLength(false);
}


@Test
public void testInvalidContentLength02() {
doTestInvalidContentLength(true);
}


private void doTestInvalidContentLength(boolean rejectIllegalHeader) {
getTomcatInstance().getConnector().setProperty("rejectIllegalHeader", Boolean.toString(rejectIllegalHeader));

String[] request = new String[1];
request[0] =
"POST /test HTTP/1.1" + CRLF +
"Host: localhost:8080" + CRLF +
"Content-Length: 12\u000734" + CRLF +
"Connection: close" + CRLF +
CRLF;

InvalidClient client = new InvalidClient(request);

client.doRequest();
Assert.assertTrue(client.getResponseLine(), client.isResponse400());
Assert.assertTrue(client.isResponseBodyOK());
}

/**
* Invalid request test client.
Expand Down

0 comments on commit 05834ed

Please sign in to comment.