All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.xml.wss.impl.resolver.URIResolver Maven / Gradle / Ivy

There is a newer version: 4.0.4
Show newest version
/*
 * Copyright (c) 2023 Contributors to the Eclipse Foundation
 * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0, which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

package com.sun.xml.wss.impl.resolver;

import com.sun.xml.wss.WSITXMLFactory;
import com.sun.xml.wss.XWSSecurityException;
import com.sun.xml.wss.impl.MessageConstants;
import com.sun.xml.wss.impl.SecurableSoapMessage;
import com.sun.xml.wss.impl.XWSSecurityRuntimeException;
import com.sun.xml.wss.impl.dsig.NamespaceContextImpl;
import com.sun.xml.wss.impl.misc.URI;
import com.sun.xml.wss.logging.LogDomainConstants;
import com.sun.xml.wss.logging.LogStringsMessages;
import com.sun.xml.wss.swa.MimeConstants;

import jakarta.xml.soap.AttachmentPart;
import jakarta.xml.soap.SOAPException;
import jakarta.xml.soap.SOAPMessage;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.xml.security.signature.XMLSignatureInput;
import org.apache.xml.security.signature.XMLSignatureNodeSetInput;
import org.apache.xml.security.utils.XMLUtils;
import org.apache.xml.security.utils.resolver.ResourceResolver;
import org.apache.xml.security.utils.resolver.ResourceResolverContext;
import org.apache.xml.security.utils.resolver.ResourceResolverException;
import org.apache.xml.security.utils.resolver.ResourceResolverSpi;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import static com.sun.xml.wss.logging.LogDomainConstants.IMPL_DOMAIN_BUNDLE;

/**
 * This resolver is used for resolving URIs.
 *
 * Resolves URLs that refers to attachments that has a (1) Content-ID
 * or a (2) Content-Location MIME header.
 *
 * In case of Content-Location, the URL may require resolution to determine
 * the referenced attachment [RFC2557].
 *
 * Also resolves (3) URL's that are Ids on XML elements within the
 * SOAPMessage.
 *
 * @author XWS-Security Team
 *
 */
public class URIResolver extends ResourceResolverSpi {

    private static final int ID_REFERENCE  = 0;
    private static final int CID_REFERENCE = 1;
    private static final int CLOCATION_REFERENCE  = 2;

    private final String[] errors = new String[] {
        "Can not resolve reference type",
        "Required SOAPMessage instance to resolve reference"
    };

    private static String implementationClassName = URIResolver.class.getName();
    private static final Logger log = Logger.getLogger(LogDomainConstants.IMPL_DOMAIN, IMPL_DOMAIN_BUNDLE);

    int referenceType = -1;

    private SOAPMessage soapMsg;

    public URIResolver() {}

    public URIResolver(SOAPMessage soapMsg) {
        this.soapMsg = soapMsg;
    }

    public void setSOAPMessage(SOAPMessage soapMsg) {
        this.soapMsg = soapMsg;
    }

    /**
     * Method getResolverName
     *
     * @return The resolver implementation class name
     */
    public static String getResolverName() {
        return implementationClassName;
    }

    /**
     * Method engineResolve
     *
     * @return XMLSignatureInput
     *
     */
    public XMLSignatureInput engineResolve(Attr uri, String baseURI) throws ResourceResolverException {

        if (referenceType == -1) {
            if (!engineCanResolve(uri, baseURI)) {
                throw generateException(uri, baseURI, errors[0]);
            }
        }

        XMLSignatureInput result = null;
        switch (referenceType) {
            case ID_REFERENCE:
                result = _resolveId(uri, baseURI);
                break;
            case CID_REFERENCE:
                result = _resolveCid(uri, baseURI);
                break;
            case CLOCATION_REFERENCE:
                try {
                    result = _resolveClocation(uri, baseURI);
                } catch (URIResolverException ure) {
                    ResourceResolverContext resContext = new ResourceResolverContext(uri, baseURI, false);
                    result = ResourceResolver.resolve(resContext);
                }
                break;
            default:
        }

        referenceType = -1;
        return result;
    }


    private XMLSignatureInput _resolveId(Attr uri, String baseUri) throws ResourceResolverException {
        String uriNodeValue = uri.getNodeValue();
        Document doc = uri.getOwnerDocument();

        XMLUtils.circumventBug2650(doc);

        Element selectedElem = null;
        if (uriNodeValue.equals("")) {
            selectedElem = doc.getDocumentElement();
        } else {
            String id = uriNodeValue.substring(1);
            selectedElem = getElementById(doc, id);
        }

        if (selectedElem == null) {
            log.log(Level.SEVERE,
                LogStringsMessages.WSS_0604_CANNOT_FIND_ELEMENT());
            throw new ResourceResolverException("empty", uri.getValue(), baseUri);
        }

        Set resultSet = prepareNodeSet(selectedElem);
        XMLSignatureNodeSetInput result = new XMLSignatureNodeSetInput(resultSet);
        result.setMIMEType(MimeConstants.TEXT_XML_TYPE);

        try {
            URI uriNew = new URI(new URI(baseUri), uriNodeValue);
            result.setSourceURI(uriNew.toString());
        } catch (URI.MalformedURIException ex) {
            result.setSourceURI(baseUri);
        }
        return result;
    }

