3

Hi SecurityManager Experts out there ;-)

I have written a small plugin framework that loads plugins with separate isolated classloaders. For a successfull undeploy of a plugin it is important to make sure that no reference to classes loaded by the plugin classloader are held by the application.

There is a feature in Java called shutdown hook that enables client code to register a thread that is executed when the JVM was requested to shut down. This implies that potentially there will be references held by the JVM to a class loaded by the plugin classloader.

My first attempt was to install a SecurityManager that denies adding a shutdown hook. This totally works and denies all attempts to add shutdown hooks. The interesting thing is, that I realized, that it is not the plugin itself that wants to add a shutdown hook. The plugin just triggers the AWT/Swing internals (precise: SunFontManager) by loading a font. The SunFontManager in turn adds the shutdown hook to do some of it's internal handling when the JVM exits. I do not want to deny that the plugin loads a font (because it has to) and I do not want do deny the Java internals to add their stuff.

For me it seems that I would have to grant more permissions to the code (if the sun.*|java.*|javax.* package is in the stack trace..uuuhmm...ugly) than it would have normally. But this breaks the conventions of the security framework which assumes that client code cannot have more privileges than code that is calling it.

How can I distinguish that it is not the plugin code but the Java internals that wants to do something? Or is there any other way to make sure not to corrupt the internals by denying something?

Example of what happens

To make things clearer, here is what actually happens in my application. I have a custom SecurityManager installed that should deny addShutdownHook performed directly by plugins, but should not deny those calls if performed as a side-effect by Java internal classes.

Context:            Calls:
----------------------------------------------
AppClassloader      Application
                        |
PluginClassloader   PluginObject.init()
                        |
AppClassloader      Font.create()
                        |
                    AccessController.doPrivileged(...) {
                        Runtime.addShutdownHook(...)
                    }

The SecurityManager was installed using System.setSecurityManager() and does this:

@Override
public void checkPermission(java.security.Permission perm) {
  if (perm.getName().equals("shutdownHooks")) {
    if (threadContextClassLoaderIsPluginClassLoader()) {
      throw new SecurityException("Installing shutdown hooks is not allowed.");
    }
  }
}

Although the addShutdownHook is called inside a doPrivileged-block my SecurityManager is called. Am I missing something? Should I check for the privileged context myself?

UNIQUEorn
  • 420
  • 6
  • 17
  • I found this article stackoverflow.com/questions/2233761/… and it adresses the exact point. If I get things right, the addShutdownHook method should succeed if done in a AccessController.doPrivileged() block because the class `java.awt.Font` has all the permissions of the AppClassloader. Although called in a doPrivilieged() block, I still find my SecurityManager being asked for permissions. How should I check this in `java.lang.SecurityManager.checkPermissi‌​on(Permission)` ? – UNIQUEorn Oct 11 '16 at 12:02
  • if you call it inside a `doPrivileged` the `SecurityManager` still has to check if the 'privileged' class has the permission to register a shutdown hook. – Stefan Oct 11 '16 at 12:13

1 Answers1

5

I found out that I was totally misunderstanding the Java security concept. I want to build a sandbox for plugin code so that it is always running in a restricted environment where I can control the permissions it has. The application should always run alongside with full access granted.

Here is how to build a sandbox for classes loaded by a separate classloader:

  1. Use the default SecurityManager If you find yourself implementing your own java.lang.SecurityManager to allow or deny actions based on permissions you are probably doing wrong! You only need to install the default java.lang.SecurityManager by doing this System.setSecurityManager(new SecurityManager());. Now you get a working access control by using Java defaults.
  2. Grant all permissions to application The application that loads the plugins should get full access. This is because we trust ourselves. I solved this by starting my application with a policy file that grants all access. I think the policy file is needed on application start because the AppClassLoader needs to create the correct protection domains for the classes. Start the main application with the JavaVM argument java.security.policy=<URL-TO-POLICY> where <URL-TO-POLICY points to the following policy file:

    grant { permission java.security.AllPermission; };

  3. Custom policy implementation We need to install our custom policy after application startup. We want to separate the permissions for the main application (which runs with full access granted) and the plugins (which permissions we want to limit). The plugins should run in a sandbox with hand-picked permissions. To achieve this, here is the custom policy implementation:

    class SandboxPolicy extends Policy {
    @Override
    
      public PermissionCollection getPermissions(ProtectionDomain domain) {
        // Decide if the plugin permissions are needed or full access can be granted using all permissions.
        if (isPlugin(domain)) {
          return pluginPermissions();
        } else {
          return applicationPermissions();
        }
      }
    
      private boolean isPlugin(ProtectionDomain domain) {
        // Identify the classloader of the protection domain
        // The PluginClassLoader is assumed to be the one that loaded
        // the plugin
        return domain.getClassLoader() instanceof PluginClassLoader;
      }
    
      private PermissionCollection pluginPermissions() {
        // Empty permissions = No permissions
        // This is not the point to add plugin permissions
        return new Permissions();
      }
    
      private PermissionCollection applicationPermissions() {
        // Grant full access to the application
        Permissions permissions = new Permissions();
        permissions.add(new AllPermission());
        return permissions;
      }
    }
    
  4. ProtectionDomains per Classloader The classloaders are responsible to create a ProtectionDomain for every source, code is loaded from. The protection domains specify the permissions that will be granted to the code. We have to modify the set of permissions the classloader adds to the protection domains. To do this, let your PluginClassloader extend the java.security.SecureClassLoader. Then override the method java.security.SecureClassLoader.getPermissions(CodeSource) in the following way:

    @Override
    protected PermissionCollection getPermissions(CodeSource codeSource)
    {
        PermissionCollection pc;
        // The SecureClassloader per default grants access to read resources from the source JAR.
        // This is useful. Call super to get those permissions:
        pc = super.getPermissions(codeSource);
        // At this point you can extend permissions.
        // For example grant read access to a file.
        pc.add(new FilePermission("path\\file", "read"));
        return (pc);
    }
    

Note: See the policy implementation and have a look at my comment // This is not the point to add plugin permissions. The policy is asked by the AccessController to see if a permission can be granted. If permissions are added here, the information you can get from the caller are very limited. I woul not recommend to calculate and add any permissions here. The classloader is the point where you can add permissions based on the code source. I recommend to add permissions here. They become visible in the protection domains and you can easily observe what the AccessController is doing with those protection domains.

Privileged actions

There is another part in my question that I want to answer.

The interesting thing is, that I realized, that it is not the plugin itself that wants to add a shutdown hook. The plugin just triggers the AWT/Swing internals (precise: SunFontManager) by loading a font.

There are actions that will be performed as a side effect of plugin code. A class org.plugin.A can trigger the Swing/AWT internals to add a shutdown hook. In this case we do not want to deny this. The Java classes are in scope of the application and should get full access. The plugin code may be restricted and adding a shutdown hook directly should fail with SecurityException. This is a common use-case of java.security.AccessController.doPrivileged(PrivilegedAction<T>). A privileged action makes sure that the AccessController will only take the protection domain of the last stack frame into account. AWT/Swing is adding its shutdown hook inside a privileged action and therefore is allowed to do so, because the protection domain of the classes performin this action where setup with full access permission. Please refer to any documentation of java.security.AccessController to learn more about privileged actions.

UNIQUEorn
  • 420
  • 6
  • 17