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

com.sun.web.security.RealmAdapter Maven / Gradle / Ivy

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2016 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-2017] [Payara Foundation and/or its affiliates]

package com.sun.web.security;

import static com.sun.enterprise.security.auth.digest.api.Constants.A1;
import static com.sun.enterprise.security.jmac.config.HttpServletConstants.AUTH_TYPE;
import static com.sun.enterprise.security.jmac.config.HttpServletConstants.IS_MANDATORY;
import static com.sun.enterprise.security.jmac.config.HttpServletConstants.REGISTER_SESSION;
import static com.sun.enterprise.security.jmac.config.HttpServletConstants.REGISTER_WITH_AUTHENTICATOR;
import static java.lang.Boolean.TRUE;
import static java.net.URLEncoder.encode;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.SEVERE;
import static java.util.logging.Level.WARNING;
import static javax.security.auth.message.AuthStatus.SUCCESS;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
import static org.apache.catalina.ContainerEvent.AFTER_AUTHENTICATION;
import static org.apache.catalina.ContainerEvent.BEFORE_AUTHENTICATION;
import static org.apache.catalina.Globals.WRAPPED_REQUEST;
import static org.apache.catalina.Globals.WRAPPED_RESPONSE;
import static org.glassfish.api.admin.ServerEnvironment.DEFAULT_INSTANCE_NAME;

import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
/*V3:Comment
import com.sun.enterprise.webservice.monitoring.WebServiceEngineImpl;
import com.sun.enterprise.webservice.monitoring.AuthenticationListener;
 */
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.security.auth.Subject;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.config.ServerAuthConfig;
import javax.security.auth.message.config.ServerAuthContext;
import javax.security.jacc.PolicyContext;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.Authenticator;
import org.apache.catalina.Container;
import org.apache.catalina.ContainerEvent;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.HttpRequest;
import org.apache.catalina.HttpResponse;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Realm;
import org.apache.catalina.authenticator.AuthenticatorBase;
import org.apache.catalina.connector.RequestFacade;
import org.apache.catalina.deploy.LoginConfig;
import org.apache.catalina.deploy.SecurityConstraint;
import org.apache.catalina.realm.Constants;
import org.apache.catalina.realm.RealmBase;
import org.glassfish.api.invocation.ComponentInvocation;
import org.glassfish.grizzly.config.dom.NetworkConfig;
import org.glassfish.grizzly.config.dom.NetworkListener;
import org.glassfish.grizzly.config.dom.NetworkListeners;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.PostConstruct;
import org.glassfish.internal.api.ServerContext;
import org.glassfish.security.common.CNonceCache;
import org.glassfish.security.common.NonceInfo;
import org.jvnet.hk2.annotations.Service;

//import com.sun.enterprise.Switch;
import com.sun.enterprise.deployment.Application;
import com.sun.enterprise.deployment.RunAsIdentityDescriptor;
import com.sun.enterprise.deployment.WebBundleDescriptor;
import com.sun.enterprise.deployment.WebComponentDescriptor;
//import org.glassfish.deployment.common.SecurityRoleMapper;
import com.sun.enterprise.deployment.web.LoginConfiguration;
import com.sun.enterprise.security.AppCNonceCacheMap;
import com.sun.enterprise.security.CNonceCacheFactory;
import com.sun.enterprise.security.SecurityContext;
import com.sun.enterprise.security.WebSecurityDeployerProbeProvider;
import com.sun.enterprise.security.auth.digest.api.DigestAlgorithmParameter;
import com.sun.enterprise.security.auth.digest.api.Key;
import com.sun.enterprise.security.auth.digest.impl.DigestParameterGenerator;
import com.sun.enterprise.security.auth.digest.impl.HttpAlgorithmParameterImpl;
import com.sun.enterprise.security.auth.digest.impl.NestedDigestAlgoParamImpl;
import com.sun.enterprise.security.auth.login.DigestCredentials;
import com.sun.enterprise.security.auth.login.LoginContextDriver;
import com.sun.enterprise.security.auth.realm.certificate.CertificateRealm;
import com.sun.enterprise.security.authorize.PolicyContextHandlerImpl;
import com.sun.enterprise.security.ee.SecurityUtil;
import com.sun.enterprise.security.integration.RealmInitializer;
import com.sun.enterprise.security.jmac.config.HttpServletConstants;
import com.sun.enterprise.security.jmac.config.HttpServletHelper;
import com.sun.enterprise.security.web.integration.WebPrincipal;
import com.sun.enterprise.security.web.integration.WebSecurityManager;
import com.sun.enterprise.security.web.integration.WebSecurityManagerFactory;
import com.sun.enterprise.util.net.NetUtils;
import com.sun.logging.LogDomains;

import fish.payara.nucleus.requesttracing.RequestTracingService;
import fish.payara.nucleus.requesttracing.domain.RequestEvent;
import sun.security.x509.X500Name;

/**
 * This is the realm adapter used to authenticate users and authorize
 * access to web resources. The authenticate method is called by Tomcat
 * to authenticate users. The hasRole method is called by Tomcat during
 * the authorization process.
 * @author Harpreet Singh
 * @author JeanFrancois Arcand
 */
@Service
@PerLookup
public class RealmAdapter extends RealmBase implements RealmInitializer, PostConstruct {

    private static final Logger _logger = LogDomains.getLogger(RealmAdapter.class, LogDomains.WEB_LOGGER);
    private static final ResourceBundle rb = _logger.getResourceBundle();
    public static final String SECURITY_CONTEXT = "SecurityContext";
    public static final String BASIC = "BASIC";
    public static final String FORM = "FORM";
    private static final String SERVER_AUTH_CONTEXT = "__javax.security.auth.message.ServerAuthContext";
    private static final String MESSAGE_INFO = "__javax.security.auth.message.MessageInfo";
    private static final WebSecurityDeployerProbeProvider websecurityProbeProvider = new WebSecurityDeployerProbeProvider();

    // name of system property that can be used to define 
    // corresponding default provider for system apps.
    private static final String SYSTEM_HTTPSERVLET_SECURITY_PROVIDER = "system_httpservlet_security_provider";

    private WebBundleDescriptor webDesc;

    // BEGIN IASRI 4747594
    private HashMap runAsPrincipals;
    // END IASRI 4747594
    
    // required for realm-per-app login
    private String _realmName;
    
    /**
     * Descriptive information about this Realm implementation.
     */
    protected static final String name = "J2EE-RI-RealmAdapter";
    
    /**
     * The context Id value needed by the jacc architecture.
     */
    private String CONTEXT_ID;
    private Container virtualServer;

    /**
     * A WebSecurityManager object associated with a CONTEXT_ID
     */
    protected volatile WebSecurityManager webSecurityManager;
            
    protected boolean isCurrentURIincluded;
    
    /* the following fields are used to implement a bypass of
     * FBL related targets
     */
    protected final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private boolean contextEvaluated;
    private String loginPage;
    private String errorPage;
    private final static SecurityConstraint[] emptyConstraints = new SecurityConstraint[]{};
    
    /**
     * the default provider id for system apps if one has been established.
     * the default provider for system apps is established by defining
     * a system property.
     */
    private static String defaultSystemProviderID = getDefaultSystemProviderID();
    
    private String moduleID;
    private boolean isSystemApp;
    private HttpServletHelper helper;

