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

com.sun.enterprise.security.web.integration.WebSecurityManager Maven / Gradle / Ivy

There is a newer version: 4.1.2.181
Show newest version
/*
 * 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 [2016] [Payara Foundation and/or its affiliates] 

package com.sun.enterprise.security.web.integration;

import org.glassfish.internal.api.ServerContext;
import java.security.*;
import java.util.Set;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.Collections;
import java.net.URL;

import javax.servlet.http.HttpServletRequest;
import javax.security.jacc.*;

import java.util.logging.*;

import com.sun.logging.LogDomains;
import com.sun.enterprise.deployment.WebBundleDescriptor;
import com.sun.enterprise.security.common.AppservAccessController;
import com.sun.enterprise.security.ee.CachedPermission;
import com.sun.enterprise.security.ee.CachedPermissionImpl;
import com.sun.enterprise.security.ee.PermissionCache;
import com.sun.enterprise.security.ee.PermissionCacheFactory;
import com.sun.enterprise.security.SecurityContext;
import com.sun.enterprise.security.ee.audit.AppServerAuditManager;
import com.sun.enterprise.deployment.runtime.common.SecurityRoleMapping;
import org.glassfish.security.common.PrincipalImpl;
import org.glassfish.security.common.Group;
import com.sun.enterprise.config.serverbeans.*;
//V3:Commented import com.sun.enterprise.server.ApplicationServer;
import com.sun.enterprise.deployment.runtime.common.wls.SecurityRoleAssignment;
import com.sun.enterprise.deployment.web.LoginConfiguration;
import com.sun.enterprise.deployment.runtime.web.SunWebApp;
//import org.apache.catalina.Globals;
import com.sun.enterprise.security.SecurityRoleMapperFactoryGen;
import com.sun.enterprise.security.SecurityServicesUtil;
import com.sun.enterprise.security.ee.SecurityUtil;
import com.sun.enterprise.security.WebSecurityDeployerProbeProvider;
import com.sun.enterprise.security.audit.AuditManager;
import java.util.List;
import org.glassfish.api.web.Constants;

/**
 * The class implements the JSR 115 - JavaTM Authorization Contract for Containers.
 * This class is a companion class of EJBSecurityManager.
 *
 * All the security decisions required to allow access to a resource are defined
 * in that class.
 *
 * @author Jean-Francois Arcand
 * @author Harpreet Singh.
 * @todo introduce a new class called AbstractSecurityManager. Move functionality
 * from this class and EJBSecurityManager class and extend this class from
 * AbstractSecurityManager
 */
public class WebSecurityManager  {
    private static final Logger logger =
    Logger.getLogger(LogDomains.SECURITY_LOGGER);

    /**
     * Request path. Copied from org.apache.catalina.Globals;
     * Required to break dependence on WebTier of Security Module
     */
    public static final String CONSTRAINT_URI =
        "org.apache.catalina.CONSTRAINT_URI";

    private static final String RESOURCE = "hasResourcePermission";
    private static final String USERDATA = "hasUserDataPermission";
    private static final String ROLEREF = "hasRoleRefPermission";

    private static final String DEFAULT_PATTERN = "/";
    private static final String EMPTY_STRING = "";

    // The context ID associated with this instance. This is the name
    // of the application
    private  String CONTEXT_ID = null;
    private String CODEBASE = null;

    // The JACC policy provider.
    protected Policy policy = Policy.getPolicy();
    protected PolicyConfiguration pc = null;
    protected PolicyConfigurationFactory pcf= null;
    protected CodeSource codesource = null;

    // protection domain cache
    private Map protectionDomainCache =
        Collections.synchronizedMap(new WeakHashMap());

    private static final WebResourcePermission allResources =
    new WebResourcePermission("/*",(String) null);

    private static final WebUserDataPermission allConnections =
    new WebUserDataPermission("/*",null);

    private static Permission[] protoPerms = {
        allResources,
	allConnections
    };

    // permissions tied to unchecked permission cache, and used
    // to determine if the effective policy is grant all
    // WebUserData and WebResource permisions.
    private CachedPermission allResourcesCP = null;

    private CachedPermission allConnectionsCP = null;

    // unchecked permission cache
    private PermissionCache uncheckedPermissionCache = null;

    private static Set defaultPrincipalSet =
	SecurityContext.getDefaultSecurityContext().getPrincipalSet();

