Rolling your own SecurityManager

If you look in the source code for FileOutputStream, you’ll notice that, before a file is created and written to, a check is performed to see whether it is allowed to do so.

java.io.FileOutputStream.java:

    public FileOutputStream(String name, boolean append)
        throws FileNotFoundException
    {
	SecurityManager security = System.getSecurityManager();
	if (security != null) {
	    security.checkWrite(name);
	}
      . . .

If no SecurityManager is installed, System.getSecurityManager will return null and no security checks will be performed. We can see that happening by just creating an application that writes to a file.

Main.java:

import java.io.*;
 
public class Main {
   public static void main(String []args) throws Exception {
      FileOutputStream fos = new FileOutputStream("testfile.txt");
      fos.write("hey".getBytes());
      fos.close();
      System.out.println("testfile.txt successfully written!");
   }
}

Running this code with java Main will write the file testfile.txt. If you run this code with java -Djava.security.manager Main, notice that an exception is thrown:

 Exception in thread "main" java.security.AccessControlException: access denied (
java.io.FilePermission testfile.txt write)
        at java.security.AccessControlContext.checkPermission(AccessControlConte
xt.java:195)
        at java.security.AccessController.checkPermission(AccessController.java:
403)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
        at java.lang.SecurityManager.checkWrite(SecurityManager.java:958)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:96)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:62)
        at Main.main(Main.java:5)

We could write our own SecurityManager that allows to write to testfile.txt and restricts everything else to the security sandbox.

MySecurityManager.java:

import java.io.*;
 
public class MySecurityManager extends SecurityManager {
   public void checkWrite(String file) {
      if (!file.equals("testfile.txt")) {
         throw new SecurityException("Only allowed to write to file testfile.txt.");
      }
   }
}

Now we can use our new security manager by either specifying it at command line:

C:> java -Djava.security.manager=MySecurityManager Main
testfile.txt successfully written!

or by doing so programmatically:

import java.io.*;
 
public class Main {
   public static void main(String []args) throws Exception {
      System.setSecurityManager(new MySecurityManager()); 
 
      FileOutputStream fos = new FileOutputStream("testfile.txt");
      fos.write("hey".getBytes());
      fos.close();
      System.out.println("testfile.txt successfully written!");
   }
}

Running:

C:> java Main
testfile.txt successfully written!

A custom security manager can be useful in JDK1.1 applications. However, it becomes a real pain (lots of programming) when you want to specify security restrictions based on who is running the code or where the code is coming from.

From JDK1.2, you can use policy files to customize the security manager in a fine-grained fashion. Let’s use the original program:
Main.java:

import java.io.*;
 
public class Main {
   public static void main(String []args) throws Exception {
      FileOutputStream fos = new FileOutputStream("testfile.txt");
      fos.write("hey".getBytes());
      fos.close();
      System.out.println("testfile.txt successfully written!");
   }
}

and write a policy file that allows writing to only the file testfile.txt:
mypolicy.txt

grant {
   permission java.io.FilePermission "testfile.txt", "write";
};

Run the code and specify the policy file to be used at runtime:

C:> java -Djava.security.policy=mypolicy.txt -Djava.security.manager Main
testfile.txt successfully written!