    @Inject
    private ServerContext serverContext;

    @Inject
    private Provider appCNonceCacheMapProvider;

    @Inject
    private Provider cNonceCacheFactoryProvider;

    @Inject
    @Named(DEFAULT_INSTANCE_NAME)
    private NetworkConfig networkConfig;
    
    /**
     * The factory used for creating WebSecurityManager object.
     */
    @Inject
    protected WebSecurityManagerFactory webSecurityManagerFactory;
    
    @Inject
    RequestTracingService requestTracing;


    private CNonceCacheFactory cNonceCacheFactory;
    private CNonceCache cnonces;
    private AppCNonceCacheMap haCNonceCacheMap;
   
    private NetworkListeners nwListeners;
    
/**
     * ThreadLocal object to keep track of the reentrancy status of each thread.
     * It contains a byte[] object whose single element is either 0 (initial
     * value or no reentrancy), or 1 (current thread is reentrant). When a
     * thread exits the implies method, byte[0] is always reset to 0.
     */
    private static ThreadLocal reentrancyStatus;

    static {
        reentrancyStatus = new ThreadLocal() {

            @Override
            protected synchronized byte[] initialValue() {
                return new byte[]{0};
            }
        };
    }
    
    public RealmAdapter() {
        // used during Injection in WebContainer (glue code)
    }

    /**
     * Create for WS Ejb endpoint authentication.
     * Roles related data is not available here.
     */
     
    public RealmAdapter(String realmName, String moduleID) {
        _realmName = realmName;
        this.moduleID = moduleID;
    }

    @Override
    public void destroy() {
        super.destroy();
        if (helper != null) {
            helper.disable();
        }
    }

    /**
     * Sets the virtual server on which the web module (with which this
     * RealmAdapter is associated with) has been deployed.
     *
     * @param container The virtual server
     */
    @Override
    public void setVirtualServer(Object container) {
        this.virtualServer = (Container)container;
    }

    public WebBundleDescriptor getWebDescriptor() {
        return webDesc;
    }

    // utility method to get web security anager.
    // will log warning if the manager is not found in the factory, and
    // logNull is true.
    public WebSecurityManager getWebSecurityManager(boolean logNull) {
        if (webSecurityManager == null) {
            synchronized (this) {
                webSecurityManager = webSecurityManagerFactory.getManager(CONTEXT_ID,null, false);
            }
            if (webSecurityManager == null && logNull) {
                _logger.log(Level.WARNING, "realmAdapter.noWebSecMgr",
                            CONTEXT_ID);
            }
        }

        return webSecurityManager;
    }

    public void updateWebSecurityManager() {
        if (webSecurityManager == null) {
            webSecurityManager = getWebSecurityManager(true);
        }
        if (webSecurityManager != null) {
            try {
                webSecurityManager.release();
                webSecurityManager.destroy();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            webSecurityManager = webSecurityManagerFactory.createManager(webDesc, true, serverContext);
            if (_logger.isLoggable(Level.FINE)) {
                _logger.fine("WebSecurityManager for "+CONTEXT_ID+" has been update");
            }
        }
    }

    /**
     * Check if the given principal has the provided role. Returns
     * true if the principal has the specified role, false otherwise.
     * @param principal the principal
     * @param role the role
     * @return true if the principal has the specified role.
     * @param request Request we are processing
     * @param response Response we are creating
     */
    //START OF SJSAS 6232464 
    //public boolean hasRole(Principal principal, String role) {
    @Override
    public boolean hasRole(HttpRequest request,
            HttpResponse response,
            Principal principal,
            String role) {
        WebSecurityManager secMgr = getWebSecurityManager(true);
        if (secMgr == null) {
            return false;
        }

        //add HttpResponse and HttpResponse to the parameters, and remove
        //instance variable currentRequest from this class. References to
        //this.currentRequest are also removed from other methods.
        //String servletName = getResourceName( currentRequest.getRequestURI(),
        //                                      currentRequest.getContextPath());
        String servletName = getCanonicalName(request);

        // END S1AS8PE 4966609
        boolean isGranted = secMgr.hasRoleRefPermission(servletName, role, principal);

        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine("Checking if servlet " + servletName + " with principal " + principal + " has role " + role + " isGranted: " + isGranted);
        }

        return isGranted;

    }

    public boolean hasRole(String servletName, Principal principal, String role) {
        WebSecurityManager secMgr = getWebSecurityManager(true);
	if (secMgr == null) {
	    return false;
	}
        return secMgr.hasRoleRefPermission(servletName, role, principal);
    }

    @Override
    public void logout(final HttpRequest req) {
        boolean securityExtensionEnabled = isSecurityExtensionEnabled(req.getRequest().getServletContext());
        byte[] alreadyCalled = (byte[]) reentrancyStatus.get();
        if (securityExtensionEnabled && helper != null && alreadyCalled[0] == 0) {
            alreadyCalled[0] = 1;
            MessageInfo messageInfo = (MessageInfo) req.getRequest().getAttribute(MESSAGE_INFO);
            if (messageInfo == null) {
                messageInfo = new HttpMessageInfo((HttpServletRequest) req.getRequest(),
                        (HttpServletResponse) req.getResponse().getResponse());
            }
            messageInfo.getMap().put(HttpServletConstants.IS_MANDATORY,
                        Boolean.TRUE.toString());
            try {
                ServerAuthContext sAC = helper.getServerAuthContext(messageInfo,null);
                if (sAC != null) {
                    /*
                     * Check for the default/server-generated/unauthenticated
                     * security context.
                     */
                    final SecurityContext securityContext = SecurityContext.getCurrent();
                    Subject subject = securityContext.didServerGenerateCredentials() ?
                            new Subject() : securityContext.getSubject();
                    
                    if (subject == null) {
                        subject = new Subject();
                    }
                    if (subject.isReadOnly()) {
                        _logger.log(Level.WARNING, "Read-only subject found during logout processing");
                    }
                    try {
                        req.getContext().fireContainerEvent(ContainerEvent.BEFORE_LOGOUT, null);
                        sAC.cleanSubject(messageInfo, subject);
                    }finally {
                        req.getContext().fireContainerEvent(ContainerEvent.AFTER_LOGOUT, null);
                    }
                }
            } catch (AuthException ex) {
                throw new RuntimeException(ex);
            } finally {
                doLogout(req, true);
                alreadyCalled[0] = 0;
            }
        } else {
            doLogout(req, alreadyCalled[0] == 1);
        }
    }

    private void doLogout(HttpRequest request, boolean extensionEnabled) {
        Context context = request.getContext();
        Authenticator authenticator = null;
        if (context != null) {
            authenticator = context.getAuthenticator();
        }
        if (authenticator == null) {
            throw new RuntimeException("Context or Authenticator is null");
        }
        try {
            if (extensionEnabled) {
                AuthenticatorProxy proxy = new AuthenticatorProxy(authenticator, null, null);
                proxy.logout(request);
            } else {
                authenticator.logout(request);
            }
        } catch(Exception ex) {
            throw new RuntimeException(ex);
        }

        logout();
    }

    @Override
    public void logout() {
        setSecurityContext(null);
        AccessController.doPrivileged(new PrivilegedAction() {
            @Override
            public Void run() {
                resetPolicyContext();
                return null;
            }
        });
    }

