mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Chris Ridd
02.24.2015 517c27e644b19c613a052f74f2cf6afa08777270
FR-655 OPENDJ-1916: securely configure XMLReader
1 files modified
86 ■■■■ changed files
opendj-server-legacy/src/dsml/org/opends/dsml/protocol/DSMLServlet.java 86 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/dsml/org/opends/dsml/protocol/DSMLServlet.java
@@ -41,6 +41,7 @@
import java.io.OutputStream;
import java.net.URL;
import java.text.ParseException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.ArrayList;
import java.util.Enumeration;
@@ -49,12 +50,15 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
@@ -62,6 +66,8 @@
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.soap.*;
import javax.xml.soap.SOAPConstants;
import javax.xml.validation.Schema;
@@ -91,9 +97,10 @@
import org.w3c.dom.Document;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLReader;
@@ -145,6 +152,8 @@
   * using SOAP or JAXB.
   */
  private DSMLContentHandler contentHandler;
  /** Prevent multiple logging when trying to set unavailable/unsupported parser features */
  private static AtomicBoolean logFeatureWarnings = new AtomicBoolean(false);
  private String hostName;
  private Integer port;
@@ -234,6 +243,8 @@
      messageFactorySOAP_1_2 = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      dbf.setNamespaceAware(true);
      dbf.setXIncludeAware(false);
      dbf.setExpandEntityReferences(false);
      db = dbf.newDocumentBuilder();
      this.contentHandler = new DSMLContentHandler();
@@ -585,12 +596,45 @@
  }
  /**
   * Safely set a feature on an XMLReader instance.
   *
   * @param xmlReader The reader to configure.
   * @param feature The feature string to set.
   * @param flag The value to set the feature to.
   */
  private void safeSetFeature(XMLReader xmlReader, String feature, Boolean flag)
  {
    try
    {
      xmlReader.setFeature(feature, flag);
    }
    catch (SAXNotSupportedException e)
    {
      if (logFeatureWarnings.compareAndSet(false, true))
      {
        Logger.getLogger(PKG_NAME).log(Level.SEVERE, "XMLReader unsupported feature " + feature);
      }
    }
    catch (SAXNotRecognizedException e)
    {
      if (logFeatureWarnings.compareAndSet(false, true))
      {
        Logger.getLogger(PKG_NAME).log(Level.SEVERE, "XMLReader unrecognized feature " + feature);
      }
    }
  }
  /**
   * Returns an error response after a parsing error. The response has the
   * requestID of the batch request, the error response message of the parsing
   * exception message and the type 'malformed request'.
   *
   * @param is the xml InputStream to parse
   * @param is the XML InputStream to parse
   * @param batchResponse the JAXB object to fill in
   * @param parserErrorMessage the parsing error message
   *
@@ -602,17 +646,41 @@
                                                    String parserErrorMessage) {
    ErrorResponse errorResponse = objFactory.createErrorResponse();
    try {
    try
    {
      // try alternative XML parsing using SAX to retrieve requestID value
      XMLReader xmlReader = XMLReaderFactory.createXMLReader();
      SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
      // Ensure we are doing basic secure processing.
      saxParserFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
      saxParserFactory.setXIncludeAware(false);
      saxParserFactory.setNamespaceAware(true);
      // Configure a safe XMLReader appropriate for SOAP.
      XMLReader xmlReader = saxParserFactory.newSAXParser().getXMLReader();
      safeSetFeature(xmlReader, "http://xml.org/sax/features/validation", false);
      safeSetFeature(xmlReader, "http://xml.org/sax/features/external-general-entities", false);
      safeSetFeature(xmlReader, "http://xml.org/sax/features/external-parameter-entities", false);
      safeSetFeature(xmlReader, "http://xml.org/sax/features/namespaces", true);
      safeSetFeature(xmlReader, "http://apache.org/xml/features/disallow-doctype-decl", true);
      // clear previous match
      this.contentHandler.requestID = null;
      xmlReader.setContentHandler(this.contentHandler);
      is.reset();
      xmlReader.parse(new InputSource(is));
    } catch (Throwable e) {
      // document cannot be parsed, so will jump here
    }
    catch (ParserConfigurationException e)
    {
      // ignore
    }
    catch (SAXException e)
    {
      // ignore
    }
    catch (IOException e)
    {
      // ignore
    }
    if ( parserErrorMessage!= null ) {
      errorResponse.setMessage(parserErrorMessage);
@@ -810,9 +878,9 @@
    return nextID;
  }
    /**
   * This class is used when a xml request is malformed to retrieve the
   * requestID value using an event xml parser.
  /**
   * This class is used when an XML request is malformed to retrieve the
   * requestID value using an event XML parser.
   */
  private static class DSMLContentHandler extends DefaultHandler {
    private String requestID;