Java Institute DreamsCity 2000
Welcome to DreamsCity
Return to Java Institute

USING SECURITYMANAGER

The basic SecurityManager architecture is simple. Throughout the 
JDK, the Java security team had to:

o Identify operations in the code that might pose a security 
  risk. 
  
o Find places in the code where checks could be placed to guard 
  these operations (but do so with the smallest number of 
  bottlenecks). 
   
o Throw an exception if the caller is not allowed to proceed.

This is how the SecurityManager class is used in the JDK source. 
For example, writing to a file on a user's local hard drive is an 
operation that needs to be secured. All file writes must at some 
point involve a FileOutputStream constructor. So you should expect 
to find a security checkpoint there:

file://from the JDK 1.3 source...
public FileOutputStream(String name, boolean append)
    throws FileNotFoundException
{
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkWrite(name);
    }
    file://go on and actually construct the object
    
This is a representative example of the security checks you find 
throughout the JDK. Before the actual work of the constructor 
begins, there is a check with the System class to see if a 
security manager is installed. If there is one, the constructor 
calls an appropriate method on the security manager, passing in 
any additional information that might influence the outcome. In 
the case of writing to a file, the relevant method is 
checkWrite() and the extra information is the name of a file.  

Because the hooks are already in place throughout the JDK, you 
can customize security by writing your own subclass of 
SecurityManager. Here is a simple example that only permits 
writing to a file named "temp" in the current directory.

import java.io.*;

class TempfileSecurityManager extends SecurityManager {
    
    public void checkWrite(String name) {
        if (!("temp".equals(name))) {
            throw new SecurityException("Access to '" + name + "' denied");
        }
    }
}

public class TestSecurityManager {

    public static void writeFile(String name) throws IOException {
        System.out.println("Writing to file " + name);
        FileOutputStream fos = new FileOutputStream(name);
        file://write something here...
        fos.close();
    }
    
    public static void main(String[] args) throws IOException {
        System.setSecurityManager(new TempfileSecurityManager());
        writeFile("temp");
        writeFile("other");
    }
}

The TestSecurityManager class installs a TempfileSecurityManager 
through the System.setSecurityManager method.  If you run 
TestSecurityManager, you should see that the writeFile method 
works fine when the file passed in is named "temp" but fails 
when "other" is passed in as the filename.

The TempfileSecurityManager is simple, but it has a major 
weakness. A particular capability is either granted to all the 
code running in the VM*, or not granted at all. Real systems 
need to assign different abilities to different pieces of code 
running in the same VM. For example, it would be nice to have 
a logging facility that could write to a logfile, but prevent 
any other code from writing to the local file system. The 
TempfileSecurityManager cannot handle this because it only looks 
at the filename being opened. A better implementation would also 
look at the context in which the file is opened.  

The SecurityManager base class provides the needed context 
information. The protected method getClassContext() returns an 
array of all the classes currently on the callstack. This enables 
a security manager to examine all the classes and decide if they 
should be trusted to perform the operation in question. For 
example, the following callstack array could be trusted:

Class java.io.FileOutputStream
Class com.develop.log.EventLog
etc.

But the following callstack array will probably not be trusted.

Class java.io.FileOutputStream
Class org.fierypit.EvilApplet
etc.

Of course, the perpetrators of evil will not normally indicate
their intent by naming a class "EvilApplet." So more work is 
necessary. For each class on the callstack, a security manager 
implementation could call getClassLoader to determine the 
class loader for the class. Given smart implementations of a 
class loader such as the JDK's java.net.URLClassLoader, it would 
then be possible to determine where on the web a class came from, 
and even check its digital signature.  

At this point implementing your own security manager is starting 
to sound like a lot of work. The checkWrite() method shown above 
is only one of several dozen methods that you might need to 
implement. Others cover operations such as accessing the network, 
accessing system properties, and invoking native code methods.  
For every one of these methods, a security manager needs to analyze 
the callstack returned by getClassContext. For each class on the 
stack, it might be necessary to collaborate with a class loader to 
determine the class's origin. Even worse, the code can be tricky 
to write and debug. In JDK 1.1, subclassing SecurityManager was 
the only way to do context-based security, and because it was so 
difficult, only a few people wrote security managers.  

Any comments? email to:
richard@dreamscity.net