    //private SecurityRoleMapperFactory factory = null;
    private WebSecurityManagerFactory wsmf = null;
    private ServerContext serverContext = null;
    // WebBundledescriptor
    private WebBundleDescriptor wbd = null;
    //ProbeProvider
    private WebSecurityDeployerProbeProvider probeProvider = new WebSecurityDeployerProbeProvider();
    private boolean register = true;

    WebSecurityManager(WebBundleDescriptor wbd, ServerContext svc, WebSecurityManagerFactory fact, boolean register) throws PolicyContextException{
        this.register = register;
         this.wbd = wbd;
        this.CONTEXT_ID = getContextID(wbd);
        this.serverContext = svc;
        this.wsmf = fact;
        String appname = getAppId();
        //factory = SecurityRoleMapperFactoryGen.getSecurityRoleMapperFactory();
        postConstruct();
        initialise(appname);
    }

    // Create a WebSecurityObject
    private WebSecurityManager(WebBundleDescriptor wbd, WebSecurityManagerFactory fact) throws PolicyContextException {
        this(wbd,null, fact);
    }

    WebSecurityManager(WebBundleDescriptor wbd, ServerContext svc, WebSecurityManagerFactory fact) throws PolicyContextException {
        this.wbd = wbd;
        this.CONTEXT_ID = getContextID(wbd);
        this.serverContext = svc;
        this.wsmf = fact;
        String appname = getAppId();
//        factory = SecurityRoleMapperFactoryGen.getSecurityRoleMapperFactory();
        postConstruct();
        initialise(appname);
    }

    private void postConstruct() {
        SecurityRoleMapperFactoryGen.getSecurityRoleMapperFactory().setAppNameForContext(getAppId(), CONTEXT_ID);
    }

    private String removeSpaces(String withSpaces){
        return withSpaces.replace(' ', '_');
    }
    // fix for CR 6155144
    // used to get the policy context id. Also used by the RealmAdapter
    public static String getContextID(WebBundleDescriptor wbd) {
        return SecurityUtil.getContextID(wbd);
   }

    private void initialise(String appName) throws PolicyContextException {
        getPolicyFactory();
        CODEBASE = removeSpaces(CONTEXT_ID) ;
        //V3:Commented if(VirtualServer.ADMIN_VS.equals(getVirtualServers(appName))){
           if(Constants.ADMIN_VS.equals(getVirtualServers(appName))){
            LoginConfiguration lgConf = wbd.getLoginConfiguration();
            if (lgConf != null){
                String realmName = lgConf.getRealmName();
                SunWebApp sunDes = wbd.getSunDescriptor();
                if(sunDes != null){
                    SecurityRoleMapping[] srms = sunDes.getSecurityRoleMapping();
                    if(srms != null){
                        for (SecurityRoleMapping srm : srms) {
                            String[] principals = srm.getPrincipalName();
                            if (principals != null) {
                                for (String principal : principals) {
                                    wsmf.ADMIN_PRINCIPAL.put(realmName + principal, new PrincipalImpl(principal));
                                }
                            }
                            for (String group : srm.getGroupNames()) {
                                wsmf.ADMIN_GROUP.put(realmName + group, new Group(group));
                            }
                        }
                    }
                    SecurityRoleAssignment[] sras = sunDes.getSecurityRoleAssignments();
                    if(sras != null){
                        for (SecurityRoleAssignment sra : sras) {
                            List principals = sra.getPrincipalNames();
                            if (sra.isExternallyDefined()) {
                                wsmf.ADMIN_GROUP.put(realmName + sra.getRoleName(), new Group(sra.getRoleName()));
                                continue;
                            }
                            for (String principal : principals) {
                                    wsmf.ADMIN_PRINCIPAL.put(realmName + principal, new PrincipalImpl(principal));
                            }

                        }
                    }
                }
            }
        }

        // will require stuff in hash format for reference later on.
        try{
            java.net.URI uri = null;
            try{
		if(logger.isLoggable(Level.FINE))
		    logger.log(Level.FINE, "[Web-Security] Creating a Codebase URI with = {0}", CODEBASE);
		uri = new java.net.URI("file:///"+ CODEBASE);
		if(uri != null){
		    codesource = new CodeSource(new URL(uri.toString()),
                            (java.security.cert.Certificate[]) null);
		}

            } catch(java.net.URISyntaxException use){
                // manually create the URL
                logger.log(Level.FINE, "[Web-Security] Error Creating URI ", use);
                throw new RuntimeException(use);
            }

        } catch(java.net.MalformedURLException mue){
            logger.log(Level.SEVERE, "[Web-Security] Exception while getting the CodeSource", mue);
            throw new RuntimeException(mue);
        }

        if(logger.isLoggable(Level.FINE)){
            logger.log(Level.FINE, "[Web-Security] Context id (id under which  WEB component in application will be created) = {0}", CONTEXT_ID);
            logger.log(Level.FINE, "[Web-Security] Codebase (module id for web component) {0}", CODEBASE);
        }

        loadPolicyConfiguration();

 	if (uncheckedPermissionCache == null) {
            if (register) {
                uncheckedPermissionCache =
                        PermissionCacheFactory.createPermissionCache(this.CONTEXT_ID, codesource, protoPerms, null);

                allResourcesCP =
                        new CachedPermissionImpl(uncheckedPermissionCache,
                        allResources);
                allConnectionsCP =
                        new CachedPermissionImpl(uncheckedPermissionCache,
                        allConnections);
            }
 	} else {
 	    uncheckedPermissionCache.reset();
 	}

    }