    @Override
    public Principal authenticate(HttpServletRequest hreq) {
        try {
            DigestParameterGenerator generator = DigestParameterGenerator.getInstance(DigestParameterGenerator.HTTP_DIGEST);
            DigestAlgorithmParameter[] params = generator.generateParameters(new HttpAlgorithmParameterImpl(hreq));
            Key key = null;

            if (cnonces == null) {
                String appName = webDesc.getApplication().getAppName();
                synchronized (this) {
                    if (this.haCNonceCacheMap == null) {
                        this.haCNonceCacheMap = appCNonceCacheMapProvider.get();
                    }
                    if (this.haCNonceCacheMap != null) {
                        //get the initialized HA CNonceCache
                        cnonces = haCNonceCacheMap.get(appName);
                    }

                    if (cnonces == null) {
                        if (this.cNonceCacheFactory == null) {
                            this.cNonceCacheFactory = cNonceCacheFactoryProvider.get();
                        }
                        //create a Non-HA CNonce Cache
                        cnonces =
                                cNonceCacheFactory.createCNonceCache(
                                webDesc.getApplication().getAppName(), null, null, null);
                    }
                }

            }

            String nc = null;
            String cnonce = null;
            for (DigestAlgorithmParameter p : params) {
                if (p instanceof NestedDigestAlgoParamImpl) {
                    NestedDigestAlgoParamImpl np = (NestedDigestAlgoParamImpl) p;
                    DigestAlgorithmParameter[] nps = (DigestAlgorithmParameter[]) np.getNestedParams();
                    for (DigestAlgorithmParameter p1 : nps) {
                        if ("cnonce".equals(p1.getName())) {
                            cnonce = new String(p1.getValue());
                        } else if ("nc".equals(p1.getName())) {
                            nc = new String(p1.getValue());
                        }
                        if (cnonce != null && nc != null) {
                            break;
                        }
                    }
                    if (cnonce != null && nc != null) {
                        break;
                    }
                }
                if ("cnonce".equals(p.getName())) {
                    cnonce = new String(p.getValue());
                } else if ("nc".equals(p.getName())) {
                    nc = new String(p.getValue());
                }
            }
            
            long count;
            long currentTime = System.currentTimeMillis();
            try {
                count = Long.parseLong(nc, 16);
            } catch (NumberFormatException nfe) {
                throw new RuntimeException(nfe);
            }
            NonceInfo info;
            synchronized (cnonces) {
                info = cnonces.get(cnonce);
            }
            if (info == null) {
                info = new NonceInfo();
            } else {
                if (count <= info.getCount()) {
                    throw new RuntimeException("Invalid Request : Possible Replay Attack detected ?");
                }
            }
            info.setCount(count);
            info.setTimestamp(currentTime);
            synchronized (cnonces) {
                cnonces.put(cnonce, info);
            }

            for (int i = 0; i < params.length; i++) {
                DigestAlgorithmParameter dap = params[i];
                if (A1.equals(dap.getName()) && (dap instanceof Key)) {
                    key = (Key) dap;
                    break;
                }
            }

           if (key != null) {
               DigestCredentials creds = new DigestCredentials(_realmName,key.getUsername(), params);     
               LoginContextDriver.login(creds);
               SecurityContext secCtx = SecurityContext.getCurrent();
               return new WebPrincipal(creds.getUserName(),(char[])null, secCtx);
           } else {
               throw new RuntimeException("No key found in parameters");
           }

       } catch (Exception le) {
           if (_logger.isLoggable(Level.WARNING)) {
               _logger.log(Level.WARNING,"web.login.failed", le.toString());
            }
       }
        return null;
    }

