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

com.sun.enterprise.security.jauth.jaspic.provider.BaseAuthConfig Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
// Portions Copyright [2018-2021] [Payara Foundation and/or its affiliates]
package com.sun.enterprise.security.jauth.jaspic.provider;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.namespace.QName;
import jakarta.xml.soap.MimeHeaders;
import jakarta.xml.soap.Name;
import jakarta.xml.soap.SOAPBody;
import jakarta.xml.soap.SOAPElement;
import jakarta.xml.soap.SOAPEnvelope;
import jakarta.xml.soap.SOAPException;
import jakarta.xml.soap.SOAPMessage;
import jakarta.xml.soap.SOAPPart;
import jakarta.xml.ws.handler.MessageContext;
import jakarta.xml.ws.handler.soap.SOAPMessageContext;

import com.sun.enterprise.deployment.MethodDescriptor;
import com.sun.enterprise.deployment.runtime.common.MessageDescriptor;
import com.sun.enterprise.deployment.runtime.common.MessageSecurityDescriptor;
import com.sun.enterprise.deployment.runtime.common.ProtectionDescriptor;
import com.sun.enterprise.security.jauth.AuthPolicy;
import com.sun.enterprise.security.webservices.LogUtils;

/**
 * This class is the container's base interface to the AuthConfig subsystem to get AuthContext
 * objects on which to invoke message layer authentication providers. It is not intended to be layer
 * or web services specific (see getMechanisms method at end). The ServerAuthConfig and
 * ClientAuthConfig classes extend this class.
 */
public class BaseAuthConfig {

    private static final Logger logger = LogUtils.getLogger();

    private Object defaultContext_;

    // holds protected msd that applies to all methods (if there is one)
    private MessageSecurityDescriptor superMSD_;
    private int superIndex_;

    private ArrayList contexts_;

    private List messageSecurityDescriptors_;

    private ArrayList contextsForOpcodes_;

    private HashMap contextsForOpNames_;

    private boolean onePolicy_;

    private final Object contextLock = new Object();

    private ExplicitNull explicitNull = new ExplicitNull();