     public void loadPolicyConfiguration() throws PolicyContextException {



	boolean inService = getPolicyFactory().inService(CONTEXT_ID);

 	// only regenerate policy file if it isn't already in service
 	// Consequently all things that deploy modules (as apposed to
 	// loading already deployed modules) must make sure pre-exiting
 	// pc is either in deleted or open state before this method
 	// (i.e. initialise) is called. That is, before constructing
 	// the WebSecurityManager. Note that policy statements are not
 	// removed to allow multiple web modules to be represented by same pc.

 	if (!inService) {

 	    pc = getPolicyFactory().getPolicyConfiguration(CONTEXT_ID,false);
	    try{
		WebPermissionUtil.processConstraints(wbd, pc);
		WebPermissionUtil.createWebRoleRefPermission(wbd, pc);
	    } catch (PolicyContextException pce){
		logger.log(Level.FINE,"[Web-Security] FATAL Permission Generation: " + pce.getMessage());
		throw pce;
	    }
	}

    }

    // this will change too - get the application id name
    private String getAppId() {
        return wbd.getApplication().getRegistrationName();
    }

    public boolean permitAll(HttpServletRequest req) {
        boolean ret = false;
        WebResourcePermission webResPerm = createWebResourcePermission(req);
        if (uncheckedPermissionCache != null) {
           ret = uncheckedPermissionCache.checkPermission(webResPerm);
        }
        if (ret == false) {
            ret = checkPermissionWithoutCache(webResPerm, null);
        }
        return ret;
    }

    /*
     * Invoke the Policy to determine if the Permission
     * object has security permission.
     * @param perm an instance of Permission.
     * @param principalSet a set containing the principals to check for authorization
     * @return true if granted, false if denied.
     */
    protected boolean checkPermission(Permission perm, Set principalSet) {
        boolean ret = false;
        if (uncheckedPermissionCache != null) {
            ret = uncheckedPermissionCache.checkPermission(perm);
        }
        if (ret == false) {
            ret = checkPermissionWithoutCache(perm, principalSet);
        } else {
            try {
                setPolicyContext(CONTEXT_ID);
            } catch(Throwable t){
                if (logger.isLoggable(Level.FINE)){
 	            logger.log(Level.FINE,
                        "[Web-Security] Web Permission Access Denied.",t);
                }
 	        ret = false;
            }
        }
        return ret;
    }

