Understanding Java Security and JAAS (part 1)

2011-05-09

This is the first article in a three-part series. This first part explains the Java's standard Security model (without JAAS), the second part will explain JAAS from a using-perspective, and the last part will explain how to develop your own JAAS login module to connect with a particular security domain.

What is Java Security anyway?

Java security (the way it was introduced in Java 2 as "Java 2 Security") is a mechanism to protect your environment from possible threats. For example when running a Java application, how do you know for sure it is not scanning your computer for interesting information? This question becomes even more interesting in a Java EE environment. The code running on the most vulnerable places in large enterprises is developed by developers like you and me, often by external developers. How does an administrator at a Bank installing my war file or ejb-jar know for sure that I have not implemented my own little e-banking system which scans files on the server or what have you? Many of you probably recall Applets and their security model called the sandbox. This made sure the applet stayed in a sandbox and could not get out (meaning to the file system for example). It had a little backdoor to the server that allowed it for example to read data from a database (sorry to speak in the paste-tense when talking about Applets). In fact the Java Security model is very much like this sandbox model, only far more configurable. By default, when running a Java program, it runs without security; it does not have a Security Manager enabled. To enable security just run a Java program with the -Djava.security.manager option.

java -Djava.security.manager
          evil.SeePrettyDuckPicturesWhenYouRunMe

Now this Java process is not allowed to do anything: read/write files, access the clipboard, show a model dialog, read mouse coordinates, reflection, create a classloader, access system properties etc etc. So without a security manager an application can do anything it wants, with security however it can hardly do anything and you have to explicitly grant permissions to an application. This is done because throughout the Java API implementation checks have been built-in:

public FileInputStream(File file) throws FileNotFoundException {
  String name = (file != null ? file.getPath() : null);
  SecurityManager security = System.getSecurityManager();
  if (security != null) {
    security.checkRead(name); // HERE IS THE CHECK
  }
  ...
  open(name);
}

On line 3 the FileInputStream obtains the Security Manager, which is null when not running with the -Djava.security.manager flag (hence the if-statement on line 4). When security is enabled, it does a check whether a read is allowed. This checkRead implementation is shown below

public void checkRead(String file) {
  checkPermission(new java.io.FilePermission(file, SecurityConstants.FILE_READ_ACTION));
}

So it checks whether there is a permission java.io.FilePermission to read that particular file. Now how do we grant permissions, and to what or whom do we grant permissions.

Granting permissions to what?

Now the first question when talking about granting permissions is to what or whom do you grant these permissions. In other words what is Java's Security model based on. The standard Java Security model allows you to grant permissions to code origin and/or the identity of the code owner. The code origin is based on the classpath location from where a class is loaded; the identity of the code owner is achieved using signed Jar files. JAAS adds to this equation the identity of the user executing the application (more on that in the second part in this series). When a class is loaded by a classloader it gets assigned a CodeSource, which is a holder for both the URL from which the class originates as well as any optional code signers. 

When the SecurityManager is asked to check a permission it checks if this permission is implied by all ProtectionDomain's of the classes on the call stack

Authorization is granted when all code on the call stack prior to the actual resource access, has the appropriate permission. The diagram above illustrates this. When a permission check is made, the permission is eventually checked by the AccessController. The AccessController will go through the AccessControllContext which consists of all the ProtectionDomains (abbreviated to pd in the diagram above) of the classes that are on the call stack. On each of these ProtectionDomains the implies method is invoked. This checks if the passed permission is implied by the ProtectionDomain of that class. Classes loaded by the bootstrap class loader (classes not on the classpath like the ones in the java package etc) are always granted permissions.

Granting permissions, How (part 1)?

How do you grant permissions? Well the answer to that is JVM vendor specific. We will take a look at how this is done by Oracle's implementation. Oracle's JVM uses policy files. These files can be created by hand or using the policytool (just run it, it makes things a lot clearer) Policy files can be created to grant permissions to applications. These policy files are loaded from disk in a specific order. First, the global policy file (java.policy) is loaded from $JAVA_HOME/lib/security (if present). After that, the policy file located in the user.home directory is loaded. You can specify policy files (and their load order) in the JAVA_HOME/lib/security/java.security file. Entries in the format of policy.url.n=URL (where n is the load order), specify which policy files to load. It is also possible to specify the policy file at execution time using the D-flag: -Djava.security.policy

