What is a SecurityManager?

The SecurityManager contains a number of methods that check whether a certain operation is permitted, eg. checkRead, checkWrite, checkPropertyAccess. For example, when you are instantiate an object of the class FileInputStream to open a file, the security manager will be consulted to see if you allowed to do so:

   public FileInputStream(String name) throws FileNotFoundException {
      SecurityManager security = System.getSecurityManager();
      if (security != null) {
         security.checkRead(name);
      }
      fd = new FileDescriptor();
      open(name);
   }

For applications, no security manager is installed and hence the checkRead in the FileInputStream constructor will not be executed and the file is successfully opened. The following program shows that no security manager is installed and thus will output null:

 
public class Main
{
   public static void main(String []args) {
      SecurityManager security = System.getSecurityManager();
      System.out.println(security);
   }
}

To make sure your program is using a default security manager, use the switch -Djava.security.manager when your run your program or do it programmatically by calling System.setSecurityManager.

c:> java Main
null
 
c:> java -Djava.security.manager Main
java.lang.SecurityManager@7032dd38

Look at the output of the following program with and without a security manager:

public class Main
{
   public static void main(String []args) {
      System.out.println(System.getProperty("java.version"));
      System.out.println(System.getProperty("test"));
   }
}

The program tries to output the System properties “java.version” and “test” (a dummy one).

C:> java Main
1.2.2
null

C:> java -Djava.security.manager Main
1.2.2
Exception in thread "main" java.security.AccessControlException: access denied
java.util.PropertyPermission test read)
        at java.security.AccessControlContext.checkPermission(AccessControlCont
xt.java:195)
        at java.security.AccessController.checkPermission(AccessController.java
403)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
        at java.lang.SecurityManager.checkPropertyAccess(SecurityManager.java:1
43)
        at java.lang.System.getProperty(System.java:539)
        at Main.main(Main.java:5)

Notice that without a security manager installed, it runs as we wish. With a default security manager installed, it correctly returns the System property “java.version” but throws an AccessControlException when trying to read the property “test”. Why is that?

First, let’s look at the getProperty method in the System.java source file:

   public static String getProperty(String key) {
      if (security != null) {
         security.checkPropertyAccess(key);
      }
      return props.getProperty(key);
   }

It checks for a security manager and if one exists, it uses checkPropertyAccess to determine whether it is allowed to read the property key that is passed in as an argument. Permissions are granted through policy configuration files. There is a system-wide policy file and a single user policy file. The system-wide policy file is located at JAVA_HOME/lib/security/java.policy and the user policy file can be found at USER_HOME/.java.policy (in my case, I found it in C:WINDOWS). The locations of these two policy files are specified in the file JAVA_HOME/lib/security/java.security (in my case, I found it in C:jdk1.2.2jrelibsecurity). When starting up, the system policy is loaded first and the user policy is added to it. If neither of these policy files are present, a built-in policy is used, which is the same as the sandbox policy.

Now, if you look at a part of the system wide policy file java.policy:

      . . .
 
	// allows anyone to listen on un-privileged ports
	permission java.net.SocketPermission "localhost:1024-", "listen";

	// "standard" properies that can be read by anyone

	permission java.util.PropertyPermission "java.version", "read";
	permission java.util.PropertyPermission "java.vendor", "read";
	permission java.util.PropertyPermission "java.vendor.url", "read";
   
      . . .

To find out how these policy files are structured, look at the document Permissions in the Java 2 SDK or Permissions and Security Policy.

Notice that a permission is granted to read the System property “java.version”. That’s why we can read in this property even with a security manager installed. No permission has been given to read the property “test”, so this results in an Exception.

You can make the JVM use additional policy configuration files with a command-line argument:

   java -Djava.security.manager -Djava.security.policy=mypolicyfile Main

For example, we could add an extra permission to read the property “test”.

mypolicyfile:

grant {
   permission java.util.PropertyPermission "test", "read";
};

result:

C:>java -Djava.security.manager -Djava.security.policy=mypolicyfile Main
1.2.2
null

mypolicyfile can be either made with a simple text editor or with the graphical JDK tool policytool.

If you want the JVM use only your policy file and not the system-wide nor the user policy, use a double equal sign:

C:>java -Djava.security.manager -Djava.security.policy==mypolicyfile Main

This would result in an exception when trying to read the system property “java.version”.

The policy file can be highly customized. It should be structured as follows:

   grant signedBy "signer_name", codeBase "URL", 
      principal principal_class_name "principal_name",
      principal principal_class_name "principal_name",
      ... {
      permission permission_class_name "target_name", "action", 
         signedBy "signer_name";
      permission permission_class_name "target_name", "action", 
         signedBy "signer_name";
      ...
   };

For more information on policy file syntax look at the document Default Policy Implementation and Policy File Syntax

( Note: the principal entry is useful for assigning permission based on who is running the code as opposed to where the code is coming from or who signed it, look at the JAAS category for more information. )

For example, to only grant permissions to our Main class located in C: we would write:

grant codeBase "file:C:/" {
   permission java.util.PropertyPermission "test", "read";
};

Running our Main in C:test would result in an AccessControlException: Access Denied.