Skip to content

Commit

Permalink
EST: handle dubious Accept headers
Browse files Browse the repository at this point in the history
At least one EST client is known to send requests with HTTP header
`Accept: text/plain`; see
thales-e-security/estclient#5.

This behaviour is dubious.  It is problematic when communicating
with servers/frameworks that have rigid content negotiation
behaviour (such as JAX-RS).  Nevertheless, the EST protocol uses a
narrow range of media types.  The method and path are sufficient to
determine the request and response media types, regardless of
Content-Type and Accept header values.

To tolerate bogus Accept header values, define and apply a
ContainerRequestFilter that detects when the Accept header does not
match any of the response types used in the EST protocol.  If it
detects this condition it removes the Accept header from the
request.

NOTE: the JAX-RS spec is ambiguous as to whether our use of the API
is legal.  Per the spec,
`ContainerRequestContext.getAcceptableMediaTypes()` returns an
IMMUTABLE `List<MediaType>`.  However,
`ContainerRequestContext.getHeaders()` returns a MUTABLE map of
headers.  We are able to delete the Accept header via that map.  It
seems to be a RestEasy implementation detail that
`getAcceptableMediaTypes()` always reads the Accept header afresh
from the mutable map.

Part of: dogtagpki#3297
  • Loading branch information
frasertweedale committed Oct 11, 2022
1 parent 00a816f commit b801612
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 0 deletions.
2 changes: 2 additions & 0 deletions base/est/src/main/java/org/dogtagpki/est/ESTApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public ESTApplication() {

// exception mapper
classes.add(PKIExceptionMapper.class);

singletons.add(new HandleBadAcceptHeaderRequestFilter());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// Copyright Red Hat, Inc.
//
// SPDX-License-Identifier: GPL-2.0-or-later
//
package org.dogtagpki.est;

import java.util.Arrays;
import java.util.List;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;

import com.netscape.cms.servlet.base.PKIService;

/** Filter bad Accept header values.
*
* Some EST clients are known to send requests with Accept:
* text/plain. The JAX-RS API is quite rigid in how it processes
* the Accept header, although the HTTP semantics allow a server to
* ignore the Accept header.
*
* So, we should make some effort to handle these requests despite
* their dubious Accept header values. This request filter fires
* BEFORE resource method matching and handles request thus:
*
* - We do not (yet) know what resource is being requested. We do
* not want to (re)implement the path matching ourselves - too
* complicated.
*
* - So, we have a list of ALL valid (success) response types of the
* EST protocol. If the Accept header matches any of those, we
* leave it alone.
*
* - If the Accept header does not match any valid content-type in
* the EST protocol, then we delete the header and request
* processing continues as if the header had not been included in
* the request.
*
* - The result is that a request has an Accept header that demands
* a content-type that is used within the EST protocol, but which
* is not valid for the method and path combination, will still
* fail 406. But there is only so much complexity we are willing
* to take on to handle dubious client behaviour.
*/
@Provider
@PreMatching
public class HandleBadAcceptHeaderRequestFilter
implements ContainerRequestFilter {

private static org.slf4j.Logger logger =
org.slf4j.LoggerFactory.getLogger(HandleBadAcceptHeaderRequestFilter.class);

private static List<MediaType> RESPONSE_TYPES = Arrays.asList(
// /cacerts, /simpleenroll, /simplereenroll
MediaType.valueOf("application/pkcs7-mime"),

// /serverkeygen
MediaType.valueOf("multipart/mixed"),

// /csrattrs
MediaType.valueOf("application/csrattrs")
);

@Override
public void filter(ContainerRequestContext requestContext) {
logger.debug("HandleBadAcceptHeaderRequestFilter: inspecting request");
List<MediaType> acceptTypes = requestContext.getAcceptableMediaTypes();
MediaType match = PKIService.resolveFormat(acceptTypes, RESPONSE_TYPES);

// if no match, delete the Accept header
if (match == null) {
logger.info("HandleBadAcceptHeaderRequestFilter: no matching Accept header; removing it and proceeding");
requestContext.getHeaders().remove(HttpHeaders.ACCEPT);
}
}

}

0 comments on commit b801612

Please sign in to comment.