Context
I am writing a Java system where code is executed in very strict sandboxes. A query (consisting of one or more classes) should only be allowed access to exactly one folder (and subfolders and files contained within the folders) for the duration of its execution.
I enforce sandboxing by using a SecurityManager
, and a new ClassLoader
per query execution. When defining the classes in the ClassLoader
using defineClass
, I pass along a ProtectionDomain
containing the file read permissions that should be granted.
As not all objets on the call stack have the required privileges, the read actions in the query are run within an AccessController.doPrivileged(...)
-block.
Problem
- When I call
AccessController.checkPermission(...)
directly from within thedoPrivileged(...)
block it returns silently - When I call
System.getSecurityManager().checkPermission(...)
, which forwards the request to theAccessController
, then theAccessController
throws an exception. - The
ProtectionDomain
seems to get lost when callingAccessController
through theSecurityManager
? - Restricted file actions (like creating a
java.io.FileReader
), directly call theSecurityManager
rather than theAccessController
. How do I get theAccessController
, when called through theSecurityManager
, to respect theProtectionDomain
of the class that invoked thedoRestricted(...)
-block? - Could it be that the
SecurityManager
itself doesn't have the required permissions? Thereby, by being sandwiched into the call-stack between the privileged code, and theAccessController
generates a privilege union of none?
Below follows a sample section:
AccessController.doPrivileged(new PrivilegedAction<QueryResult>() {
public QueryResult run() {
String location = folderName + "/hello";
FilePermission p = new FilePermission(location, "read");
try {
AccessController.checkPermission(p); // Doesn't raise an exception
System.out.println("AccessController says OK");
System.getSecurityManager().checkPermission(p); // Raises AccessControlException
System.out.println("SecurityManager says OK");
} catch (AccessControlException e) {
System.out.println("### Not allowed to read");
}
return null;
}
});
The output generated by the program, including parts of the stack trace (PATH substituting the long pathname used):
AccessController says OK
Asked for permission: ("java.io.FilePermission" "PATH/hello" "read")
java.security.AccessControlException: access denied ("java.io.FilePermission" "PATH/hello" "read")
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:366)
at java.security.AccessController.checkPermission(AccessController.java:560)
at com.aircloak.cloak.security.CloakSecurityManager.checkPermission(CloakSecurityManager.java:40)
at com.dummycorp.queries.ValidQuery$1.run(ValidQuery.java:23)
at com.dummycorp.queries.ValidQuery$1.run(ValidQuery.java:1)
at java.security.AccessController.doPrivileged(Native Method)
at com.dummycorp.queries.ValidQuery.run(ValidQuery.java:16)
at com.aircloak.cloak.security.CloakSecurityManagerTest$1.run(CloakSecurityManagerTest.java:75)
at java.lang.Thread.run(Thread.java:722)
The CloakAccessController.checkPermission(...)
implementation might also be of interest. It looks like this:
public void checkPermission(Permission perm) {
if (Thread.currentThread().getId() == this.masterThread) {
return;
} else {
System.out.println("Asked for permission: "+perm.toString());
}
AccessController.checkPermission(perm);
}
What it does it mainly bypassing the security restrictions for the thread that created it.
The contents of my java.policy file are those of a standard MacOSX system. I believe that it doesn't contain any non-standard changes.
defineClass(name, binary, protection-domain)
. The protection-domains are created by the class loader as well, and contain exactly the set of permissions I want that particular class to have. – Carrie