Skip to content

Commit

Permalink
Merge pull request #35 from cesarhernandezgt/master
Browse files Browse the repository at this point in the history
backport fix for CVE-2023-46589
  • Loading branch information
cesarhernandezgt authored Dec 19, 2023
2 parents 0347708 + aa34dc1 commit 09ef2cc
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 26 deletions.
68 changes: 68 additions & 0 deletions java/org/apache/catalina/connector/BadRequestException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.connector;

import java.io.IOException;

/**
* Extend IOException to identify it as being caused by a bad request from a remote client.
*/
public class BadRequestException extends IOException {

private static final long serialVersionUID = 1L;


// ------------------------------------------------------------ Constructors

/**
* Construct a new BadRequestException with no other information.
*/
public BadRequestException() {
super();
}


/**
* Construct a new BadRequestException for the specified message.
*
* @param message Message describing this exception
*/
public BadRequestException(String message) {
super(message);
}


/**
* Construct a new BadRequestException for the specified throwable.
*
* @param throwable Throwable that caused this exception
*/
public BadRequestException(Throwable throwable) {
super(throwable);
}


/**
* Construct a new BadRequestException for the specified message and throwable.
*
* @param message Message describing this exception
* @param throwable Throwable that caused this exception
*/
public BadRequestException(String message, Throwable throwable) {
super(message, throwable);
}
}
36 changes: 32 additions & 4 deletions java/org/apache/catalina/connector/InputBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,38 @@ public int realReadBytes(byte cbuf[], int off, int len)
if(state == INITIAL_STATE)
state = BYTE_STATE;

int result = coyoteRequest.doRead(bb);

return result;

try {
return coyoteRequest.doRead(bb);
} catch (BadRequestException bre) {
// Set flag used by asynchronous processing to detect errors on non-container threads
coyoteRequest.setErrorException(bre);

// In synchronous processing, this exception may be swallowed by the application so set error flags here.
// CH: We can't use ERROR_EXCEPTION because Tomcat 6 implements Servlet 2.5 and ERROR_EXCEPTION was
// introduced in Servlet 3.0
// coyoteRequest.setAttribute(RequestDispatcher.ERROR_EXCEPTION, bre);

coyoteRequest.getResponse().setStatus(400);
coyoteRequest.getResponse().setErrorTrue();

// Make the exception visible to the application
throw bre;
} catch (IOException ioe) {
// Set flag used by asynchronous processing to detect errors on non-container threads
coyoteRequest.setErrorException(ioe);

// In synchronous processing, this exception may be swallowed by the application so set error flags here.
// CH: We can't use ERROR_EXCEPTION because Tomcat 6 implements Servlet 2.5 and ERROR_EXCEPTION was
// introduced in Servlet 3.0
// coyoteRequest.setAttribute(RequestDispatcher.ERROR_EXCEPTION, bre);

coyoteRequest.getResponse().setStatus(400);
coyoteRequest.getResponse().setErrorTrue();

// Any other IOException on a read is almost always due to the remote client aborting the request.
// Make the exception visible to the application
throw new ClientAbortException(ioe);
}
}


Expand Down
21 changes: 13 additions & 8 deletions java/org/apache/catalina/connector/Response.java
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,6 @@ public void setContext(Context context) {
*/
private boolean isCharacterEncodingSet = false;

/**
* The error flag.
*/
protected boolean error = false;


/**
Expand Down Expand Up @@ -264,7 +260,6 @@ public void recycle() {
usingWriter = false;
appCommitted = false;
included = false;
error = false;
isCharacterEncodingSet = false;

cookies.clear();
Expand Down Expand Up @@ -453,15 +448,15 @@ public boolean isClosed() {
* Set the error flag.
*/
public void setError() {
error = true;
getCoyoteResponse().setErrorTrue();
}


/**
* Error flag accessor.
*/
public boolean isError() {
return error;
return getCoyoteResponse().getErrorFlag();
}


Expand Down Expand Up @@ -1326,6 +1321,16 @@ public void sendError(int status, String message)
*/
public void sendRedirect(String location)
throws IOException {
sendRedirect(location, SC_FOUND);

}

/**
* Internal method that allows a redirect to be sent with a status other
* than {@link HttpServletResponse#SC_FOUND} (302). No attempt is made to
* validate the status code.
*/
public void sendRedirect(String location, int status) throws IOException {

if (isCommitted())
throw new IllegalStateException
Expand All @@ -1341,7 +1346,7 @@ public void sendRedirect(String location)
// Generate a temporary redirect to the specified location
try {
String absolute = toAbsolute(location);
setStatus(SC_FOUND);
setStatus(status);
setHeader("Location", absolute);
} catch (IllegalArgumentException e) {
setStatus(SC_NOT_FOUND);
Expand Down
18 changes: 13 additions & 5 deletions java/org/apache/catalina/core/StandardHostValve.java
Original file line number Diff line number Diff line change
Expand Up @@ -275,11 +275,19 @@ protected void throwable(Request request, Response response,
}
}
} else {
// A custom error-page has not been defined for the exception
// that was thrown during request processing. Check if an
// error-page for error code 500 was specified and if so,
// send that page back as the response.
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

/*
* A custom error-page has not been defined for the exception that was thrown during request processing.
* Set the status to 500 if an error status has not already been set and check for custom error-page for
* the status.
*/
if (response.getStatus() < HttpServletResponse.SC_BAD_REQUEST) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} else if (throwable instanceof org.apache.catalina.connector.BadRequestException) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}