    protected BaseAuthConfig(Object context) {

        defaultContext_ = context;
        superMSD_ = null;
        superIndex_ = -1;

        messageSecurityDescriptors_ = null;
        contexts_ = null;
        contextsForOpcodes_ = null;
        contextsForOpNames_ = null;

        onePolicy_ = true;

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "WSS: New BAC defaultContext: {0}", defaultContext_);
        }
    }

    protected BaseAuthConfig(List descriptors, ArrayList authContexts) {

        defaultContext_ = null;
        superMSD_ = null;
        superIndex_ = -1;

        messageSecurityDescriptors_ = descriptors;
        contexts_ = authContexts;
        contextsForOpcodes_ = null;
        contextsForOpNames_ = null;

        onePolicy_ = true;

        for (int i = 0; i < descriptors.size(); i++) {

            MessageSecurityDescriptor msd = (MessageSecurityDescriptor) descriptors.get(i);

            // determine if all the different messageSecurityDesriptors have the
            // same policy which will help us interpret the effective policy if
            // we cannot determine the opcode of a request at runtime.

            for (int j = 0; j < descriptors.size(); j++) {
                if (j != i && !policiesAreEqual(msd, ((MessageSecurityDescriptor) descriptors.get(j)))) {
                    onePolicy_ = false;
                }
            }
        }

        for (int i = 0; defaultContext_ == null && i < descriptors.size(); i++) {

            MessageSecurityDescriptor msd = (MessageSecurityDescriptor) descriptors.get(i);

            AuthPolicy requestPolicy = getAuthPolicy(msd.getRequestProtectionDescriptor());
            AuthPolicy responsePolicy = getAuthPolicy(msd.getResponseProtectionDescriptor());

            boolean noProtection = (!requestPolicy.authRequired() && !responsePolicy.authRequired());

            // if there is one policy, and it is null set the associated context as the
            // defaultContext used for all messages.
            if (i == 0 && onePolicy_ && noProtection) {
                defaultContext_ = explicitNull;
                break;
            }

            List mDs = msd.getMessageDescriptors();

            for (int j = 0; mDs != null && j < mDs.size(); j++) {

                MessageDescriptor mD = (MessageDescriptor) mDs.get(j);
                MethodDescriptor methD = mD.getMethodDescriptor();

                // if any msd covers all methods and operations.
                if ((mD.getOperationName() == null && methD == null) || (methD != null && methD.getStyle() == 1)) {

                    if (onePolicy_) {
                        // if there is only one policy make it the default.
                        defaultContext_ = contexts_.get(i);
                        if (defaultContext_ == null) {
                            defaultContext_ = explicitNull;
                        }
                        break;
                    } else if (superIndex_ == -1) {
                        // if it has a noProtection policy make it the default.
                        if (noProtection) {
                            defaultContext_ = explicitNull;
                        } else {
                            superIndex_ = i;
                        }
                    } else if (!policiesAreEqual(msd, ((MessageSecurityDescriptor) descriptors.get(superIndex_)))) {
                        // if there are conflicting policies that cover all methods
                        // set the default policy to noProtection
                        defaultContext_ = explicitNull;
                        superIndex_ = -1;
                        break;
                    }
                }
            }
        }
        // if there is protected policy that applies to all methods remember the descriptor.
        // Note that the corresponding policy is not null, and thus it is not the default.
        // wherever there is a conflicting policy the effective policy will be noProtection.
        if (superIndex_ >= 0) {
            superMSD_ = (MessageSecurityDescriptor) descriptors.get(superIndex_);
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "WSS: new BAC defaultContext_: {0} superMSD index: {1} onePolicy_: {2}",
                    new Object[] { defaultContext_, superIndex_, onePolicy_ });
        }
    }

    protected static AuthPolicy getAuthPolicy(ProtectionDescriptor pd) {
        int sourceAuthType = AuthPolicy.SOURCE_AUTH_NONE;
        boolean recipientAuth = false;
        boolean beforeContent = false;
        if (pd != null) {
            String source = pd.getAttributeValue(ProtectionDescriptor.AUTH_SOURCE);
            if (source != null) {
                if (source.equals(AuthPolicy.SENDER)) {
                    sourceAuthType = AuthPolicy.SOURCE_AUTH_SENDER;
                } else if (source.equals(AuthPolicy.CONTENT)) {
                    sourceAuthType = AuthPolicy.SOURCE_AUTH_CONTENT;
                }
            }
            String recipient = pd.getAttributeValue(ProtectionDescriptor.AUTH_RECIPIENT);
            if (recipient != null) {
                recipientAuth = true;
                if (recipient.equals(AuthPolicy.BEFORE_CONTENT)) {
                    beforeContent = true;
                } else if (recipient.equals(AuthPolicy.AFTER_CONTENT)) {
                    beforeContent = false;
                }
            }
        }

        return new AuthPolicy(sourceAuthType, recipientAuth, beforeContent);
    }

    private static boolean isMatchingMSD(MethodDescriptor targetMD, MessageSecurityDescriptor mSD) {
        List messageDescriptors = mSD.getMessageDescriptors();
        if (messageDescriptors.isEmpty()) {
            // If this happens the deployment descriptor is invalid.
            //
            // Unfortunately the deployment system does not catch such problems.
            // This case will be treated the same as if there was an empty message
            // element, and the deployment will be allowed to succeed.
            return true;
        }

        for (int i = 0; i < messageDescriptors.size(); i++) {
            MessageDescriptor nextMD = (MessageDescriptor) messageDescriptors.get(i);
            MethodDescriptor mD = nextMD.getMethodDescriptor();
            String opName = nextMD.getOperationName();

            if (opName == null && (mD == null || mD.implies(targetMD))) {
                return true;
            }
        }

        return false;
    }

    private static boolean policiesAreEqual(MessageSecurityDescriptor reference, MessageSecurityDescriptor other) {
        if (!getAuthPolicy(reference.getRequestProtectionDescriptor()).equals(getAuthPolicy(other.getRequestProtectionDescriptor())) ||

                !getAuthPolicy(reference.getResponseProtectionDescriptor())
                        .equals(getAuthPolicy(other.getResponseProtectionDescriptor()))) {

            return false;
        }
        return true;
    }

    /*
     * When method argument is null, returns the default AC if there is one, or the onePolicy shared by
     * all methods if there is one, or throws an error. method is called with null argument when the
     * method cannot be determined (e.g. when the message is encrypted)
     */
    private Object getContextForMethod(Method m) {
        Object rvalue = null;
        synchronized (contextLock) {
            if (defaultContext_ != null) {
                rvalue = defaultContext_;
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "WSS: ForMethod returning default_context: {0}", rvalue);
                }
                return rvalue;
            }
        }
        if (m != null) {
            int match = -1;
            MethodDescriptor targetMD = new MethodDescriptor(m);
            for (int i = 0; i < messageSecurityDescriptors_.size(); i++) {
                if (isMatchingMSD(targetMD, (MessageSecurityDescriptor) messageSecurityDescriptors_.get(i))) {
                    if (match < 0) {
                        match = i;
                    } else if (!policiesAreEqual((MessageSecurityDescriptor) messageSecurityDescriptors_.get(match),
                            (MessageSecurityDescriptor) messageSecurityDescriptors_.get(i))) {

                        // set to unprotected because of conflicting policies

                        rvalue = explicitNull;
                        match = -1;
                        if (logger.isLoggable(Level.FINE)) {
                            logger.log(Level.FINE, "WSS: ForMethod detected conflicting policies: {0}.{1}", new Object[] { match, i });
                        }
                        break;
                    }
                }
            }
            if (match >= 0) {
                rvalue = contexts_.get(match);
                if (rvalue == null) {
                    rvalue = explicitNull;
                }
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "WSS: ForMethod returning matched context: {0}", rvalue);
                }
            }
        } else if (onePolicy_ && contexts_.size() > 0) {
            // ISSUE: since the method is undefined we will not be
            // able to tell if the defined policy covers this method.
            // We will be optimistic and try the policy, because
            // the server will reject the call if the method is not
            // covered by the policy.
            // If the policy is not null, there remains a problem at the
            // client on the response side, as it is possible that the
            // client will enforce the wrong policy on the response.
            // For this reason, messages in sun-application-client.xml
            // should be keyed by operation-name.

            rvalue = contexts_.get(0);
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, "WSS: ForMethod resorting to first context: {0}", rvalue);
            }

        } else {
            if (logger.isLoggable(Level.FINE)) {
                logger.fine("WSS: Unable to select policy for SOAP Message");
            }
            throw new RuntimeException("Unable to select policy for Message");
        }
        return rvalue;
    }

    private static String getOpName(SOAPMessage message) {

        String rvalue = null;

        // first look for a SOAPAction header.
        // this is what .net uses to identify the operation

        MimeHeaders headers = message.getMimeHeaders();
        if (headers != null) {
            String[] actions = headers.getHeader("SOAPAction");
            if (actions != null && actions.length > 0) {
                rvalue = actions[0];
                if (rvalue != null && rvalue.equals("\"\"")) {
                    rvalue = null;
                }
            }
        }

        // if that doesn't work then we default to trying the name
        // of the first child element of the SOAP envelope.

        if (rvalue == null) {
            Name name = getName(message);
            if (name != null) {
                rvalue = name.getLocalName();
            }
        }

        return rvalue;
    }

    private static String getOpName(SOAPMessageContext soapMC) {

        String rvalue;

        // first look for a the property value in the context
        QName qName = (QName) soapMC.get(MessageContext.WSDL_OPERATION);
        if (qName != null) {
            rvalue = qName.getLocalPart();
        } else {
            rvalue = getOpName(soapMC.getMessage());
        }

        return rvalue;
    }

    private Object getContextForOpName(String operation) {

        synchronized (contextLock) {
            if (contextsForOpNames_ == null) {

                // one time initialization of the opName to authContext array.

                contextsForOpNames_ = new HashMap();
                for (int i = 0; messageSecurityDescriptors_ != null && i < messageSecurityDescriptors_.size(); i++) {

                    MessageSecurityDescriptor mSD = (MessageSecurityDescriptor) messageSecurityDescriptors_.get(i);

                    List mDs = mSD.getMessageDescriptors();

                    for (int j = 0; mDs != null && j < mDs.size(); j++) {

                        MessageDescriptor mD = (MessageDescriptor) mDs.get(j);
                        String opName = mD.getOperationName();

                        if (opName != null) {

                            if (contextsForOpNames_.containsKey(opName)) {

                                Integer k = (Integer) contextsForOpNames_.get(opName);
                                if (k != null) {

                                    MessageSecurityDescriptor other = (MessageSecurityDescriptor) messageSecurityDescriptors_
                                            .get(k.intValue());

                                    // set to null if different policies on operation

                                    if (!policiesAreEqual(mSD, other)) {
                                        contextsForOpNames_.put(opName, null);
                                    }
                                }
                            } else if (superMSD_ != null && !policiesAreEqual(mSD, superMSD_)) {
                                // set to null if operation policy differs from superPolicy
                                contextsForOpNames_.put(opName, null);
                            } else {
                                contextsForOpNames_.put(opName, Integer.valueOf(i));
                            }
                        }
                    }
                }
            }
        }

        Object rvalue = null;
        if (operation != null) {
            if (contextsForOpNames_.containsKey(operation)) {
                Integer k = (Integer) contextsForOpNames_.get(operation);
                if (k != null) {
                    rvalue = contexts_.get(k.intValue());
                }
            } else if (superIndex_ >= 0) {
                // if there is a msb that matches all methods, use the
                // associatedContext
                rvalue = contexts_.get(superIndex_);
            }

            if (rvalue == null) {
                // else return explicitNull under the assumption
                // that methodName was known, and no match was found
                rvalue = explicitNull;
            }
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, "WSS: ForOpName={0} context: {1}", new Object[] { operation, rvalue });
            }
        }
        return rvalue;
    }

    // DO NOT CALL THIS ON THE SERVER SIDE, as it will return a null
    // context if there is no default context and there isn't a message
    // element defined with the corresponding operation name (even though the
    // corresponding method may be protected).
    //
    // This method is intended to be used by clients where it serves as a
    // work-around for not being able to map the message to the method (due
    // to lack of access to a streaming handler equivalent).
    //
    // This method will not be called when the handler argument passed in
    // a call to getContext or getContextForOpCode is not null.
    // Thus, server-side calls to these methods must pass a non-null
    // handler argument.

    private Object getContextForMessage(SOAPMessage message) {

        String opName = getOpName(message);

        Object rvalue = getContextForOpName(opName);
        if (rvalue == null) {

            // opName is not mapped or msg body is encrypted, and the best
            // we can do is try to return a policy that applies to all
            // operations, if there is one.

            rvalue = getContextForMethod(null);

        }
        return rvalue;
    }

    // used by jaxws system handler delegates and handlers
    protected Object getContext(SOAPMessageContext soapMC) {

        Object rvalue = null;

        synchronized (contextLock) {
            if (defaultContext_ != null) {
                rvalue = defaultContext_;
            }
        }

        if (rvalue == null) {

            Method m = getMethod(soapMC);
            String opName = null;

            if (m != null) {
                rvalue = getContextForMethod(m);
            }

            if (rvalue == null) {
                opName = getOpName(soapMC);
                if (opName != null) {
                    rvalue = getContextForOpName(opName);
                }
            }

            if (rvalue == null && (m == null || opName == null)) {

                // we were unable to determine either method or
                // opName, so lets see if one policy applies to all

                rvalue = getContextForMethod(null);
            }
        }

        if (rvalue != null && rvalue instanceof ExplicitNull) {
            rvalue = null;
        }

        return rvalue;
    }

    private static Name getName(SOAPMessage message) {
        Name rvalue = null;
        SOAPPart soap = message.getSOAPPart();
        if (soap != null) {
            try {
                SOAPEnvelope envelope = soap.getEnvelope();
                if (envelope != null) {
                    SOAPBody body = envelope.getBody();
                    if (body != null) {
                        Iterator it = body.getChildElements();
                        while (it.hasNext()) {
                            Object o = it.next();
                            if (o instanceof SOAPElement) {
                                rvalue = ((SOAPElement) o).getElementName();
                                break;
                            }
                        }
                    }
                }
            } catch (SOAPException se) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "WSS: Unable to get SOAP envelope", se);
                }
            }
        }

        return rvalue;
    }

    public static Method getMethod(SOAPMessageContext soapMC) {

        // It should never come here
        return null;
    }

    // each instance of AuthConfig maps to one provider
    // configuration, either via a message-security-binding, or a default
    // provider-config.

    // mechanisms are temporarily encapsulated here, until a method that
    // returns the list of supported mechanisms is added to
    // jauth.ServerAuthContext and jauth.ClientAuthContext.
    public QName[] getMechanisms() {
        return mechanisms;
    }

    // WSS security header QName
    private static QName mechanisms[] = new QName[] {
            new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", "wsse") };

    // internal class used to differentiate not protected from policy undefined or
    // not determined.

    static class ExplicitNull {

        ExplicitNull() {
        }

        @Override
        public boolean equals(Object other) {
            return (other != null && other instanceof ExplicitNull ? true : false);
        }

        @Override
        public int hashCode() {
            return super.hashCode();
        }

        @Override
        public String toString() {
            return "ExplicitNull";
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy