I'm trying to implement a custom java.security.Permission type, which should be checked at runtime (so no policy file, but in code). This checking is done by a java.security.Policy. I understood I should implement my own java.security.PolicySpi for this.
I cannot find any explanation on how to initialise and use a PolicySpi, or is there a better way to do this?
Checking permissions
In your question you stated that you then want to check the permission with java.security.Policy, but without using a spi.policy file.
From the PolicySpi API, you can see that a PolicySpi object features 4 methods:
engineGetPermissions(CodeSource codesource)
engineGetPermissions(ProtectionDomain domain)
engineImplies(ProtectionDomain domain, Permission permission)
engineRefresh()
However, you might not need PolicySpi as there are easier solutions to check permissions.
See:
Security Manager vs Access Controller
AccessController usage
Since you haven't specified what kind of permission you will grant, I will assume it is a permission concerning a java.security.CodeSource object.
To check all current permissions for a file:
public static void main(String[] args) {
CodeSource source;
try {
source = new CodeSource(new URL("file:/c:/*"), (java.security.cert.Certificate[]) null);
Policy policy = Policy.getPolicy();
System.out.println(policy.getPermissions(source));
} catch (IOException e) {
e.printStackTrace();
}
}
A nice example for the SecurityManager checkPermission() is this tutorial.
For checking specific FilePermissions, you can use:
FilePermission perm = new FilePermission("path/file", "read");
AccessController.checkPermission(perm);
Granting permissions
Granting permissions at runtime can be done with java.lang.RuntimePermission.
For other examples of how to grant permissions to a file, I suggest you read the following:
Access Control Mechanisms and Algorithms
Configuring spi.policy files
Security Managers and Permissions
That should bring you a long way! Good luck!
The previous answer lists alternatives to using PolicySpi (and more generally custom Policy implementations ). This answer will instead give a simplistic example on how a PolicySpi implementation can actually be used as a replacement of the system-default Policy.
Author a JCA Provider.
package com.example;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.Provider;
public final class TestProvider extends Provider {
private static final long serialVersionUID = 5544432861418770903L;
public TestProvider() {
super("TestProvider", 1, "TestProvider 1.0");
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
putService(new TestPolicySpiService(this));
return null;
});
}
}
Author the sole Service descriptor encapsulated by the provider.
package com.example;
import java.security.Policy.Parameters;
import java.security.PolicySpi;
import java.security.Provider;
import java.security.Provider.Service;
import java.util.Collections;
final class TestPolicySpiService extends Service {
TestPolicySpiService(Provider p) {
super(p, "Policy", "TestPolicy", PolicySpi.class.getName(), Collections.emptyList(), Collections.emptyMap());
}
#Override
public PolicySpi newInstance(Object constructorParameter) {
Parameters policyParams = null;
if (constructorParameter instanceof Parameters) {
policyParams = (Parameters) constructorParameter;
}
return new TestPolicySpi(policyParams);
}
#Override
public boolean supportsParameter(Object parameter) {
return parameter instanceof Parameters;
}
}
Author the actual service (the PolicySpi implementation in this case) that the service descriptor produces.
package com.example;
import java.security.Permission;
import java.security.Policy.Parameters;
import java.security.PolicySpi;
import java.security.ProtectionDomain;
final class TestPolicySpi extends PolicySpi {
TestPolicySpi(Parameters policyParams) {}
#Override
protected boolean engineImplies(ProtectionDomain domain, Permission permission) {
// deny unconditionally
return false;
}
}
Register the provider either statically, by modifying the security.provider.n properties in JAVA_HOME/lib/security/java.security, or programmatically, via java.security.Security.addProvider(Provider) / java.security.Security.insertProviderAt(Provider, int).
Replace the default Policy.
package com.example;
import java.security.NoSuchAlgorithmException;
import java.security.Policy;
public class Main {
public static void main(String... args) throws NoSuchAlgorithmException {
// the following assumes that the provider has been statically registered
Policy.setPolicy(Policy.getInstance("TestPolicy", null));
System.setSecurityManager(new SecurityManager());
// test
System.out.println(System.getProperty("user.home")); // should raise AccessControlException
}
}
Is there a better way to do this?
There certainly is a less involved way, as long as the consequent tight coupling between application and policy does not irk you too badly: Just subclass Policy directly and pass an instance of your implementation to Policy.setPolicy(Policy).
Further reading:
Java Cryptography Architecture (JCA) Reference Guide
How to Implement a Provider in the Java Cryptography Architecture
Standard Algorithm Name Documentation for JDK 8
Troubleshooting Security
As of Java 6, the default implementation for PolicySpi is sun.security.provider.PolicySpiFile. You can get inspired from the source code of PolicySpiFile:
package sun.security.provider;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Policy;
import java.security.PolicySpi;
import java.security.ProtectionDomain;
import java.security.URIParameter;
import java.net.MalformedURLException;
/**
* This class wraps the PolicyFile subclass implementation of Policy
* inside a PolicySpi implementation that is available from the SUN provider
* via the Policy.getInstance calls.
*
*/
public final class PolicySpiFile extends PolicySpi {
private PolicyFile pf;
public PolicySpiFile(Policy.Parameters params) {
if (params == null) {
pf = new PolicyFile();
} else {
if (!(params instanceof URIParameter)) {
throw new IllegalArgumentException
("Unrecognized policy parameter: " + params);
}
URIParameter uriParam = (URIParameter)params;
try {
pf = new PolicyFile(uriParam.getURI().toURL());
} catch (MalformedURLException mue) {
throw new IllegalArgumentException("Invalid URIParameter", mue);
}
}
}
protected PermissionCollection engineGetPermissions(CodeSource codesource) {
return pf.getPermissions(codesource);
}
protected PermissionCollection engineGetPermissions(ProtectionDomain d) {
return pf.getPermissions(d);
}
protected boolean engineImplies(ProtectionDomain d, Permission p) {
return pf.implies(d, p);
}
protected void engineRefresh() {
pf.refresh();
}
}
Related
Resilience4j version: 1.7.0
Java version: 1.8
I have challenge in implementing TimeLimiter feature of Resilience4j. I am able to get the Circuit Breaker (CB) work.
We have 2 services Lets say serviceA and serviceB. We have used Command design pattern which encapsulates logic to communicate with ServiceB. RabbitMQ is used to establish inter microservice communication. We had implemented Hystrix CB by making all our Command classes extend HystrixCommand. When we decided to move to Resilience4j main challenge was to retain the existing design pattern than configuring Resilence4J CB.
We have Synchronous communication at present between ServiceA and ServiceB. Though we use RabbitMQ to communicate which is Async communication, with the help of Spring wrapper method RabbitTemplate.convertSendAndReceive() we are able to achieve Sync mode of communication with RabbitMQ.
When I removed HystrixCommand reference which was the Base class for all my Command classes, naturally there was a need to implement a custom Base Command class which will be implemented using Resilience4J Decorators.
I managed introduce a Resilience4JCommand abstract class which will implement a execute() and execute run() from all my command classes. Also defined a abstract run() which all my existing Command classes will override and implement business logic.
I understood from many of the discussion that our method which needs to implement CB pattern needs to return of type CompletableFuture and also understood from many places that fallback method also must have same return type. My Base Command Class Resilience4JCommand looks something like below
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import com.ge.hc.XYZ.exception.ResourceNotFoundException;
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import io.github.resilience4j.bulkhead.annotation.Bulkhead.Type;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.timelimiter.annotation.TimeLimiter;
#Component
public abstract class Resilience4JCommand<R> {
/** The class logger. */
protected static final Logger LOGGER = LoggerFactory.getLogger(Resilience4JCommand.class);
public R execute() {
R result = null;
try {
result = executeWithCircuitBreaker().get();
} catch (Exception e) {
System.out.println("Inside Catch block of executeAsync ...........**************\n\n ");
e.printStackTrace();
throw new RuntimeException(e);
}
return result;
}
#Bulkhead(name = "XYZStreamingServer3", fallbackMethod = "getFallback", type = Bulkhead.Type.THREADPOOL)
#TimeLimiter(name = "XYZStreamingServer2", fallbackMethod = "getFallback")
#CircuitBreaker(name = "XYZStreamingServer1", fallbackMethod = "getFallback")
public CompletableFuture<R> executeWithCircuitBreaker() {
return CompletableFuture.supplyAsync(new Supplier<R>() {
#Override
public R get() {
return run();
}
});
}
protected abstract R run();
public CompletableFuture<R> getFallback(Throwable e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
if (e != null) {
e.printStackTrace(pw);
}
String reason = sw.toString();
LOGGER.error("Calling XYZ-hystrix fallback method for command: {}; fallback reason: {}",
this.getClass().getSimpleName(), (reason.isEmpty() ? "unknown" : reason));
throw new ResourceNotFoundException("Circuit Breaker ");
}
}
But nothing works with above setup. I am able to achieve CB alone work without the need of writing new method executeWithCircuitBreaker() which returns CompletableFuture. I can make CB work just with below execute()
Bulkhead AND TimeLimiter do not work with return type other than CompletableFuture
#CircuitBreaker(name = SCHEME_NAME, fallbackMethod = "getFallback")
public R execute() {
return run();
}
I have spent more than a week in setting up this .. Helpful if someone can point me what I am missing 😢
My application.properties looks something like belwo
management.health.circuitbreakers.enabled=true
management.endpoints.web.exposure.include=health
management.endpoint.health.show-details=always
resilience4j.circuitbreaker.instances.XYZStreamingServer1.registerHealthIndicator=true
resilience4j.circuitbreaker.instances.XYZStreamingServer1.eventConsumerBufferSize=10
resilience4j.circuitbreaker.instances.XYZStreamingServer1.failureRateThreshold=50
resilience4j.circuitbreaker.instances.XYZStreamingServer1.minimumNumberOfCalls=5
resilience4j.circuitbreaker.instances.XYZStreamingServer1.automaticTransitionFromOpenToHalfOpenEnabled=true
resilience4j.circuitbreaker.instances.XYZStreamingServer1.waitDurationInOpenState=5s
resilience4j.circuitbreaker.instances.XYZStreamingServer1.permittedNumberOfCallsInHalfOpenState=3
resilience4j.circuitbreaker.instances.XYZStreamingServer1.slidingWindowSize=10
resilience4j.circuitbreaker.instances.XYZStreamingServer1.slidingWindowType=COUNT_BASED
resilience4j.timelimiter.instances.XYZStreamingServer2.timeoutDuration=5s
resilience4j.timelimiter.instances.XYZStreamingServer2.cancelRunningFuture=true
resilience4j.thread-pool-bulkhead.instances.XYZStreamingServer3.maxThreadPoolSize=10
resilience4j.thread-pool-bulkhead.instances.XYZStreamingServer3.coreThreadPoolSize=5
resilience4j.thread-pool-bulkhead.instances.XYZStreamingServer3.queueCapacity=5
I'm trying to prevent plugins that run "inside" the main Java application from accessing things they shouldn't. I've read about Policies, and AccessControllers, and ProtectionDomains, but they're very oriented around JARs.
I've tried this:
import java.nio.file.Files
import java.nio.file.Paths
import java.security.*
fun main(args: Array<String>) {
Policy.setPolicy(object : Policy() {})
System.setSecurityManager(SecurityManager())
val domain = ProtectionDomain(null, Permissions() /* no permissions */)
AccessController.doPrivileged(PrivilegedAction {
untrusted()
}, AccessControlContext(arrayOf(domain)))
}
fun untrusted() {
try {
// Works as expected
Files.readAllBytes(Paths.get("build.gradle"))
throw IllegalStateException("Was able to access file, but shouldn't have been able to")
} catch (e: AccessControlException) {
}
try {
// Should throw AccessControlException, but doesn't
AccessController.doPrivileged(PrivilegedAction {
Files.readAllBytes(Paths.get("build.gradle"))
})
throw IllegalStateException("Was able to access file, but shouldn't have been able to")
} catch (e: AccessControlException) {
}
}
Even though I'm invoking untrusted() via a custom limited ProtectionDomain, it seems it can trivially break out of it. I'm expecting the the doPrivileged call in untrusted to operate with the intersection of the permissions of the outermost ProtectionDomain (the main program, which has all permissions) and the caller's ProtectionDomain (which has no permissions), resulting in untrusted having essentially 0 permissions.
I've also tried with the domain set like this:
val domain = ProtectionDomain(CodeSource(URL("http://foo"), null as Array<CodeSigner>?), Permissions() /* no permissions */)
but this also doesn't work -- the Policy is queried with the main program's ProtectionDomain and not the one calling untrusted(). (Obviously I'd need to update the Policy to handle "http://foo" correctly, but it doesn't even check that ProtectionDomain anyway)
So where has my understanding gone wrong?
After doing some research on this, I think I have an answer. I could write a significantly longer answer, but I think I'll just cut to the chase here.
Each class loaded by a ClassLoader has a ProtectionDomain+CodeSource associated with it. These are somewhat coarse -- a CodeSource represents where a class came from, but it's not a pointer to an individual .class file or anything -- it's to a directory or a JAR. Thus two classes in the same JAR or directory will generally have identical permissions. Any class or script that has an identifiable ProtectionDomain+CodeSource can be whitelisted/blacklisted by your Policy.
The exception (kinda) to this is, of course, is AccessController.doPrivileged with Permission arguments. This lets you clamp down the permissions of a region of code. But that code could, in theory, call AccessController.doPrivileged with just the callback. That method signature means "don't check my entire call stack for permissions; just look up my ProtectionDomain+CodeSource in the Policy file and see what it says." So if you're running truly untrusted code, you better make sure that a. it has a ProtectionDomain+CodeSource different from your trusted application, and b. that your Policy is able to identify that code and grant it appropriately-limited permissions.
Here is one way for the example to run as intended, i.e., to effectively blacklist subsequent execution paths under the same domain. The core permission-intersection-based authorization model should still hold. The sample must be run with -Djava.system.class.loader=com.example.Test$AppClassLoader (this replacement system class loader is only needed in order to attain a working single-file example).
Obligatory disclaimer: While technically many things are possible, to the point of dynamically white-/blacklisting individual instances and beyond, they all involve additional context of some sort being introduced into the already non-trivial authorization process. Such approaches should be avoided whenever possible. The proper solution, sufficing in the vast majority of cases, as documented in the OP's answer's conclusion, is to package trusted code separately from untrusted (and, when manually managing class-to-domain mappings, ensuring that code bases of distinct trustworthiness are mapped to distinct domains), and assign appropriate permissions to the resulting domains.
package com.example;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test {
public static final class AppClassLoader extends URLClassLoader {
private static final URL[] CLASS_PATH;
private static final String SANDBOXABLE_DOMAIN_CLASS_NAME = "com.example.Test$SandboxableDomain";
static {
String[] paths = System.getProperty("java.class.path").split(File.pathSeparator);
List<URL> classPath = new ArrayList<>();
for (String path : paths) {
try {
classPath.add(new URL("file://" + path));
}
catch (MalformedURLException ex) {}
}
CLASS_PATH = classPath.toArray(new URL[0]);
}
private final Constructor<?> sandboxableDomainCtor;
{
try {
// ensure this loader defines SandboxableDomain so that normal code
// can safely / conveniently access it via class literal
Class<?> sandboxableDomainClass = loadClass(SANDBOXABLE_DOMAIN_CLASS_NAME, true);
sandboxableDomainCtor = sandboxableDomainClass.getConstructor(CodeSource.class,
PermissionCollection.class, ClassLoader.class);
}
catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
public AppClassLoader(ClassLoader parent) {
super(CLASS_PATH, parent);
}
#Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.startsWith("java") || name.startsWith("sun")) {
return super.loadClass(name, resolve);
}
Class<?> ret = findLoadedClass(name);
if (ret != null) {
return ret;
}
ProtectionDomain assignedDomain;
byte[] classData;
try {
URL classResource = getResource(name.replace(".", "/") + ".class");
CodeSource assignedCodeSource = new CodeSource(classResource, (Certificate[]) null);
classData = Files.readAllBytes(Paths.get(classResource.toURI()));
if (SANDBOXABLE_DOMAIN_CLASS_NAME.equals(name)) {
// loading the domain class itself; ensure _its own_ domain is fully privileged,
// so that it doesn't affect authorization
PermissionCollection perms = new Permissions();
perms.add(new AllPermission());
assignedDomain = new ProtectionDomain(assignedCodeSource, perms, this, null);
}
else {
// the per-class code source (URL) is unintentional; normally all classes under
// the same class path entry would share one
assignedDomain = (ProtectionDomain) sandboxableDomainCtor.newInstance(assignedCodeSource,
getPermissions(assignedCodeSource), this);
}
}
catch (NullPointerException | URISyntaxException | IOException | ReflectiveOperationException ex) {
throw new ClassNotFoundException(name);
}
ret = defineClass(name, classData, 0, classData.length, assignedDomain);
if (resolve) {
resolveClass(ret);
}
return ret;
}
}
public static final class SandboxableDomain extends ProtectionDomain {
private static final Permission DO_SANDBOXED_PERM = new RuntimePermission("com.example.doSandboxed");
private final ThreadLocal<Boolean> sandboxed = new InheritableThreadLocal<>();
public SandboxableDomain(CodeSource cs, PermissionCollection permissions, ClassLoader classLoader) {
super(cs, permissions, classLoader, null);
sandboxed.set(false);
}
// no equivalent doUnsandboxed here for escaping the sandbox on-demand;
// firstly because it's fishy; secondly because it would be impossible
// to distinguish a privileged caller based on permissions alone
public void doSandboxed(Runnable action) {
if (!sandboxed.get()) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(DO_SANDBOXED_PERM);
}
}
sandboxed.set(true);
try {
action.run();
}
finally {
sandboxed.set(false);
}
}
#Override
public boolean implies(Permission permission) {
if (sandboxed.get()) {
// static only (AppClassLoader only grants essentials like reading from own directory)
PermissionCollection perms = getPermissions();
return (perms == null) ? false : perms.implies(permission);
}
// static + policy
return super.implies(permission);
}
}
public static void main(String[] args) throws Exception {
initSecurity();
SandboxableDomain ownDomain = (SandboxableDomain) Test.class.getProtectionDomain();
System.out.println("Try unsandboxed"); // should succeed
untrusted();
System.out.println("---\n\nTry sandboxed"); // should fail
ownDomain.doSandboxed(Test::untrusted);
System.out.println("---\n\nTry unsandboxed from within a child thread"); // should succeed
new Thread(Test::untrusted).start();
Thread.sleep(1000);
System.out.println("---\n\nTry unsandboxed from within a sandboxed child thread"); // should fail
ownDomain.doSandboxed(() -> new Thread(Test::untrusted).start());
}
private static void initSecurity() throws Exception {
Path tempPolicyConfig = Files.createTempFile(null, null);
// self-grant AllPermission
Files.write(tempPolicyConfig,
Collections.singletonList(new StringBuilder("grant codebase \"")
.append(Test.class.getProtectionDomain().getCodeSource().getLocation()).append("\"{permission ")
.append(AllPermission.class.getName()).append(";};").toString()));
System.setProperty("java.security.policy", "=" + tempPolicyConfig.toString());
System.setSecurityManager(new SecurityManager());
Files.delete(tempPolicyConfig);
}
private static void untrusted() {
try {
untrusted0();
System.out.println("\tSucceeded");
}
catch (AccessControlException ex) {
System.out.println("\tFailed; try via doPrivileged");
try {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
untrusted0();
return null;
});
System.out.println("\t\tSucceeded");
}
catch (AccessControlException ex1) {
System.out.println("\t\tFailed anew");
}
}
}
private static void untrusted0() {
try {
Files.readAllBytes(Paths.get("build.gradle"));
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
What am I trying to achieve?
I am working on a java application that can be extended by additional jars that get integrated via ServiceLoader. These loaded extensions should run with some restrictions by the SecurityManager, of course simply to improve the security. As an example each Extension shall get one specific directory where it can store whatever, but access to any other file/folder should be restricted. The main application is trusted code and can therefore run without any restrictions. Furthermore the main application provides some api implementations for each extension that shall also run without restrictions. That means an extension mustn't access a file outside of its directory but when the extension is calling an api method that tries to access any other file, the access should be granted.
Question
How can I achieve the mentioned behaviour that only 'direct' calls from extension classes get restricted but not any code from the main application?
Running extensions in different threads/threadGroups might be a good solution anyway but since calls to the api might run under the same thread(group) it might not help to identify whether access should be restricted or not based only on the thread.
Example
I created a simplified test environment. On one hand there are 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 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);
}
}
So when I call extension.doSomethingRestricted(); it should result in a SecurityException but calling extension.doSameViaApi(api); should work just fine.
So both methods try to do the same but one does try to do it via the api call. The only approach I could think of is iterating through the call history and checking the classloaders to analyze whether the access request is based on trusted code or extension code. But I feel like this might be a nasty error-prone solution so maybe I missed some better approaches?
First ensure your "main" JAR's classes get to enjoy full privileges. Programmatically this may be accomplished as follows:
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's 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());
// ...
}
}
Alternatively, you will either have to a) modify the JRE distribution's java.policy (or specify a different configuration via the policy.url.n properties in java.security), or b) replace the implementation of the System ClassLoader with one that statically grants AllPermission to the ProtectionDomain associated with classes loaded from the "main" JAR.
Secondly, when loading Extensions from some JAR, employ a URLClassLoader subclass that a) manages extension-specific directories and b) includes a java.io.FilePermission in the permission collection being statically accorded to the protection domain mapped to its defined classes. Crude sample implementation (note that there is no persistent relationship between an extension JAR and a directory; also note that two Extensions originating from the same JAR (but loaded by different class loaders, of course) will get different directories):
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
}
Lastly, for unprivileged Extensions to be able to execute privileged operations via ExtensionApi, the latter's implementation must wrap privileged method (methods issuing SecurityManager::checkXXX requests) invocations within Privileged(Exception)Actions and pass them to AccessController::doPrivileged; e.g.:
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);
}
});
};
For details on the (proper) use of "privileged blocks", refer to the AccessController documentation and the "Secure Coding Guidelines for Java SE" document.
I have a small problem with implementing a own SocketImplFactory in Java.
My goal is to write a factory which offers me a way to close all open sockets with one simple method call. So I only want to have a kind of "proxy factory" which stores all the created sockets in a list. On this list I could perform all the actions I need.
I tried to implement it like this:
package java.net;
import java.io.IOException;
import java.net.SocketImpl;
import java.net.SocketImplFactory;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.io.IOUtils;
import com.crosscloud.applicationlayer.logger.CCLogger;
public class CCSocketImplFactory implements SocketImplFactory
{
private List<SocketImpl> _openSockets;
public CCSocketImplFactory()
{
_openSockets = new LinkedList<>();
}
#Override
public SocketImpl createSocketImpl()
{
SocketImpl impl = new SocksSocketImpl();
_openSockets.add(impl);
return impl;
}
public void closeAll()
{
_openSockets.forEach((socket)->
{
try
{
socket.close();
}
catch (Exception e)
{
logException(this, e);
}
});
}
public static CCSocketImplFactory register()
{
CCSocketImplFactory fact = new CCSocketImplFactory();
try
{
Socket.setSocketImplFactory(fact);
}
catch (IOException e)
{
logException(CCSocketImplFactory.class, e);
}
return fact;
}
The problem I have now is that I have to create the class in the package java.net because the class SocksSocketImpl(In my opinion this should be the standard type) is only visible in this package.
When I now want to run the code I get a SecurityException because the package name is probhibited.
Is there a workaround for my problem?
Thank you!
It appears that you are trying to use only one class from java.net There is no need to move you class tot hat package just to create an instance of it. I suggest using reflection instead.
Constructor cons = Class.forName("java.net.SocksSocketImpl").getDeclaredConstructor();
cons.setAccessible(true);
SocketImpl si = (SocketImpl) cons.newInstance();
However using SOCKS by default is likely to be a bad idea as it will change the default not just for your sockets, but all sockets even ones for internal use such as JMX or VisualVM.
What would be an alternative instead of always using SocksSocketImpl?
I also found this example which shows some extended possibilities of this method.
Finding out what network sockets are open in the current Java VM
Here in webcenter-driven ADF application we have a standard library called generic-site-resources-model.jar which contains file META-INF/service-definition.xml, which i wish to alter at one line, to add view resource' security permission, as in:
## -1189,7 +1189,7 ##
<resource-permission>
<resource-permission-impl>oracle.webcenter.security.auth.WebCenterResourcePermission</resource-permission-impl>
<resource-permission-target-id>resource_oracle_webcenter_siteresource_#scope#_navigation_#resource#</resource-permission-target-id>
- <resource-permission-action-list>manage,update</resource-permission-action-list>
+ <resource-permission-action-list>view,manage,update</resource-permission-action-list>
</resource-permission>
</permission-metadata>
</security-definition>
How this is can be possibly done without alterning weblogic domain containing this library itself, somehow configuring our application? Maybe some way to override the whole generic-site-resources-model.jar with application-shipped clone? Or (ideally) some way to replace the targeted resource permission? Or some custom way of taking control over resource loading in application?
It is possible to implement appending custom actions to specific resource type using initialization phase listener and a little bit code, without any overriding at all.
Here is how:
ViewControllerProject/src/META-INF/adf-settings.xml
<?xml version="1.0" encoding="UTF-8" ?>
<adf-settings xmlns="http://xmlns.oracle.com/adf/settings">
<adfc-controller-config xmlns="http://xmlns.oracle.com/adf/controller/config">
<lifecycle>
<phase-listener>
<listener-id>PortalInitializer</listener-id>
<class>com.otr.portal.initializer.PortalInitializer</class>
</phase-listener>
</lifecycle>
</adfc-controller-config>
</adf-settings>
com.otr.portal.initializer.PortalInitializer
package com.otr.portal.initializer;
import oracle.adf.controller.v2.lifecycle.Lifecycle;
import oracle.adf.controller.v2.lifecycle.PagePhaseEvent;
import oracle.adf.controller.v2.lifecycle.PagePhaseListener;
import oracle.webcenter.security.internal.common.SecurityUtil;
import oracle.webcenter.security.model.exception.SecExtensionNotFoundException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class PortalInitializer implements PagePhaseListener {
private static final Log log = LogFactory.getLog(PortalInitializer.class);
private boolean firstCall = true;
#Override
public void afterPhase(PagePhaseEvent pagePhaseEvent) {
if (pagePhaseEvent.getPhaseId() == Lifecycle.INIT_CONTEXT_ID) {
if (firstCall) {
setupViewNavigationResourcePermssion();
firstCall = false;
}
}
}
private void setupViewNavigationResourcePermssion() {
try {
SecurityUtil.getSecurityExtension("oracle.webcenter.siteresources.navigation").getExtensionPermMetadata().getResourcePermMetadata().getResourcePermActionsList().add("view");
} catch (SecExtensionNotFoundException e) {
log.error("Error adding view resource permission to navigation resource type", e);
}
}
#Override
public void beforePhase(PagePhaseEvent pagePhaseEvent) {
}
}