    /**
     * Resolver for content-ID.
     *
     * A content-ID MIME header value corresponding to the URL scheme
     * is defined in RFC 2392.
     *
     * For example, content-ID of "foo" may be specified with Content-ID:<foo>
     * and be references with a CID schema URL cid:foo.
     */
    private XMLSignatureInput _resolveCid(Attr uri, String baseUri) throws ResourceResolverException {
        String uriNodeValue = uri.getNodeValue();

        if (soapMsg == null) {
            throw generateException(uri, baseUri, errors[1]);
        }

        AttachmentSignatureInput result;
        try {
            AttachmentPart part = ((SecurableSoapMessage) soapMsg).getAttachmentPart(uriNodeValue);
            if (part == null) {
                // log
                throw new ResourceResolverException("empty", uri.getValue(), baseUri);
            }
            Object[] obj = AttachmentSignatureInput._getSignatureInput(part);
            result = new AttachmentSignatureInput((byte[]) obj[1]);
            result.setMimeHeaders((Vector)obj[0]);
            result.setContentType(part.getContentType());
        } catch (Exception e) {
            throw new ResourceResolverException(e, uri.getValue(), baseUri, "empty");
        }

        try {
            URI uriNew = new URI(new URI(baseUri), uriNodeValue);
            result.setSourceURI(uriNew.toString());
        } catch (URI.MalformedURIException ex) {
            result.setSourceURI(baseUri);
        }
        return result;
    }

    private XMLSignatureInput _resolveClocation(Attr uri, String baseUri)
        throws ResourceResolverException, URIResolverException {
        URI uriNew;
        try {
            uriNew = getNewURI(uri.getNodeValue(), baseUri);
        } catch (URI.MalformedURIException ex) {
            throw new ResourceResolverException(ex, uri.getValue(), baseUri, "empty");
        }

        if (soapMsg == null) {
            throw generateException(uri, baseUri, errors[1]);
        }

        AttachmentSignatureInput result;
        try {
            AttachmentPart part = ((SecurableSoapMessage)soapMsg).getAttachmentPart(uriNew.toString());
            if (part == null) {
                // log
                throw new URIResolverException();
            }
            Object[] obj = AttachmentSignatureInput._getSignatureInput(part);
            result = new AttachmentSignatureInput((byte[])obj[1]);
            result.setMimeHeaders((Vector)obj[0]);
            result.setContentType(part.getContentType());
        } catch (XWSSecurityException | SOAPException | IOException e) {
            throw new ResourceResolverException(e, uri.getValue(), baseUri, "empty");
        }
        result.setSourceURI(uriNew.toString());
        return result;
    }

    /**
     * Method engineCanResolve
     *
     * @return true if uri node can be resolved, false otherwise
     */
    public boolean engineCanResolve(Attr uri, String baseURI) {
        if (uri == null) {
            return false;
        }

        String uriNodeValue = uri.getNodeValue();

        /* #Id, #wsu:Id */
        if (uriNodeValue.startsWith("#")) {
            referenceType = ID_REFERENCE;
            return true;
        }

        /* cid:xxx */
        if (uriNodeValue.startsWith("cid:")) {
            referenceType = CID_REFERENCE;
            return true;
        }

        /* attachmentRef:xxx */
        if (uriNodeValue.startsWith("attachmentRef:")) {
            referenceType = CID_REFERENCE;
            return true;
        }

        URI uriNew;
        try {
            uriNew = getNewURI(uriNodeValue, baseURI);
        } catch (URI.MalformedURIException ex) {
            // log
            return false;
        }

        /* content-location of the type http:// (for now) */
        if ((uriNew != null) &&
            uriNew.getScheme().equals("http") ||
            uriNodeValue.startsWith("thismessage:/") ||
            !(uriNew.getScheme().equals("ftp") ||
                uriNew.getScheme().equals("telnet") ||
                uriNew.getScheme().equals("gopher") ||
                uriNew.getScheme().equals("news") ||
                uriNew.getScheme().equals("mailto") ||
                uriNew.getScheme().equals("file"))) {
            // log
            referenceType = CLOCATION_REFERENCE;
            return true;
        }

        return false;
    }