    /**
     * Authenticates and sets the SecurityContext in the TLS.
     * @return the authenticated principal.
     * @param username the user name.
     * @param password the password.
     */
    @Override
    public Principal authenticate(String username, char[] password) {
        
        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine("Tomcat callback for authenticate user/password");
            _logger.fine("usename = " + username);
        }
        if (authenticate(username, password, null)) {
            SecurityContext secCtx = SecurityContext.getCurrent();
            assert (secCtx != null); // or auth should've failed
            return new WebPrincipal(username, password, secCtx);
            
        } else {
            return null;
        }
    }

    @Override
    public Principal authenticate(X509Certificate certs[]) {
        if (authenticate(null, null, certs)) {
            SecurityContext secCtx = SecurityContext.getCurrent();
            assert (secCtx != null); // or auth should've failed
            return new WebPrincipal(certs, secCtx);
        } else {
            return null;
        }
    }

    /* IASRI 4688449
    This method was only used by J2EEInstanceListener to set the security
    context prior to invocations by re-authenticating a previously set
    WebPrincipal. This is now cached so no need.
     */
    public boolean authenticate(WebPrincipal prin) {
        if (prin.isUsingCertificate()) {
            return authenticate(null, null, prin.getCertificates());
        } else {
            return authenticate(prin.getName(), prin.getPassword(), null);
        }
    }

    /**
     * Authenticates and sets the SecurityContext in the TLS.
     * @return true if authentication succeeded, false otherwise.
     * @param username the username .
     * @param password the password.
     * @param certs Certificate Array.
     */
    protected boolean authenticate(String username, char[] password,
            X509Certificate[] certs) {

        String realm_name = null;
        boolean success = false;
        try {
            if (certs != null) {
                Subject subject = new Subject();
                X509Certificate certificate = certs[0];
                X500Name x500Name = (X500Name) certificate.getSubjectDN();
                subject.getPublicCredentials().add(x500Name);
                // Put the certificate chain as an List in the subject, to be accessed by user's LoginModule.
                final List certificateCred = Arrays.asList(certs);
                subject.getPublicCredentials().add(certificateCred);
                LoginContextDriver.doX500Login(subject, moduleID);
                realm_name = CertificateRealm.AUTH_TYPE;
            } else {
                realm_name = _realmName;
               
                LoginContextDriver.login(username, password, realm_name);
            }
            success = true;
        } catch (Exception le) {
            success = false;
            if (_logger.isLoggable(Level.WARNING)) {
                _logger.log(Level.WARNING, "web.login.failed", le.toString());
                if (_logger.isLoggable(Level.FINE)) {
                    _logger.log(Level.FINE, "Exception", le);
                }
            }
        }
        if (success) {
            if (_logger.isLoggable(Level.FINE)) {
                _logger.log(Level.FINE, "Web login succeeded for: " + username);
            }
        }
        
        return success;
    }

    // BEGIN IASRI 4747594
    /**
     * Set the run-as principal into the SecurityContext when needed.
     *
     * 

This method will attempt to obtain the name of the servlet from * the ComponentInvocation. Note that there may not be one since this * gets called also during internal processing (not clear..) not just * part of servlet requests. However, if it is not a servlet request * there is no need (or possibility) to have a run-as setting so no * further action is taken. * *

If the servlet name is present the runAsPrincipals cache is * checked to find the run-as principal to use (if any). If one is set, * the SecurityContext is switched to this principal. * * @param inv The invocation object to process. * */ public void preSetRunAsIdentity(ComponentInvocation inv) { //Optimization to avoid the expensivce call to getServletName //for cases with no run-as descriptors if(runAsPrincipals != null && runAsPrincipals.isEmpty()) { return; } String servletName = this.getServletName(inv); if (servletName == null) { return; } String runAs = runAsPrincipals.get(servletName); if (runAs != null) { // The existing SecurityContext is saved - however, this seems // meaningless - see bug 4757733. For now, keep it unchanged // in case there are some dependencies elsewhere in RI. SecurityContext old = getSecurityContext(); inv.setOldSecurityContext(old); // Set the run-as principal into SecurityContext loginForRunAs(runAs); if (_logger.isLoggable(Level.FINE)) { _logger.fine("run-as principal for " + servletName + " set to: " + runAs); } } } /** * Obtain servlet name from invocation. * *

In order to obtain the servlet name one of the following must be true: * 1. The instanceName of the ComponentInvocation is not null * 2. The ComponentInvocation contains a 'class' of type HttpServlet, which * contains a valid ServletConfig object. This method returns the value returned * by getServletName() on the ServletConfig. * *

If the above is not met, null is returned. * * @param inv The invocation object to process. * @return Servlet name or null. * */ private String getServletName(ComponentInvocation inv) { String servletName = inv.getInstanceName(); if (servletName != null) { return servletName; } Object invInstance = inv.getInstance(); if (invInstance instanceof HttpServlet) { HttpServlet thisServlet = (HttpServlet) invInstance; ServletConfig svc = thisServlet.getServletConfig(); if (svc != null) { return thisServlet.getServletName(); } } return null; } /** * Attempts to restore old SecurityContext (but fails). * *

In theory this method seems to attempt to check if a run-as * principal was set by preSetRunAsIdentity() (based on the indirect * assumption that if the servlet in the given invocation has a run-as * this must've been the case). If so, it retrieves the oldSecurityContext * from the invocation object and set it in the SecurityContext. * *

The problem is that the invocation object is not the same object * as was passed in to preSetRunAsIdentity() so it will never contain * the right info - see bug 4757733. * *

In practice it means this method only ever sets the * SecurityContext to null (if run-as matched) or does nothing. In * particular note the implication that it will be set to * null after a run-as invocation completes. This behavior will be * retained for the time being for consistency with RI. It must be fixed * later. * * @param inv The invocation object to process. * */ public void postSetRunAsIdentity(ComponentInvocation inv) { //Optimization to avoid the expensivce call to getServletName //for cases with no run-as descriptors if(runAsPrincipals != null && runAsPrincipals.isEmpty()) { return; } String servletName = this.getServletName(inv); if (servletName == null) { return; } String runAs = runAsPrincipals.get(servletName); if (runAs != null) { setSecurityContext((SecurityContext) inv.getOldSecurityContext()); // always null } } // END IASRI 4747594 private void loginForRunAs(String principal) { LoginContextDriver.loginPrincipal(principal, _realmName); } private SecurityContext getSecurityContext() { return SecurityContext.getCurrent(); } private void setSecurityContext(SecurityContext sc) { SecurityContext.setCurrent(sc); } /** * Used to detect when the principals in the subject correspond to the * default or "ANONYMOUS" principal, and therefore a null principal * should be set in the HttpServletRequest. * @param principalSet * @return true whe a null principal is to be set. */ private boolean principalSetContainsOnlyAnonymousPrincipal(Set principalSet) { boolean rvalue = false; Principal defaultPrincipal = SecurityContext.getDefaultCallerPrincipal(); if (defaultPrincipal != null && principalSet != null) { rvalue = principalSet.contains(defaultPrincipal); } if (rvalue) { Iterator it = principalSet.iterator(); while (it.hasNext()) { if (!it.next().equals(defaultPrincipal)) { return false; } } } return rvalue; } @Override protected char[] getPassword(String username) { throw new IllegalStateException("Should not reach here"); } @Override protected Principal getPrincipal(String username) { throw new IllegalStateException("Should not reach here"); } //START OF IASRI 4809144 /** * This method is added to create a Principal based on the username only. * Hercules stores the username as part of authentication failover and * needs to create a Principal based on username only * @param username * @return Principal for the user username * HERCULES:add */ public Principal createFailOveredPrincipal(String username) { _logger.log(Level.FINEST, "IN createFailOveredPrincipal ({0})", username); //set the appropriate security context loginForRunAs(username); SecurityContext secCtx = SecurityContext.getCurrent(); _logger.log(Level.FINE, "Security context is {0}", secCtx); assert (secCtx != null); Principal principal = new WebPrincipal(username, (char[])null, secCtx); _logger.log(Level.INFO, "Principal created for FailOvered user {0}", principal); return principal; } //END OF IASRI 4809144 /** * Perform access control based on the specified authorization constraint. * Return true if this constraint is satisfied and processing * should continue, or false otherwise. * * @param request Request we are processing * @param response Response we are creating * @param constraint Security constraint we are enforcing * @param The Context to which client of this class is attached. * * @exception IOException if an input/output error occurs */ public boolean hasResourcePermission(HttpRequest request, HttpResponse response, SecurityConstraint[] constraints, Context context) throws IOException { boolean isGranted = false; try { isGranted = invokeWebSecurityManager( request, response, constraints); } catch (IOException iex) { throw iex; } catch (Throwable ex) { _logger.log(Level.SEVERE,"web_server.excep_authenticate_realmadapter", ex); ((HttpServletResponse) response.getResponse()).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); response.setDetailMessage(rb.getString("realmBase.forbidden")); return isGranted; } if (isGranted) { return isGranted; } else { ((HttpServletResponse) response.getResponse()).sendError(HttpServletResponse.SC_FORBIDDEN); response.setDetailMessage(rb.getString("realmBase.forbidden")); // invoking secureResponse invokePostAuthenticateDelegate(request, response, context); return isGranted; } } /** * Invokes WebSecurityManager to perform access control check. * Return true if permission is granted, or false * otherwise. * * @param request Request we are processing * @param response Response we are creating * @param constraints Security constraint we are enforcing * * @exception IOException if an input/output error occurs */ private boolean invokeWebSecurityManager(HttpRequest request, HttpResponse response, SecurityConstraint[] constraints) throws IOException { // allow access to form login related pages and targets // and the "j_security_check" action boolean evaluated = false; try { rwLock.readLock().lock(); evaluated = contextEvaluated; } finally { rwLock.readLock().unlock(); } if (!evaluated) { try { rwLock.writeLock().lock(); if (!contextEvaluated) { // get Context here as preAuthenticateCheck does not have it // and our Container is always a Context Context context = (Context) getContainer(); LoginConfig config = context.getLoginConfig(); if ((config != null) && (Constants.FORM_METHOD.equals(config.getAuthMethod()))) { loginPage = config.getLoginPage(); errorPage = config.getErrorPage(); } contextEvaluated = true; } } finally { rwLock.writeLock().unlock(); } } if (loginPage != null || errorPage != null) { String requestURI = request.getRequestPathMB().toString(); if (_logger.isLoggable(Level.FINE)) { _logger.fine("[Web-Security] requestURI: " + requestURI + " loginPage: " + loginPage); } if (loginPage != null && loginPage.equals(requestURI)) { if (_logger.isLoggable(Level.FINE)) { _logger.fine(" Allow access to login page " + loginPage); } return true; } else if (errorPage != null && errorPage.equals(requestURI)) { if (_logger.isLoggable(Level.FINE)) { _logger.fine(" Allow access to error page " + errorPage); } return true; } else if (requestURI.endsWith(Constants.FORM_ACTION)) { if (_logger.isLoggable(Level.FINE)) { _logger.fine(" Allow access to username/password submission"); } return true; } } HttpServletRequest hrequest = (HttpServletRequest) request; if (hrequest.getServletPath() == null) { request.setServletPath(getResourceName(hrequest.getRequestURI(), hrequest.getContextPath())); } if (_logger.isLoggable(Level.FINE)) { _logger.fine("[Web-Security] [ hasResourcePermission ] Principal: " + hrequest.getUserPrincipal() + " ContextPath: " + hrequest.getContextPath()); } WebSecurityManager secMgr = getWebSecurityManager(true); if (secMgr == null) { return false; } return secMgr.hasResourcePermission(hrequest); } /** * Enforce any user data constraint required by the security constraint * guarding this request URI. * * @param request Request we are processing * @param response Response we are creating * @param constraints Security constraint being checked * * @exception IOException if an input/output error occurs * * @return true if this constraint was not violated and * processing should continue, or false if we have created * a response already */ public boolean hasUserDataPermission(HttpRequest request, HttpResponse response, SecurityConstraint[] constraints) throws IOException { return hasUserDataPermission(request, response, constraints, null, null); } /** * Checks if the given request URI and method are the target of any * user-data-constraint with a transport-guarantee of CONFIDENTIAL, * and whether any such constraint is already satisfied. * * If uri and method are null, then the URI and method * of the given request are checked. * * If a user-data-constraint exists that is not satisfied, then the * given request will be redirected to HTTPS. * * @param request the request that may be redirected * @param response the response that may be redirected * @param constraints the security constraints to check against * @param uri the request URI (minus the context path) to check * @param method the request method to check * * @return true if the request URI and method are not the target of any * unsatisfied user-data-constraint with a transport-guarantee of * CONFIDENTIAL, and false if they are (in which case the given request * will have been redirected to HTTPS) */ public boolean hasUserDataPermission(HttpRequest request, HttpResponse response, SecurityConstraint[] constraints, String uri, String method) throws IOException { HttpServletRequest hrequest = (HttpServletRequest) request; if (hrequest.getServletPath() == null) { request.setServletPath( getResourceName(hrequest.getRequestURI(), hrequest.getContextPath())); } if (_logger.isLoggable(FINE)) { _logger.fine("[Web-Security][ hasUserDataPermission ] Principal: " + hrequest.getUserPrincipal() + " ContextPath: " + hrequest.getContextPath()); } if (request.getRequest().isSecure()) { if (_logger.isLoggable(FINE)) { _logger.fine("[Web-Security] request.getRequest().isSecure(): " + request.getRequest().isSecure()); } return true; } WebSecurityManager secMgr = getWebSecurityManager(true); if (secMgr == null) { return false; } int isGranted = 0; try { isGranted = secMgr.hasUserDataPermission(hrequest,uri,method); } catch (IllegalArgumentException e) { // End the request after getting IllegalArgumentException while checking user data permission String msgWithId = rb.getString("realmAdapter.badRequestWithId"); _logger.log(WARNING, msgWithId, e); String msg = rb.getString("realmAdapter.badRequest"); ((HttpServletResponse) response.getResponse()).sendError(SC_BAD_REQUEST, msg); return false; } // Only redirect if we are sure the user will be granted. // See bug 4947698 // This method will return: // 1 - if granted // 0 - if not granted // -1 - if the current transport is not granted, but a redirection can occur // so the grand will succeed. if (isGranted == -1) { if (_logger.isLoggable(FINE)) { _logger.fine("[Web-Security] redirecting using SSL"); } return redirect(request, response); } if (isGranted == 0) { ((HttpServletResponse) response.getResponse()).sendError(SC_FORBIDDEN, rb.getString("realmBase.forbidden")); return false; } return true; } private List getHostAndPort(HttpRequest request) throws IOException { boolean isWebServerRequest = false; Enumeration headerNames = ((HttpServletRequest) request.getRequest()).getHeaderNames(); String[] hostPort = null; boolean isHeaderPresent = false; while (headerNames.hasMoreElements()) { String headerName = (String) headerNames.nextElement(); String hostVal; if (headerName.equalsIgnoreCase("Host")) { hostVal = ((HttpServletRequest) request.getRequest()).getHeader(headerName); isHeaderPresent = true; hostPort = hostVal.split(":"); } } if (hostPort == null) { throw new ProtocolException(rb.getString("missing_http_header.host")); } //If the port in the Header is empty (it refers to the default port), which is //not one of the GlassFish listener ports -> GF is front-ended by a proxy (LB plugin) boolean isHostPortNullOrEmpty = ( (hostPort.length <= 1) || (hostPort[1] == null || hostPort[1].trim().isEmpty())); if (!isHeaderPresent) { isWebServerRequest = false; } else if (isHostPortNullOrEmpty) { isWebServerRequest = true; } else { boolean breakFromLoop = false; for (NetworkListener nwListener : nwListeners.getNetworkListener()) { //Loop through the network listeners String nwAddress = nwListener.getAddress(); InetAddress[] localHostAdresses; if (nwAddress == null || nwAddress.equals("0.0.0.0")) { nwAddress = NetUtils.getCanonicalHostName(); if (!nwAddress.equals(hostPort[0])) { // compare the InetAddress objects //only if the hostname in the header //does not match with the hostname in the //listener-To avoid performance overhead localHostAdresses = NetUtils.getHostAddresses(); InetAddress hostAddress = InetAddress.getByName(hostPort[0]); for (InetAddress inetAdress : localHostAdresses) { if (inetAdress.equals(hostAddress)) { //Hostname of the request in the listener and the hostname in the Host header match. //Check the port String nwPort = nwListener.getPort(); //If the listener port is different from the port //in the Host header, then request is received by WS frontend if (!nwPort.equals(hostPort[1])) { isWebServerRequest = true; } else { isWebServerRequest = false; breakFromLoop = true; break; } } } } else { //Host names are the same, compare the ports String nwPort = nwListener.getPort(); //If the listener port is different from the port //in the Host header, then request is received by WS frontend if (!nwPort.equals(hostPort[1])) { isWebServerRequest = true; } else { isWebServerRequest = false; breakFromLoop = true; } } } if (breakFromLoop && !isWebServerRequest) { break; } } } String serverHost = request.getRequest().getServerName(); int redirectPort = request.getConnector().getRedirectPort(); //If the request is a from a webserver frontend, redirect to the url //with the webserver frontend host and port if (isWebServerRequest) { serverHost = hostPort[0]; if (isHostPortNullOrEmpty) { //Use the default port redirectPort = -1; } else { redirectPort = Integer.parseInt(hostPort[1]); } } List hostAndPort = new ArrayList(); hostAndPort.add(serverHost); hostAndPort.add(String.valueOf(redirectPort)); return hostAndPort; } private boolean redirect(HttpRequest request, HttpResponse response) throws IOException { // Initialize variables we need to determine the appropriate action HttpServletRequest hrequest = (HttpServletRequest) request.getRequest(); HttpServletResponse hresponse = (HttpServletResponse) response.getResponse(); int redirectPort = request.getConnector().getRedirectPort(); // Is redirecting disabled? if (redirectPort <= 0) { if (_logger.isLoggable(INFO)) { _logger.fine("[Web-Security] SSL redirect is disabled"); } hresponse.sendError(SC_FORBIDDEN, encode(hrequest.getRequestURI(), "UTF-8")); return false; } String protocol = "https"; StringBuffer file = new StringBuffer(hrequest.getRequestURI()); String requestedSessionId = hrequest.getRequestedSessionId(); if ((requestedSessionId != null) && hrequest.isRequestedSessionIdFromURL()) { file.append(";" + Globals.SESSION_PARAMETER_NAME + "="); file.append(requestedSessionId); } String queryString = hrequest.getQueryString(); if (queryString != null) { file.append('?'); file.append(queryString); } URL url = null; List hostAndPort = getHostAndPort(request); String serverHost = hostAndPort.get(0); redirectPort = Integer.parseInt((hostAndPort.get(1))); try { url = new URL(protocol, serverHost, redirectPort, file.toString()); hresponse.sendRedirect(url.toString()); return false; } catch (MalformedURLException e) { hresponse.sendError(SC_INTERNAL_SERVER_ERROR, encode(hrequest.getRequestURI(), "UTF-8")); return false; } } //START SJSAS 6232464 //pass in HttpServletResponse instead of saving it as instance variable //private String getCanonicalName(){ private String getCanonicalName(HttpRequest currentRequest) { return currentRequest.getWrapper().getServletName(); } private String getResourceName(String uri, String contextPath) { if (contextPath.length() < uri.length()) { return uri.substring(contextPath.length()); } else { return ""; } } /** * Return a short name for this Realm Adapter implementation. */ protected String getName() { return name; } /** * Return the name of the realm this RealmAdapter uses. * * @return realm name * */ public String getRealmName() { return _realmName; } public void setRealmName(String realmName) { // do nothing since this is done when initializing the Realm. } //START SJSAS 6232464 6202703 /** * Returns null * 1. if there are no security constraints defined on any of the web * resources within the context, or * 2. if the target is a form login related page or target. * * otherwise return an empty array of SecurityConstraint. */ public SecurityConstraint[] findSecurityConstraints(HttpRequest request, Context context) { if (helper == null) { initConfigHelper(context.getServletContext()); } WebSecurityManager secMgr = getWebSecurityManager(false); if (secMgr != null && secMgr.hasNoConstrainedResources() && !isSecurityExtensionEnabled(context.getServletContext())) { return null; } return emptyConstraints; } //START SJSAS 6232464 6202703 /** * Returns null * 1. if there are no security constraints defined on any of the web * resources within the context, or * 2. if the target is a form login related page or target. * * otherwise return an empty array of SecurityConstraint. */ public SecurityConstraint[] findSecurityConstraints(String requestPathMB, String httpMethod, Context context) { if (this.helper == null) { initConfigHelper(context.getServletContext()); } WebSecurityManager secMgr = getWebSecurityManager(false); if (secMgr != null && secMgr.hasNoConstrainedResources() && !isSecurityExtensionEnabled(context.getServletContext())) { return null; } return emptyConstraints; } //END SJSAS 6232464 6202703 //START SJSAS 6202703 /** * Checks whether or not authentication is needed. * Returns an int, one of AUTHENTICATE_NOT_NEEDED, AUTHENTICATE_NEEDED, * or AUTHENTICATED_NOT_AUTHORIZED * * @param request Request we are processing * @param response Response we are creating * @param constraints Security constraint we are enforcing * @param disableProxyCaching whether or not to disable proxy caching for * protected resources. * @param securePagesWithPragma true if we add headers which * are incompatible with downloading office documents in IE under SSL but * which fix a caching problem in Mozilla. * @param ssoEnabled true if sso is enabled * * @exception IOException if an input/output error occurs */ public int preAuthenticateCheck(HttpRequest request, HttpResponse response, SecurityConstraint[] constraints, boolean disableProxyCaching, boolean securePagesWithPragma, boolean ssoEnabled) throws IOException { boolean isGranted = false; try { HttpServletRequest hsr = (HttpServletRequest) request.getRequest(); if (hsr.getUserPrincipal() == null) { SecurityContext.setUnauthenticatedContext(); } if (helper != null && helper.getServerAuthConfig() != null) { return Realm.AUTHENTICATE_NEEDED; } isGranted = invokeWebSecurityManager(request, response, constraints); } catch (IOException iex) { throw iex; } catch (Throwable ex) { _logger.log(SEVERE, "web_server.excep_authenticate_realmadapter", ex); ((HttpServletResponse) response.getResponse()).sendError(SC_SERVICE_UNAVAILABLE); response.setDetailMessage(rb.getString("realmBase.forbidden")); return Realm.AUTHENTICATED_NOT_AUTHORIZED; } if (isGranted) { // HashMap sharedState; boolean delegateSessionMgmt = false; if (((HttpServletRequest) request).getUserPrincipal() != null) { disableProxyCaching(request, response, disableProxyCaching, securePagesWithPragma); if (ssoEnabled) { HttpServletRequest hreq = (HttpServletRequest) request.getRequest(); WebSecurityManager webSecMgr = getWebSecurityManager(true); if (!webSecMgr.permitAll(hreq)) { // create a session for protected sso association hreq.getSession(true); } } } return Realm.AUTHENTICATE_NOT_NEEDED; } else if (((HttpServletRequest) request).getUserPrincipal() != null) { ((HttpServletResponse) response.getResponse()).sendError(HttpServletResponse.SC_FORBIDDEN); response.setDetailMessage(rb.getString("realmBase.forbidden")); return Realm.AUTHENTICATED_NOT_AUTHORIZED; } else { disableProxyCaching(request, response, disableProxyCaching, securePagesWithPragma); return Realm.AUTHENTICATE_NEEDED; } } /** * Authenticates the user making this request, based on the specified * login configuration. Return true if any specified * requirements have been satisfied, or false if we have * created a response challenge already. * * @param request Request we are processing * @param response Response we are creating * @param context The Context to which client of this class is attached. * @param authenticator the current authenticator. * @param calledFromAuthenticate * @return * @exception IOException if an input/output error occurs */ @Override public boolean invokeAuthenticateDelegate(HttpRequest request, HttpResponse response, Context context, Authenticator authenticator, boolean calledFromAuthenticate) throws IOException { boolean result = false; LoginConfig config = context.getLoginConfig(); ServerAuthConfig serverAuthConfig = null; try { if (helper != null) { serverAuthConfig = helper.getServerAuthConfig(); } } catch (Exception ex) { throw new IOException(ex); } if (serverAuthConfig != null) { // JSR 196 is enabled for this application Principal wrapped = null; try { context.fireContainerEvent(BEFORE_AUTHENTICATION, null); // Get the WebPrincipal principal and add to the security context principals RequestFacade requestFacade = (RequestFacade)request.getRequest(); if (requestFacade != null) { wrapped = requestFacade.getPrincipal(); if (wrapped != null) { SecurityContext.getCurrent().setAdditionalPrincipal(wrapped); } } if (requestTracing != null && requestTracing.isRequestTracingEnabled()) { RequestEvent re = new RequestEvent("BEFORE JASPIC AUTH"); re.addProperty("AppContext", serverAuthConfig.getAppContext()); re.addProperty("Context", context.getPath()); requestTracing.traceRequestEvent(re); } result = validate(request, response, config, authenticator, calledFromAuthenticate); if (requestTracing != null && requestTracing.isRequestTracingEnabled()) { RequestEvent re = new RequestEvent("AFTER JASPIC AUTH"); re.addProperty("AuthResult", Boolean.toString(result)); Principal p = requestFacade.getPrincipal(); String pName = "null"; if (p != null) { pName = p.getName(); } re.addProperty("Principal",pName); requestTracing.traceRequestEvent(re); } } finally { SecurityContext.getCurrent().setAdditionalPrincipal(null); context.fireContainerEvent(AFTER_AUTHENTICATION, null); } } else { // JSR 196 is not enabled. Use the current authenticator. result = ((AuthenticatorBase) authenticator).authenticate(request, response, config); } return result; } /** * Post authentication for given request and response. * * @param request Request we are processing * @param response Response we are creating * @param context The Context to which client of this class is attached. * @exception IOException if an input/output error occurs */ public boolean invokePostAuthenticateDelegate(HttpRequest request, HttpResponse response, Context context) throws IOException { boolean result = false; ServerAuthContext sAC = null; try { if (helper != null) { HttpServletRequest req = (HttpServletRequest) request.getRequest(); MessageInfo messageInfo = (MessageInfo) req.getAttribute(MESSAGE_INFO); if (messageInfo != null) { //JSR 196 is enabled for this application sAC = (ServerAuthContext) messageInfo.getMap().get(SERVER_AUTH_CONTEXT); if (sAC != null) { try { context.fireContainerEvent(ContainerEvent.BEFORE_POST_AUTHENTICATION, null); AuthStatus authStatus = sAC.secureResponse(messageInfo, null); //null serviceSubject result = AuthStatus.SUCCESS.equals(authStatus); } finally { context.fireContainerEvent(ContainerEvent.AFTER_POST_AUTHENTICATION, null); } } } } } catch (AuthException ex) { IOException iex = new IOException(); iex.initCause(ex); throw iex; } finally { if (helper != null && sAC != null) { if (request instanceof HttpRequestWrapper) { request.removeNote(Globals.WRAPPED_REQUEST); } if (response instanceof HttpResponseWrapper) { request.removeNote(Globals.WRAPPED_RESPONSE); } } } return result; } protected static final String CONF_FILE_NAME = "auth.conf"; protected static final String HTTP_SERVLET_LAYER ="HttpServlet"; /** * Return true if a Security Extension is available. * @return true if a Security Extension is available. 1171 */ @Override public boolean isSecurityExtensionEnabled(final ServletContext context) { if (helper == null) { initConfigHelper(context); } try { return (helper.getServerAuthConfig() != null); } catch (Exception ex) { throw new RuntimeException(ex); } } /** * This must be invoked after virtualServer is set. */ private HttpServletHelper getConfigHelper(final ServletContext servletContext) { Map map = new HashMap(); map.put(HttpServletConstants.WEB_BUNDLE, webDesc); return new HttpServletHelper(getAppContextID(servletContext), map, null, // null handler _realmName, isSystemApp, defaultSystemProviderID); } /** * This must be invoked after virtualServer is set. */ private String getAppContextID(final ServletContext servletContext) { if (!servletContext.getVirtualServerName().equals( this.virtualServer.getName())) { // PAYARA-1261 downgrade log messages to INFO as users haven't got a problem _logger.log(Level.INFO, "Virtual server name from ServletContext: {0} differs from name from virtual.getName(): {1}", new Object[]{servletContext.getVirtualServerName(), virtualServer.getName()}); } if (!servletContext.getContextPath().equals(webDesc.getContextRoot())) { // PAYARA-1261 downgrade log messages to INFO as users haven't got a problem _logger.log(Level.INFO, "Context path from ServletContext: {0} differs from path from bundle: {1}", new Object[]{servletContext.getContextPath(), webDesc.getContextRoot()}); } return servletContext.getVirtualServerName() + " " + servletContext.getContextPath(); } @SuppressWarnings("unchecked") private boolean validate(HttpRequest request, HttpResponse response, LoginConfig config, Authenticator authenticator, boolean calledFromAuthenticate) throws IOException { HttpServletRequest servletRequest = (HttpServletRequest) request.getRequest(); HttpServletResponse servletResponse = (HttpServletResponse) response.getResponse(); Subject subject = new Subject(); MessageInfo messageInfo = new HttpMessageInfo(servletRequest, servletResponse); boolean isValidateSuccess = false; boolean isMandatory = true; try { isMandatory = !getWebSecurityManager(true).permitAll(servletRequest); // Issue - 9578 - produce user challenge if call originates from HttpServletRequest.authenticate if (isMandatory || calledFromAuthenticate) { setMandatory(messageInfo); } ServerAuthContext authContext = helper.getServerAuthContext(messageInfo, null); // null serviceSubject if (authContext != null) { // Call the JASPIC ServerAuthContext which should eventually call the ServerAuthModule (SAM) // Notice a null is passed in as the service subject // Additionally notice we only care about SUCCESS being returned or not and ignore // all other JASPIC AuthStatus values. isValidateSuccess = SUCCESS.equals(authContext.validateRequest(messageInfo, subject, null)); if (isValidateSuccess) { // store it only if validateRequest = true storeInRequest(servletRequest, messageInfo, authContext); } } else { throw new AuthException("null ServerAuthContext"); } } catch (AuthException ae) { _logger.log(FINE, "JMAC: http msg authentication fail", ae); servletResponse.setStatus(SC_INTERNAL_SERVER_ERROR); } catch (RuntimeException e) { _logger.log(FINE, "JMAC: Exception during validateRequest", e); servletResponse.sendError(SC_INTERNAL_SERVER_ERROR); } if (isValidateSuccess) { Set principalSet = subject.getPrincipals(); // Must be at least one new principal to establish non-default security context if (hasNewPrincipal(principalSet)) { SecurityContext securityContext = new SecurityContext(subject); // Assuming no null principal here Principal callerPrincipal = securityContext.getCallerPrincipal(); WebPrincipal webPrincipal = new WebPrincipal(callerPrincipal, securityContext); // TODO: check Java SE SecurityManager access SecurityContext.setCurrent(securityContext); try { String authType = getAuthType(messageInfo); if (authType == null && config != null && config.getAuthMethod() != null) { authType = config.getAuthMethod(); } if (shouldRegister(messageInfo)) { AuthenticatorProxy proxy = new AuthenticatorProxy(authenticator, webPrincipal, authType); proxy.authenticate(request, response, config); } else { request.setAuthType(authType == null ? PROXY_AUTH_TYPE : authType); request.setUserPrincipal(webPrincipal); } } catch (LifecycleException le) { _logger.log(SEVERE, "[Web-Security] unable to register session", le); } } else { // GLASSFISH-20930. Set null for the case when SAM does not indicate that it needs the session if (hasRequestPrincipal(messageInfo)) { request.setUserPrincipal(null); request.setAuthType(null); } // If authentication is mandatory, we must have a non-anonymous principal if (isMandatory) { isValidateSuccess = false; } } if (isValidateSuccess) { // Check if the SAM instructed us to wrap the request and response HttpServletRequest newRequest = (HttpServletRequest) messageInfo.getRequestMessage(); if (newRequest != servletRequest) { request.setNote(WRAPPED_REQUEST, new HttpRequestWrapper(request, newRequest)); } HttpServletResponse newResponse = (HttpServletResponse) messageInfo.getResponseMessage(); if (newResponse != servletResponse) { request.setNote(WRAPPED_RESPONSE, new HttpResponseWrapper(response, newResponse)); } } } return isValidateSuccess; } @SuppressWarnings("unchecked") private void storeInRequest( HttpServletRequest servletRequest, MessageInfo messageInfo, ServerAuthContext authContext) { messageInfo.getMap().put(SERVER_AUTH_CONTEXT, authContext); servletRequest.setAttribute(MESSAGE_INFO, messageInfo); } private boolean hasRequestPrincipal(MessageInfo messageInfo) { return ((HttpServletRequest) messageInfo.getRequestMessage()).getUserPrincipal() != null; } private boolean hasNewPrincipal(Set principalSet) { return principalSet != null && !principalSet.isEmpty() && !principalSetContainsOnlyAnonymousPrincipal(principalSet); } @SuppressWarnings("unchecked") private void setMandatory(MessageInfo messageInfo) { messageInfo.getMap().put(IS_MANDATORY, TRUE.toString()); } private String getAuthType(MessageInfo messageInfo) { return (String) messageInfo.getMap().get(AUTH_TYPE); } private boolean shouldRegister(MessageInfo messageInfo) { @SuppressWarnings("rawtypes") Map map = messageInfo.getMap(); /* * Detect both the proprietary property and the standard one. */ return map.containsKey(REGISTER_WITH_AUTHENTICATOR) || mapEntryToBoolean(REGISTER_SESSION, map); } private boolean mapEntryToBoolean(final String propName, final Map map) { if (map.containsKey(propName)) { Object value = map.get(propName); if (value != null && value instanceof String) { return Boolean.valueOf((String) value); } } return false; } /** * get the default provider id for system apps if one has been established. * the default provider for system apps is established by defining * a system property. * @return the provider id or null. */ private static String getDefaultSystemProviderID() { String p = System.getProperty(SYSTEM_HTTPSERVLET_SECURITY_PROVIDER); if (p != null) { p = p.trim(); if (p.length() == 0) { p = null; } } return p; } private static String PROXY_AUTH_TYPE = "PLUGGABLE_PROVIDER"; private void resetPolicyContext() { ((PolicyContextHandlerImpl)PolicyContextHandlerImpl.getInstance()).reset(); PolicyContext.setContextID(null); } // inner class extends AuthenticatorBase such that session registration // of webtier can be invoked by RealmAdapter after authentication // by authentication module. static class AuthenticatorProxy extends AuthenticatorBase { private AuthenticatorBase authBase; private Principal principal; private String authType; @Override public boolean getCache() { return authBase.getCache(); } @Override public Container getContainer() { return authBase.getContainer(); } AuthenticatorProxy(Authenticator authenticator, Principal p, String authType) throws LifecycleException { this.authBase = (AuthenticatorBase) authenticator; this.principal = p; this.authType = authType == null ? RealmAdapter.PROXY_AUTH_TYPE : authType; setCache(authBase.getCache()); setContainer(authBase.getContainer()); start(); //finds sso valve and sets its value in proxy } @Override public boolean authenticate(HttpRequest request, HttpResponse response, LoginConfig config) throws IOException { if(cache) { getSession(request, true); } register(request, response, this.principal, this.authType, this.principal.getName(), null); return true; } @Override public String getAuthMethod() { return authType; } } private static class HttpMessageInfo implements MessageInfo { private Object request = null; private Object response = null; private Map map = new HashMap(); HttpMessageInfo() { } HttpMessageInfo(HttpServletRequest request, HttpServletResponse response) { this.request = request; this.response = response; } @Override public Object getRequestMessage() { return request; } @Override public Object getResponseMessage() { return response; } @Override public void setRequestMessage(Object request) { this.request = request; } @Override public void setResponseMessage(Object response) { this.response = response; } @Override public Map getMap() { return map; } } @Override public void initializeRealm(Object descriptor, boolean isSystemApp, String realmName) { this.isSystemApp = isSystemApp; webDesc = (WebBundleDescriptor) descriptor; Application app = webDesc.getApplication(); // mapper = app.getRoleMapper(); LoginConfiguration loginConfig = webDesc.getLoginConfiguration(); _realmName = app.getRealm(); if (_realmName == null && loginConfig != null) { _realmName = loginConfig.getRealmName(); } if (realmName != null && (_realmName == null || _realmName.equals(""))) { _realmName = realmName; } // BEGIN IASRI 4747594 CONTEXT_ID = WebSecurityManager.getContextID(webDesc); runAsPrincipals = new HashMap(); Iterator bundle = webDesc.getWebComponentDescriptors().iterator(); while (bundle.hasNext()) { WebComponentDescriptor wcd = (WebComponentDescriptor) bundle.next(); RunAsIdentityDescriptor runAsDescriptor = wcd.getRunAsIdentity(); if (runAsDescriptor != null) { String principal = runAsDescriptor.getPrincipal(); String servlet = wcd.getCanonicalName(); if (principal == null || servlet == null) { _logger.warning("web.realmadapter.norunas"); } else { runAsPrincipals.put(servlet, principal); _logger.fine("Servlet " + servlet + " will run-as: " + principal); } } } // END IASRI 4747594 //this.appID = app.getRegistrationName(); this.moduleID = webDesc.getModuleID(); // helper are set until setVirtualServer is invoked //handled in SecurityDeployer now. //configureSecurity(webDesc, isSystemApp); } /** * Generate the JSR 115 policy file for a web application, bundled * within a ear or deployed as a standalone war file. * * Implementation note: If the generated file doesn't contains * all the permission, the role mapper is probably broken. */ protected void configureSecurity(WebBundleDescriptor wbd, boolean isSystem) { try { webSecurityManagerFactory.createManager(wbd,true, serverContext); String context = WebSecurityManager.getContextID(wbd); SecurityUtil.generatePolicyFile(context); if (isSystem && context.equals("__admingui/__admingui")) { websecurityProbeProvider.policyCreationEvent(context); } } catch (Exception ce) { _logger.log(Level.SEVERE, "policy.configure", ce); throw new RuntimeException(ce); } } //Moved from J2EEInstanceListener.java private SecurityContext getSecurityContextForPrincipal(final Principal p) { if (p == null) { return null; } else if (p instanceof WebPrincipal) { return ((WebPrincipal) p).getSecurityContext(); } else { return AccessController.doPrivileged(new PrivilegedAction() { @Override public SecurityContext run() { Subject s = new Subject(); s.getPrincipals().add(p); return new SecurityContext(p.getName(), s); } }); } } public void setCurrentSecurityContextWithWebPrincipal(Principal principal) { if (principal instanceof WebPrincipal) { SecurityContext.setCurrent(getSecurityContextForPrincipal(principal)); } } public void setCurrentSecurityContext(Principal principal) { SecurityContext.setCurrent(getSecurityContextForPrincipal(principal)); } //TODO: reexamine this after TP2 public synchronized void initConfigHelper(final ServletContext servletContext) { if (this.helper != null) { return; } this.helper = getConfigHelper(servletContext); } public void postConstruct() { nwListeners = networkConfig.getNetworkListeners(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy