AccessController does not take ProtectionDomain into account [allowed]

Answer

I understood the answer to the question and answered below. I will leave the question unchanged for completeness.

Context

I am writing a Java system where the code is executed in very strict sandboxes. A request (consisting of one or several classes) should have access to only one folder (and subfolders and files contained in folders), during the whole time of its execution.

I am using the sandbox using SecurityManager and the new ClassLoader to execute each request. When defining classes in ClassLoader using defineClass I pass ProtectionDomain containing the read permissions of the files that must be granted.

Since not all objects in the call stack have the required privileges, the read actions in the request are performed in AccessController.doPrivileged(...) -block.

Problem

  • When I call AccessController.checkPermission(...) directly from the doPrivileged(...) block, it silently returns
  • When I call System.getSecurityManager().checkPermission(...) , which forwards the request to the AccessController , then the AccessController throws an exception.
  • AccessController ProtectionDomain seem to be lost when calling AccessController through the SecurityManager ?
  • Limited actions with the file (for example, creating java.io.FileReader ), directly call SecurityManager , not AccessController . How do I get an AccessController when called through the SecurityManager to respect the ProtectionDomain class that called doRestricted(...) -block?
  • Could it be that the SecurityManager itself does not have the necessary permissions? Thus, being AccessController call stack between privileged code, AccessController generate a privilege pool of any?

The following is a sample:

 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 result generated by the program, including the parts of the stack trace (PATH, replacing the long path name 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) 

An implementation of CloakAccessController.checkPermission(...) may 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 is basically bypassing the security restrictions for the thread that created it.


The contents of my java.policy is a MacOSX standard system file. I believe that it does not contain any non-standard changes.


Any suggestions?

Thanks!

+6
source share
2 answers

I feel a little awkward when answering my question, but I understood the correct solution and consider it correct only to add it here, so it is documented for the future if someone stumbles on this issue.

TL DR :

My custom SecurityManager did not have rights. Since it was in a callstack between the class calling doPrivileged(...) -block and AccessController , privilege intersection was not privilege at all.

Long version

The Java security model works as follows. When the AccessController checks to see if the class is allowed to call the method or not, it looks at the permissions from the top of the call towards the bottom. If each callstack entry has permission, the action is allowed.

Here is an arbitrary example where everything works fine:

 +-----------+-------------------+-----------------------+ | Callstack | Class permissions | Permissions in effect | +-----------+-------------------+-----------------------+ | Some | {Read,Write} | {Read} | | Other | {Read} | {Read} | +-----------+-------------------+-----------------------+ 

Now, in the case of my question, the lower layers in a callstack have no rights at all. Therefore, we get an image like this, where the query at the top, in fact, has no permissions.

 +-----------+-------------------+-----------------------+ | Callstack | Class permissions | Permissions in effect | +-----------+-------------------+-----------------------+ | Query | {Read} | {} | | Other | {} | {} | +-----------+-------------------+-----------------------+ 

This problem occurs when using doPrivileged(...) -block. This allows you to enable stop order search for completion in a record that invokes a privileged action:

 +-----------+-------------------+-----------------------+ | Callstack | Class permissions | Permissions in effect | +-----------+-------------------+-----------------------+ | Query | {Read} | {Read} | | Other | {} | {} | +-----------+-------------------+-----------------------+ 

That's why everything worked fine when I called AccessController.checkPermission(...) from the request. In the end, he had the correct permissions. (Un), fortunately, the Java API (for backward compatibility), always call the SecurityManager . In my case, SecurityManager did not have any privileges. Since it was essentially in a callstack between the request that invoked the privileged call and the AccessController , the net resulting permissions were not:

 +-----------------+-------------------+-----------------------+ | Callstack | Class permissions | Permissions in effect | +-----------------+-------------------+-----------------------+ | SecurityManager | {} | {} | | Query | {Read} | {Read} | | Other | {} | {} | +-----------------+-------------------+-----------------------+ 

Decision

The solution was to give the SecurityManager a basic set of permissions. As a result, the permissions granted by Query were really necessary:

 +-----------------+---------------------+-----------------------+ | Callstack | Class permissions | Permissions in effect | +-----------------+---------------------+-----------------------+ | SecurityManager | {Read,Write,Delete} | {Read} | | Query | {Read} | {Read} | | Other | {} | {} | +-----------------+---------------------+-----------------------+ 

Phew! That was pretty much! Hope this was helpful to someone there :)

+5
source

I think the problem here is how you provide ProtectionDomain for SecurityManager .

If you want to load classes yourself and be able to use SM, you must extend SecureClassLoader . This class provides a template method:

 protected Class defineClass(String name, byte[] b, int off, int len, CodeSource cs) 

In which you must provide a CodeSource for your class. And then another method:

  protected PermissionCollection getPermissions(CodeSource codesource) 

Which will return a PermissionCollection for classes from the given CodeSource . The way you should apply dynamic permissions to dynamically loaded classes.

+1
source

All Articles