grant codeBase "file:readfile.jar" {
  permission java.io.FilePermission "../resources/needstobereat.txt", "read, write, delete, execute";
}

A grant-entry describes granted permissions. The scope of these permissions can be limited by supplying a CodeBase (in the next part that you can further limit the grants to a user, using JAAS) In the example policy file, shown above, readfile.jar is a code source. The CodeBase is in the form of a URL. A code base that ends with a "/", implies all class files from a location. If you want to include jar-files from a directory "/*" should be used. When using "/-", all class files and jar files from a directory and, recursively, its sub directories should be used. A file name like the one used in the example, means only that jar file. The list of permissions are granting the CodeBase privileges. A permission entry is made for a specific Permission class. Some examples: ava.io.FilePermission, java.net.SocketPermission. Each of these entries is made up of a target and a list of actions. The possible values for the target and actions depend on the Permission class used. The target might be a file name, property name, an IP address. The action could for example be read, write, connect, delete.

Granting permissions, How (part 2) to signed jar files?

Above we granted permissions based on the location of a class (n that example inside a jar file). Now we will see how we can grant permissions to a particular "owner" of the code using its digital signature. The provider of the library needs to sign its jar file. For those that have not signed a jar file before, here are the steps. Generate a key pair

me@local:conf>keytool -genkey -alias DemoKEY -keyalg RSA
                      -keystore demo.ks

Enter keystore password:
Re-enter new password:
What is your first and last name?
  [Unknown]:  localhost
What is the name of your organizational unit?
  [Unknown]:  Borrow and Fly
What is the name of your organization?
  [Unknown]:  Highview Inc
What is the name of your City or Locality?
  [Unknown]:  Paris
What is the name of your State or Province?
  [Unknown]:  75
What is the two-letter country code for this unit?
  [Unknown]:  fr
Is CN=localhost, OU=Borrow and Fly, O=Highview Inc, L=Paris, ST=75, C=fr correct?
  [no]:  yes

Enter key password for
        (RETURN if same as keystore password):

Then you can sign the jar file

jarsigner -keystore demo.ks -storepass changeit readfile.jar DemoKEY

So these where the steps for the provider of the Jar file, how can you as a user of that library grant permissions specifically to classes in the jar file? First you would need to receive the corresponding certificate from your provider. Then import this into a truststore (a key store with certificates you trust).

keytool -import -alias HighView -keystore resources/truststore.ks -file highview.cert

The certificate is placed in the truststore under the alias "HighView". Now, when granting permissions in the policy file, you need to first tell it which truststore(s) to use (line 1 below). You can then grant permissions referring to the certificate using its alias in the store (line 3). Note the keystore has a relative URL (relative to this policy file)

keystore "file:../resources/truststore.ks";

grant codeBase "file:readfile.jar" signedBy "HighView" {
  permission java.io.FilePermission
        "..\resources\HighViewApplication.txt",
        "read, write, delete, execute";
};

But the calling code does not have these permissions!

In the examples so far, classes in a particular code source were granted permissions. Recall from the sequence diagram shown earlier that the protection domains for all code on the call back is checked for the permission. So if we were to use classes from the highview library above, we would also need to have those permissions. That would mean all classes always need all permissions, which translates to no security. You can stop the implies call going through the call stack using a PrivilegedAction. A PrivilegedAction is a piece of code, enclosed in the run method (need closures in Java!), that executes from a security "root". Coding a PrivilegedAction implies that you do not care about how and by whom you were called, but from "here on" security should be checked.

PrivilegedAction priviledgedAction;
priviledgedAction = new PrivilegedAction() {
           public LibraryBook run() {
             FileInputStream fis = new FileInputStream(…);
             …
            return book;
           }  };
LibraryBook  book =  AccessController.doPrivileged(priviledgedAction);

The sequence is now

In this particular example the class of o1 does not require this permission. In the next part we will explore JAAS! Part 2, can be found here

This article does not necessarily reflect the technical opinion of EDC4IT, but purely of the writer. If you want to discuss about this content, please use thecontact ussection of the site