    private boolean checkPermissionWithoutCache(
            Permission perm, Set principalSet) {

        try{

 	    // NOTE: there is an assumption here, that this setting of the PC will
 	    // remain in affect through the component dispatch, and that the
 	    // component will not call into any other policy contexts.
	    // even so, could likely reset on failed check.

 	    setPolicyContext(CONTEXT_ID);

 	} catch(Throwable t){
            if (logger.isLoggable(Level.FINE)){
 	        logger.log(Level.FINE,
                    "[Web-Security] Web Permission Access Denied.",t);
            }
 	    return false;
 	}

	ProtectionDomain prdm =
		(ProtectionDomain)protectionDomainCache.get(principalSet);

	if (prdm == null) {

            Principal[] principals = null;
            principals = (principalSet == null ? null :
                      (Principal []) principalSet.toArray(new Principal[0]));

            if(logger.isLoggable(Level.FINE)){
                logger.log(Level.FINE,"[Web-Security] Generating a protection domain for Permission check.");

                if (principals != null) {
                    for (int i=0; i 0) {
                    uri = uri.substring(contextLength);
                }
            }
        }
        if (uri == null) {
            if (logger.isLoggable(Level.FINE)){
                logger.log(Level.FINE,"[Web-Security] mappedUri is null");
            }
            throw new RuntimeException("Fatal Error in creating WebResourcePermission");
        }
        if(uri.equals("/")) {
            uri = EMPTY_STRING;
        }else {
	    // FIX TO BE CONFIRMED: encode all colons
 	    uri = uri.replaceAll(":","%3A");
  	}
        WebResourcePermission perm = new WebResourcePermission(
                uri, httpsr.getMethod());
        return perm;
    }

    /**
     * Perform access control based on the HttpServletRequest.
     * Return true if this constraint is satisfied and processing
     * should continue, or false otherwise.
     * @return true is the resource is granted, false if denied
     */
    public boolean hasResourcePermission(HttpServletRequest httpsr){
	SecurityContext sc = getSecurityContext(httpsr.getUserPrincipal());
        WebResourcePermission perm = createWebResourcePermission(httpsr);
        setSecurityInfo(httpsr);
        boolean isGranted = checkPermission(perm,sc.getPrincipalSet());
	SecurityContext.setCurrent(sc);
        if(logger.isLoggable(Level.FINE)){
            logger.log(Level.FINE, "[Web-Security] hasResource isGranted: {0}", isGranted);
            logger.log(Level.FINE, "[Web-Security] hasResource perm: {0}", perm);
        }
        recordWebInvocation(httpsr, RESOURCE, isGranted);
        return isGranted;
    }


    /*
     * Return true if the specified servletName has the specified
     * security role, within the context of the WebRoleRefPermission;
     * otherwise return
     * false.
     *
     * @param principal servletName the resource's name.
     * @param principal Principal for whom the role is to be checked
     * @param role Security role to be checked
     * @return true is the resource is granted, false if denied
     */
    public boolean hasRoleRefPermission(String servletName, String role, Principal p) {
	Set principalSet = getSecurityContext(p).getPrincipalSet();
        WebRoleRefPermission perm = new WebRoleRefPermission(servletName, role);
        boolean isGranted = checkPermission(perm,principalSet);
        if(logger.isLoggable(Level.FINE)){
            logger.log(Level.FINE, "[Web-Security] hasRoleRef perm: {0}", perm);
            logger.log(Level.FINE, "[Web-Security] hasRoleRef isGranted: {0}", isGranted);
        }
        return isGranted;
    }


    /**
     * if uri == null, determine if the connection characteristics of the
     * request satisfy the applicable policy.
     * If the uri is not null, determine if the uri and Http method
     * require a CONFIDENTIAL transport. The uri value does not include
     * the context path, and any colons occurring in the uri must be escaped.
     *
     * @return 1 if access is permitted (as is or without SSL). -1 if the
     * the access will be permitted after a redirect to SSL. return 0 if
     * access will be denied independent of whether a redirect to SSL is done.
     *
     * Note: this method is not intended to be called if the request is secure.
     * it checks whether the resource can be accessed over the current
     * connection type (which is presumed to be insecure), and if an
     * insecure connection type is not permitted it checks if the resource can
     * be accessed via a confidential transport.
     *
     * If the request is secure, the second check is skipped, and the proper
     * result is returned (but that is not the intended use model).
     */

    public int hasUserDataPermission(HttpServletRequest httpsr,
				     String uri, String httpMethod) {
        setSecurityInfo(httpsr);
	WebUserDataPermission perm;
	boolean requestIsSecure = httpsr.isSecure();
	if (uri == null) {
	    perm = new WebUserDataPermission(httpsr);
	} else {
	    perm = new WebUserDataPermission(uri,
		 httpMethod == null ? null : new String[] { httpMethod },
		 requestIsSecure ? "CONFIDENTIAL": null);
	}

        boolean  isGranted = checkPermission(perm, defaultPrincipalSet);
        int result = 0;

        if ( isGranted ) {
            result = 1;
        }

        if(logger.isLoggable(Level.FINE)){
            logger.log(Level.FINE, "[Web-Security] hasUserDataPermission perm: {0}", perm);
            logger.log(Level.FINE, "[Web-Security] hasUserDataPermission isGranted: {0}", isGranted);
        }

        recordWebInvocation(httpsr, USERDATA, isGranted);
        if ( !isGranted && !requestIsSecure) {

	    if (uri == null) {
		httpMethod = httpsr.getMethod();
	    }

	    perm = new WebUserDataPermission
		(perm.getName(),
		 httpMethod == null ? null : new String[] { httpMethod },
		 "CONFIDENTIAL");

             isGranted = checkPermission(perm, defaultPrincipalSet);

             if (isGranted)
                result = -1;
        }

        return result;
    }

    public void destroy() throws PolicyContextException {
        boolean wasInService = getPolicyFactory().inService(CONTEXT_ID);
        //getPolicyFactory().getPolicyConfiguration(CONTEXT_ID,true);
        if (wasInService) {
            policy.refresh();
        }
        PermissionCacheFactory.removePermissionCache(uncheckedPermissionCache);
        uncheckedPermissionCache = null;
        SecurityRoleMapperFactoryGen.getSecurityRoleMapperFactory().removeAppNameForContext(CONTEXT_ID);
        wsmf.getManager(CONTEXT_ID,null,true);
    }

   /**
    * Analogous to destroy, except does not remove links from Policy Context,
    * and does not remove context_id from role mapper factory. Used to support
    * Policy Changes that occur via ServletContextListener.
    *
    * @throws PolicyContextException
    */
    public void release() throws PolicyContextException {
        boolean wasInService = getPolicyFactory().inService(CONTEXT_ID);
        PolicyConfiguration config =
            getPolicyFactory().getPolicyConfiguration(CONTEXT_ID,false);
	WebPermissionUtil.removePolicyStatements(config,wbd);
        // refresh policy if the context was in service
	if (wasInService) {
            Policy.getPolicy().refresh();
	}
        PermissionCacheFactory.removePermissionCache(uncheckedPermissionCache);
        uncheckedPermissionCache = null;
        wsmf.getManager(CONTEXT_ID,null,true);
    }

    private void recordWebInvocation(final HttpServletRequest httpsr, final String type, final boolean isGranted) {
        AuditManager auditManager = SecurityServicesUtil.getInstance().getAuditManager();
        if (auditManager != null && auditManager.isAuditOn() && (auditManager instanceof AppServerAuditManager)) {
            final AppServerAuditManager appServerAuditManager = (AppServerAuditManager) auditManager;
            Principal prin = httpsr.getUserPrincipal();
            String user = (prin != null) ? prin.getName(): null;
            appServerAuditManager.webInvocation(user, httpsr, type, isGranted);
        }
    }

    private static String setPolicyContext(final String ctxID) throws Throwable {
	String old = PolicyContext.getContextID();
	if (old != ctxID &&
	    (old == null || ctxID == null || !old.equals(ctxID))) {

	    if(logger.isLoggable(Level.FINE)){
		logger.log(Level.FINE, "[Web-Security] Setting Policy Context ID: old = {0} ctxID = {1}", new Object[]{old, ctxID});
	    }

	    try {
		AppservAccessController.doPrivileged(new PrivilegedExceptionAction(){
		    public java.lang.Object run() throws Exception{
			PolicyContext.setContextID(ctxID);
			return null;
		    }
		});
	    } catch (java.security.PrivilegedActionException pae) {
		Throwable cause = pae.getCause();
		if( cause instanceof java.security.AccessControlException) {
		    logger.log(Level.SEVERE,"[Web-Security] setPolicy SecurityPermission required to call PolicyContext.setContextID",cause);
		} else {
		    logger.log(Level.SEVERE,"[Web-Security] Unexpected Exception while setting policy context",cause);
		}
		throw cause;
	    }
	} else if(logger.isLoggable(Level.FINE)){
	    logger.log(Level.FINE, "[Web-Security] Policy Context ID was: {0}", old);
	}
	return old;
    }

    /**
     * This is an private method for transforming principal into a SecurityContext
     * @param principal expected to be a WebPrincipal
     * @return SecurityContext
     */
    private SecurityContext getSecurityContext(Principal principal) {
        SecurityContext secContext = null;
        if (principal != null) {
	    if (principal instanceof WebPrincipal){
		WebPrincipal wp = (WebPrincipal)principal;
		secContext = wp.getSecurityContext();
	    }else {
                secContext = SecurityContext.getCurrent();
	    }
        }
	if (secContext == null) {
            secContext = SecurityContext.getDefaultSecurityContext();
        }
	return secContext;
    }

    /**
     * This is an private method for policy context handler data info
     * @param httpRequest
     */
    private void setSecurityInfo(HttpServletRequest httpRequest) {
        if (httpRequest != null) {
            wsmf.pcHandlerImpl.getHandlerData().setHttpServletRequest(httpRequest);
        }
    }

    private String principalSetToString(Set principalSet) {
 	StringBuilder result = null;
 	if (principalSet != null) {
 	    Principal[] principals =
 		(Principal []) principalSet.toArray(new Principal[0]);
 	    for (int i =0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy