Java Security Manager: restrictions on code from an external bank loaded using ServiceLoader

What am I trying to achieve? I am working on a Java application that can be supplemented with additional banks that integrate through ServiceLoader. These downloaded extensions should run with some SecurityManager limitations, of course, just to increase security. As an example, each Extension should receive one specific directory where it can store everything, but access to any other file / folder should be limited. The main application is trusted code and therefore can work without any restrictions. In addition, the main application provides some api implementations for each extension, which should also run without restriction. This means that the extension should not access the file outside its directory, but when the extension calls the api method, which tries to access any other file,Access must be granted.

Question How can I achieve the mentioned behavior, which is limited only to โ€œdirectโ€ calls from extension classes, but is not code from the main application? Running extensions on different threads / threadGroups may be a good solution anyway, but since api calls can be performed under the same thread (group), this may not help determine whether access should be restricted or not based only on thread.

Example I created a simplified test environment. On the one hand, these two interfaces:

public interface Extension {
    void doSomethingRestricted();
    void doSameViaApi(ExtensionApi api);
}

public interface ExtensionApi {
    void doSomethingWithHigherPermissions();
}

For testing, I created a jar containing this extension:

public class SomeExtension implements Extension {

    public void doSomethingRestricted() {
        System.out.println(System.getProperty("user.home"));
    }

    public void doSameViaApi(final ExtensionApi api) {
        api.doSomethingWithHigherPermissions();
    }
}

In the main application, I would like to do something like this:

final ExtensionApi api = () -> System.out.println(System.getProperty("user.home"));
try {
    final URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { jarFile.toURI().toURL() });
    for(final Extension extension : ServiceLoader.load(Extension.class, urlClassLoader)) {
        extension.doSomethingRestricted();
        extension.doSameViaApi(api);
    }
}

, extension.doSomethingRestricted();, SecurityException, extension.doSameViaApi(api); . , , api. , , - , , . , , , , , ?

+4
1

, "" JAR . :

package q46991566;

import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Policy;
import java.util.Collections;

public class Main {

    public static void main(String... args) throws Exception {
        // policy configuration contents: this JAR gets all permissions, others get nothing
        StringBuilder sb = new StringBuilder("grant {};\n\ngrant codebase \"")
                .append(Main.class.getProtectionDomain().getCodeSource().getLocation())
                .append("\" {\n\tpermission java.security.AllPermission;\n};\n");
        // temp-save the policy configuration
        Path policyPath = Files.createTempFile(null, null);
        Files.write(policyPath, Collections.singleton(sb.toString()));
        // convey to the default file-backed policy provider where to obtain its configuration from;
        // leading equals ensures only the specified config file gets processed
        System.setProperty("java.security.policy", "=".concat(policyPath.toUri().toURL().toString()));
        // establish a policy; "javaPolicy" is the default provider standard JCA name
        Policy.setPolicy(Policy.getInstance("javaPolicy", null));
        // policy loaded; backing config no longer needed
        Files.delete(policyPath);
        // establish a security manager for enforcing the policy (the default implementation is more than
        // sufficient)
        System.setSecurityManager(new SecurityManager());

        // ...
    }

}

: a) JRE java.policy ( policy.url.n java.security), b) ClassLoader AllPermission ProtectionDomain, , "" JAR.

-, Extension JAR URLClassLoader, : a) , , b) java.io.FilePermission , , . ( , JAR , , Extension, JAR ( , ), ):

package q46991566;

import java.io.FilePermission;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.cert.Certificate;
import java.util.Enumeration;
import java.util.Objects;

public final class ExtensionLoader extends URLClassLoader {

    private static void copyPermissions(PermissionCollection src, PermissionCollection dst) {
        for (Enumeration<Permission> e = src.elements(); e.hasMoreElements();) {
            dst.add(e.nextElement());
        }
    }

    private final CodeSource origin;
    private final PermissionCollection perms = new Permissions();
    private final Path baseDir;

    public ExtensionLoader(URL extensionOrigin) {
        super(new URL[] { extensionOrigin });
        origin = new CodeSource(Objects.requireNonNull(extensionOrigin), (Certificate[]) null);
        try {
            baseDir = Files.createTempDirectory(null);
            perms.add(new FilePermission(baseDir.toString().concat("/-"), "read,write,delete"));
            copyPermissions(super.getPermissions(origin), perms);
            perms.setReadOnly();
        }
        catch (IOException ioe) {
            throw new RuntimeException(ioe);
        }
    }

    @Override
    protected PermissionCollection getPermissions(CodeSource cs) {
        return (origin.implies(cs)) ? perms : super.getPermissions(cs);
    }

    // ExtensionApiImpl (or ExtensionImpl directly -- but then ExtensionLoader would have to be relocated
    // into a separate, also fully privileged JAR, accessible to the extension) can call this to relay to
    // extensions where they can persist their data
    public Path getExtensionBaseDir() {
        return baseDir;
    }

    // optionally override close() to delete baseDir early

}

, Extension, ExtensionApi, ( SecurityManager::checkXXX ) Privileged(Exception)Action AccessController::doPrivileged; :.

ExtensionApi api = () -> {
    AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
        try {
            Files.write(Paths.get("/root/Documents/highly-sensitive.doc"), Collections.singleton("trusted content"),
                    StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.APPEND);
            return null;
        }
        catch (IOException ioe) {
            throw new RuntimeException(ioe);
        }
    });
};

() " " . AccessController " " Java SE " .

+1

Source: https://habr.com/ru/post/1688341/


All Articles