// The response is an error
response.setError();

Expand Down
7 changes: 5 additions & 2 deletions java/org/apache/catalina/valves/ErrorReportValve.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,11 @@ public void invoke(Request request, Response response)
;
}

response.sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
if (throwable instanceof org.apache.catalina.connector.BadRequestException) {
response.sendError (HttpServletResponse.SC_BAD_REQUEST);
}else{
response.sendError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}

}

Expand Down
35 changes: 35 additions & 0 deletions java/org/apache/coyote/Request.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ public Request() {
private int available = 0;

private RequestInfo reqProcessorMX=new RequestInfo(this);


/**
* Holds request body reading error exception.
*/
private Exception errorException = null;

// ------------------------------------------------------------- Properties


Expand Down Expand Up @@ -431,6 +438,33 @@ public int doRead(ByteChunk chunk)
return n;
}

// -------------------- Error tracking --------------------

/**
* Set the error Exception that occurred during the writing of the response processing.
*
* @param ex The exception that occurred
*/
public void setErrorException(Exception ex) {
errorException = ex;
}


/**
* Get the Exception that occurred during the writing of the response.
*
* @return The exception that occurred
*/
public Exception getErrorException() {
return errorException;
}


public boolean isExceptionPresent() {
return errorException != null;
}



// -------------------- debug --------------------

Expand Down Expand Up @@ -510,6 +544,7 @@ public void recycle() {
remoteUser.recycle();
authType.recycle();
attributes.clear();
errorException = null;
}

// -------------------- Info --------------------
Expand Down
14 changes: 14 additions & 0 deletions java/org/apache/coyote/Response.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ public Response() {

protected Request req;

/**
* The error flag.
*/
protected boolean error = false;

// ------------------------------------------------------------- Properties

public Request getRequest() {
Expand Down Expand Up @@ -255,6 +260,14 @@ public boolean isExceptionPresent() {
return ( errorException != null );
}

public void setErrorTrue() {
error = true;
}

public boolean getErrorFlag(){
return error;
}


/**
* Set request URI that caused an error during
Expand Down Expand Up @@ -551,6 +564,7 @@ public void recycle() {
errorURI = null;
headers.clear();

error = false;
// update counters
bytesWritten=0;
}
Expand Down
14 changes: 13 additions & 1 deletion java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.IOException;
import java.util.Locale;

import org.apache.catalina.connector.BadRequestException;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.HexUtils;
import org.apache.coyote.InputBuffer;
Expand Down Expand Up @@ -477,7 +478,9 @@ private boolean parseHeader() throws IOException {
colon = true;
} else if (!HttpParser.isToken(chr)) {
// Non-token characters are illegal in header names
throw new IOException(sm.getString("chunkedInputFilter.invalidTrailerHeaderName"));
throwBadRequestException(sm.getString("chunkedInputFilter.invalidTrailerHeaderName"));
} else if (trailingHeaders.getEnd() >= trailingHeaders.getLimit()) {
throwBadRequestException(sm.getString("chunkedInputFilter.maxTrailer"));
} else {
trailingHeaders.append(chr);
}
Expand Down Expand Up @@ -543,6 +546,8 @@ private boolean parseHeader() throws IOException {
eol = true;
} else if (HttpParser.isControl(chr) && chr != Constants.HT) {
throw new IOException(sm.getString("chunkedInputFilter.invalidTrailerHeaderValue"));
} else if (trailingHeaders.getEnd() >= trailingHeaders.getLimit()) {
throw new IOException(sm.getString("chunkedInputFilter.maxTrailer"));
} else if (chr == Constants.SP || chr == Constants.HT) {
trailingHeaders.append(chr);
} else {
Expand All @@ -568,6 +573,8 @@ private boolean parseHeader() throws IOException {
chr = buf[pos];
if ((chr != Constants.SP) && (chr != Constants.HT)) {
validLine = false;
} else if (trailingHeaders.getEnd() >= trailingHeaders.getLimit()) {
throwBadRequestException(sm.getString("chunkedInputFilter.maxTrailer"));
} else {
eol = false;
// Copying one extra space in the buffer (since there must
Expand Down Expand Up @@ -597,6 +604,11 @@ private void throwIOException(String msg) throws IOException {
throw new IOException(msg);
}

private void throwBadRequestException(String msg) throws IOException {
error = true;
throw new BadRequestException(msg);
}


private void throwEOFException(String msg) throws IOException {
error = true;
Expand Down
Loading

0 comments on commit 09ef2cc

Please sign in to comment.