8026204: Enhance auth login contexts
authorxuelei
Tue, 15 Oct 2013 18:15:46 -0700
changeset 22327 0939d1a7e612
parent 22326 2fb1fdf0e74e
child 22328 5a7d30033602
8026204: Enhance auth login contexts Summary: Enforce package access control with current context. Also reviewed by Alexander Fomin <alexander.fomin@oracle.com> Reviewed-by: weijun, ahgross
jdk/src/share/classes/javax/security/auth/login/LoginContext.java
--- a/jdk/src/share/classes/javax/security/auth/login/LoginContext.java	Mon Oct 14 16:13:20 2013 -0700
+++ b/jdk/src/share/classes/javax/security/auth/login/LoginContext.java	Tue Oct 15 18:15:46 2013 -0700
@@ -37,8 +37,10 @@
 import javax.security.auth.callback.*;
 import java.security.AccessController;
 import java.security.AccessControlContext;
+import java.security.PrivilegedAction;
 import sun.security.util.PendingException;
 import sun.security.util.ResourcesMgr;
+import sun.reflect.misc.ReflectUtil;
 
 /**
  * <p> The {@code LoginContext} class describes the basic methods used
@@ -209,8 +211,7 @@
     private Map<String,?> state = new HashMap<String,Object>();
 
     private Configuration config;
-    private boolean configProvided = false;
-    private AccessControlContext creatorAcc = null;
+    private AccessControlContext creatorAcc = null;  // customized config only
     private ModuleInfo[] moduleStack;
     private ClassLoader contextClassLoader = null;
     private static final Class<?>[] PARAMS = { };
@@ -226,10 +227,23 @@
     private static final sun.security.util.Debug debug =
         sun.security.util.Debug.getInstance("logincontext", "\t[LoginContext]");
 
+    // workaround to disable additional package access control with
+    // Thread Context Class Loader (TCCL).
+    private static final boolean noPackageAccessWithTCCL = "true".equals(
+        AccessController.doPrivileged(
+            new PrivilegedAction<String>() {
+                public String run() {
+                    return System.getProperty(
+                        "auth.login.untieAccessContextWithTCCL");
+                }
+            }
+        ));
+
+
     private void init(String name) throws LoginException {
 
         SecurityManager sm = System.getSecurityManager();
-        if (sm != null && !configProvided) {
+        if (sm != null && creatorAcc == null) {
             sm.checkPermission(new AuthPermission
                                 ("createLoginContext." + name));
         }
@@ -252,7 +266,7 @@
         AppConfigurationEntry[] entries = config.getAppConfigurationEntry(name);
         if (entries == null) {
 
-            if (sm != null && !configProvided) {
+            if (sm != null && creatorAcc == null) {
                 sm.checkPermission(new AuthPermission
                                 ("createLoginContext." + OTHER));
             }
@@ -298,10 +312,10 @@
                         (DEFAULT_HANDLER);
                     if (defaultHandler == null || defaultHandler.length() == 0)
                         return null;
-                    Class<?> c = Class.forName(defaultHandler,
-                                        true,
-                                        finalLoader);
-                    return (CallbackHandler)c.newInstance();
+                    Class<? extends CallbackHandler> c = Class.forName(
+                            defaultHandler, true,
+                            finalLoader).asSubclass(CallbackHandler.class);
+                    return c.newInstance();
                 }
             });
         } catch (java.security.PrivilegedActionException pae) {
@@ -309,7 +323,7 @@
         }
 
         // secure it with the caller's ACC
-        if (this.callbackHandler != null && !configProvided) {
+        if (this.callbackHandler != null && creatorAcc == null) {
             this.callbackHandler = new SecureCallbackHandler
                                 (java.security.AccessController.getContext(),
                                 this.callbackHandler);
@@ -498,8 +512,7 @@
                         CallbackHandler callbackHandler,
                         Configuration config) throws LoginException {
         this.config = config;
-        configProvided = (config != null) ? true : false;
-        if (configProvided) {
+        if (config != null) {
             creatorAcc = java.security.AccessController.getContext();
         }
 
@@ -510,7 +523,7 @@
         }
         if (callbackHandler == null) {
             loadDefaultCallbackHandler();
-        } else if (!configProvided) {
+        } else if (creatorAcc == null) {
             this.callbackHandler = new SecureCallbackHandler
                                 (java.security.AccessController.getContext(),
                                 callbackHandler);
@@ -577,23 +590,13 @@
         }
 
         try {
-            if (configProvided) {
-                // module invoked in doPrivileged with creatorAcc
-                invokeCreatorPriv(LOGIN_METHOD);
-                invokeCreatorPriv(COMMIT_METHOD);
-            } else {
-                // module invoked in doPrivileged
-                invokePriv(LOGIN_METHOD);
-                invokePriv(COMMIT_METHOD);
-            }
+            // module invoked in doPrivileged
+            invokePriv(LOGIN_METHOD);
+            invokePriv(COMMIT_METHOD);
             loginSucceeded = true;
         } catch (LoginException le) {
             try {
-                if (configProvided) {
-                    invokeCreatorPriv(ABORT_METHOD);
-                } else {
-                    invokePriv(ABORT_METHOD);
-                }
+                invokePriv(ABORT_METHOD);
             } catch (LoginException le2) {
                 throw le;
             }
@@ -628,13 +631,8 @@
                 ("null.subject.logout.called.before.login"));
         }
 
-        if (configProvided) {
-            // module invoked in doPrivileged with creatorAcc
-            invokeCreatorPriv(LOGOUT_METHOD);
-        } else {
-            // module invoked in doPrivileged
-            invokePriv(LOGOUT_METHOD);
-        }
+        // module invoked in doPrivileged
+        invokePriv(LOGOUT_METHOD);
     }
 
     /**
@@ -677,7 +675,8 @@
 
     /**
      * Invokes the login, commit, and logout methods
-     * from a LoginModule inside a doPrivileged block.
+     * from a LoginModule inside a doPrivileged block restricted
+     * by creatorAcc (may be null).
      *
      * This version is called if the caller did not instantiate
      * the LoginContext with a Configuration object.
@@ -690,29 +689,6 @@
                     invoke(methodName);
                     return null;
                 }
-            });
-        } catch (java.security.PrivilegedActionException pae) {
-            throw (LoginException)pae.getException();
-        }
-    }
-
-    /**
-     * Invokes the login, commit, and logout methods
-     * from a LoginModule inside a doPrivileged block restricted
-     * by creatorAcc
-     *
-     * This version is called if the caller instantiated
-     * the LoginContext with a Configuration object.
-     */
-    private void invokeCreatorPriv(final String methodName)
-                throws LoginException {
-        try {
-            java.security.AccessController.doPrivileged
-                (new java.security.PrivilegedExceptionAction<Void>() {
-                public Void run() throws LoginException {
-                    invoke(methodName);
-                    return null;
-                }
             }, creatorAcc);
         } catch (java.security.PrivilegedActionException pae) {
             throw (LoginException)pae.getException();
@@ -735,24 +711,30 @@
                 } else {
 
                     // instantiate the LoginModule
-                    Class<?> c = Class.forName
-                                (moduleStack[i].entry.getLoginModuleName(),
+                    //
+                    // Allow any object to be a LoginModule as long as it
+                    // conforms to the interface if no customized config or
+                    // noPackageAccessWithTCCL is true.
+                    Class<?> c = Class.forName(
+                                moduleStack[i].entry.getLoginModuleName(),
                                 true,
                                 contextClassLoader);
+                    // check package access for customized config
+                    if (!noPackageAccessWithTCCL && creatorAcc != null) {
+                        c.asSubclass(javax.security.auth.spi.LoginModule.class);
+                        checkPackageAccess(c, creatorAcc);
+                    }
 
                     Constructor<?> constructor = c.getConstructor(PARAMS);
                     Object[] args = { };
-
-                    // allow any object to be a LoginModule
-                    // as long as it conforms to the interface
                     moduleStack[i].module = constructor.newInstance(args);
 
+                    // call the LoginModule's initialize method
                     methods = moduleStack[i].module.getClass().getMethods();
-
-                    // call the LoginModule's initialize method
                     for (mIndex = 0; mIndex < methods.length; mIndex++) {
-                        if (methods[mIndex].getName().equals(INIT_METHOD))
+                        if (methods[mIndex].getName().equals(INIT_METHOD)) {
                             break;
+                        }
                     }
 
                     Object[] initArgs = {subject,
@@ -760,19 +742,28 @@
                                         state,
                                         moduleStack[i].entry.getOptions() };
                     // invoke the LoginModule initialize method
+                    //
+                    // Throws ArrayIndexOutOfBoundsException if no such
+                    // method defined.  May improve to use LoginException in
+                    // the future.
                     methods[mIndex].invoke(moduleStack[i].module, initArgs);
                 }
 
                 // find the requested method in the LoginModule
                 for (mIndex = 0; mIndex < methods.length; mIndex++) {
-                    if (methods[mIndex].getName().equals(methodName))
+                    if (methods[mIndex].getName().equals(methodName)) {
                         break;
+                    }
                 }
 
                 // set up the arguments to be passed to the LoginModule method
                 Object[] args = { };
 
                 // invoke the LoginModule method
+                //
+                // Throws ArrayIndexOutOfBoundsException if no such
+                // method defined.  May improve to use LoginException in
+                // the future.
                 boolean status = ((Boolean)methods[mIndex].invoke
                                 (moduleStack[i].module, args)).booleanValue();
 
@@ -936,6 +927,35 @@
     }
 
     /**
+     * check package access of a class that is loaded with Thread Context
+     * Class Loader (TCCL) with specified access control context.
+     *
+     * Similar to java.lang.ClassLoader.checkPackageAccess()
+     */
+    static void checkPackageAccess(Class<?> cls, AccessControlContext context) {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            if (ReflectUtil.isNonPublicProxyClass(cls)) {
+                for (Class<?> intf: cls.getInterfaces()) {
+                    checkPackageAccess(intf, context);
+                }
+                return;
+            }
+
+            final String name = cls.getName();
+            final int i = name.lastIndexOf('.');
+            if (i != -1) {
+                AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                    public Void run() {
+                        sm.checkPackageAccess(name.substring(0, i));
+                        return null;
+                    }
+                }, context);
+            }
+        }
+    }
+
+    /**
      * Wrap the caller-specified CallbackHandler in our own
      * and invoke it within a privileged block, constrained by
      * the caller's AccessControlContext.