0

After implementing my platform to have the ability to load jar files dynamically using reflection, I come across a security issue. The jar file could be 'dangerous' and contain code that could effect things, therefore I come across on SO the use of using a SecurityManager.

I am loading the jar file like this (example of hard coded file):

URLClassLoader clsLoader = URLClassLoader
    .newInstance(new URL[] { new URL("file:/C://Temp/SettingsApp.jar") });
Class<?> cls = clsLoader.loadClass("iezon.app.SettingsApp");
JPanel settingsApp = (JPanel) cls.newInstance();

I have tried to use the SecurityManager like so:

try {
    SecurityManager sm = new SecurityManager();
    Object context = sm.getSecurityContext();
    sm.checkPermission(new AllPermission(), context);
    settingsAppPanel = (JPanel) cls.newInstance();
} catch (AccessControlException e) {
    e.printStackTrace();
}

The test with AllPermission() works and gives me this exception:

java.security.AccessControlException: access denied ("java.security.AllPermission" "" "")

However, I am unable to find any information on how to bind the jar file to this security manager and not the current platform. I just don't want the jar file to be able to open sockets, etc.. without firstly requesting permissions that the user can then accept.

I stumbled across BasicPermission, however, this is an abstract class and cannot be instanced like the AllPermission and likewise, I am unable to find any information on approaching this. Any help would be appreciated!

Jaquarh
  • 6,493
  • 7
  • 34
  • 86
  • There seems to be a fundamental misconception. Whether you invoke `sm.checkPermission(new AllPermission(), context);` somewhere, has no relevance to the security at all. So replacing `AllPermission` with another type of permission is useless. To use a security manager, you have to *install* it, either via `System.setSecurityManager(…)` or via startup options. Further, you have to decide whether you want to *implement* a security manager or use the standard security manager and write a policy file. In either case, you will never have to instantiate permission objects. – Holger Sep 19 '18 at 07:13
  • I answered the question based on your comment @Holger – Jaquarh Sep 19 '18 at 07:43

1 Answers1

0

As @Holger mentioned in the comments, the only way to implement using a Security Manager is to dynamically create policies. Therefore, when a developer uploads a new app to the appstore, he is given a set of policies to declare that his app uses. On the app, this is then fed to the user asking them to confirm this policy (at their own risk if it involves sockets etc...) and then a policy is created and the app can run:

if (!app.policyIsAgreedTo()) {
        if(readPolicy(app).get(0).equalsIgnoreCase("grant {")) {
            app.setPolicy(true);
        } else {
            throw new Exception("App policy has not been agreed to.");
        }
    }

    if (app.policyIsAgreedTo()) {
        System.setProperty("java.security.policy", "file:/C:/Temp/" + app.getName() + ".policy");
        System.setSecurityManager(new SecurityManager());
        JPanel panel = (JPanel) app.getObject().newInstance();
        System.setSecurityManager(null);
        return panel;
    }

    throw new Exception("App policy has not been agreed to.");

The policy gets created like so:

public static void createPolicy(ArrayList<String> policies, App app) {

    try {
        FileWriter fileWriter =
                new FileWriter("C:/Temp/" + app.getName() + ".policy");
        BufferedWriter bufferedWriter =
                new BufferedWriter(fileWriter);

        bufferedWriter.write("grant {");
        bufferedWriter.newLine();

        for(String policy : policies) {
            bufferedWriter.write("    permission java.lang.RuntimePermission \"" + policy + "\";");
            bufferedWriter.newLine();
        }

        bufferedWriter.write("};");
        bufferedWriter.close();

    } catch(IOException ex) {
        ex.printStackTrace();
    }
}

The app makes an API call to the app store gathering the permissions the app needs, then a simple createPolicy() is called with the permissions in an ArrayList ie:

new ArrayList<String>() {{ add("setSecurityManager"); }}; // Etc...
Jaquarh
  • 6,493
  • 7
  • 34
  • 86
  • When you want security, you have to keep the installed security manages for the entire time the code is in your JVM. Calling `System.setSecurityManager(null);` right after invoking the constructor means, you only have security while the constructor is executed. If you want dynamic policies, you have to implement your own security manager, which supports extending the current policy. You can not do it by de-installing the current security manager, not even for a short time. – Holger Sep 19 '18 at 07:50
  • Could I override the `checkPermission()` method, wrap the `super.checkPermission()` around an `instanceof()` and pass through the apps? For each of the apps, set the property to that specific policy? @Holger Would that then keep a security manager attached to that process? – Jaquarh Sep 19 '18 at 18:30
  • @Holger IE: Summit like this `System.setSecurityManager(new SecurityManager() { @Override public void checkPermission(java.security.Permission perm) { Object context = this.getSecurityContext(); if(context.getClass() == app.getClass()) super.checkPermission(perm); } });` but load the app policy before the super call – Jaquarh Sep 19 '18 at 18:36
  • [Would adding a codeBase](https://docs.oracle.com/javase/6/docs/technotes/guides/security/PolicyFiles.html) to the package `iezon.app.*` work in the policy? – Jaquarh Sep 19 '18 at 19:54