    /**
     * Looks up elements with wsu:Id or Id in xenc or dsig namespace
     *
     * @return element
     */
    private Element getElementById(
        Document doc,
        String id) {

        Element  selement = doc.getElementById(id);
        if (selement != null) {
            if (MessageConstants.debug) {
                log.log(Level.FINEST, "Document.getElementById() returned " + selement);
            }
            return selement;
        }

        if (MessageConstants.debug) {
            log.log(Level.FINEST, "Document.getElementById() FAILED......'" + id + "'");
        }

       //----------------------------------
//       Element nscontext = XMLUtils.createDSctx(doc,
//                                                "wsu",
//                                                MessageConstants.WSU_NS);
//       Element element =
//           (Element) XPathAPI.selectSingleNode(
//               doc, "//*[@wsu:Id='" + id + "']", nscontext);
//
//       if (element == null) {
//           NodeList elems = XPathAPI.selectNodeList(
//                                           doc,
//                                           "//*[@Id='" + id + "']",
//                                           nscontext);
//
//           for (int i=0; i < elems.getLength(); i++) {
//                Element elem = (Element)elems.item(i);
//                String namespace = elem.getNamespaceURI();
//                if (namespace.equals(MessageConstants.DSIG_NS) ||
//                    namespace.equals(MessageConstants.XENC_NS)) {
//                    element = elem;
//                    break;
//                }
//           }
//        }

        Element element = null;
        NodeList elems = null;
        String xpath =  "//*[@wsu:Id='" + id + "']";
        XPathFactory xpathFactory = WSITXMLFactory.createXPathFactory(WSITXMLFactory.DISABLE_SECURE_PROCESSING);
        XPath xPATH = xpathFactory.newXPath();
        xPATH.setNamespaceContext(getNamespaceContext(doc));
        XPathExpression xpathExpr;
        try {
            xpathExpr = xPATH.compile(xpath);
            elems = (NodeList)xpathExpr.evaluate(doc,XPathConstants.NODESET);
        } catch (XPathExpressionException ex) {
            //TODO: this logstring is not in this package
            log.log(Level.SEVERE, "WSS0375.error.apache.xpathAPI", new Object[] {id, ex.getMessage()});
            throw new XWSSecurityRuntimeException(ex);
        }

        if (elems != null) {
            if (elems.getLength() > 1) {
                //TODO: localize the string
                throw new XWSSecurityRuntimeException("XPath Query resulted in more than one node");
            } else {
                element = (Element)elems.item(0);
            }
        }

        if (element == null) {
            xpath =  "//*[@Id='" + id + "']";
            try {
                xpathExpr = xPATH.compile(xpath);
                elems = (NodeList)xpathExpr.evaluate(doc,XPathConstants.NODESET);
            } catch (XPathExpressionException ex) {
                //TODO: this logstring is not in this package
                log.log(Level.SEVERE,
                    LogStringsMessages.WSS_0375_ERROR_APACHE_XPATH_API(id, ex.getMessage()),
                    new Object[] {id, ex.getMessage()});
                throw new XWSSecurityRuntimeException(ex);
            }
        }
        if (elems != null) {
            if (elems.getLength() > 1) {
                for (int i=0; i < elems.getLength(); i++) {
                    Element elem = (Element)elems.item(i);
                    String namespace = elem.getNamespaceURI();
                    if (namespace.equals(MessageConstants.DSIG_NS) ||
                        namespace.equals(MessageConstants.XENC_NS)) {
                        element = elem;
                        break;
                    }
                }

            } else {
                element = (Element)elems.item(0);
            }
        }
        //------------------------------

        if (element == null) {

            NodeList assertions =
                doc.getElementsByTagNameNS(MessageConstants.SAML_v1_0_NS,
                    MessageConstants.SAML_ASSERTION_LNAME);
            int len = assertions.getLength();
            if (len > 0) {
                for (int i=0; i < len; i++) {
                    Element elem = (Element)assertions.item(i);
                    String assertionId = elem.getAttribute(MessageConstants.SAML_ASSERTIONID_LNAME);
                    if (id.equals(assertionId)) {
                        element = elem;
                        break;
                    }
                }
            }
        }

        return element;
    }

    private ResourceResolverException generateException(Attr uri, String baseUri, String error) {
        XWSSecurityException xwssE = new XWSSecurityException(error);
        return new ResourceResolverException(xwssE, uri.getValue(), baseUri, "empty");
    }

    /**
     * prepareNodeSet
     *
     * @param node the node referenced by the -
     *             URI fragment. If null, returns -
     *             an empty set.
     */
    private Set prepareNodeSet(Node node) {
        Set nodeSet = new HashSet<>();
        if (node != null) {
            nodeSetMinusCommentNodes(node, nodeSet, null);
        }
        return nodeSet;
    }

    /**
     * Method nodeSetMinusCommentNodes
     *
     * @param node the node to traverse
     * @param nodeSet the set of nodes traversed so far
     * @param prevSibling the previous sibling node
     */
    @SuppressWarnings("unchecked")
    private void nodeSetMinusCommentNodes(Node node, Set nodeSet,
        Node prevSibling) {
        switch (node.getNodeType()) {
            case Node.ELEMENT_NODE :
                NamedNodeMap attrs = node.getAttributes();
                if (attrs != null) {
                    for (int i = 0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy