A Quick Tour of Java Access Control Features

We first look at some of the Java access control features in action with help of a number of examples, each example building upon the previous one. The source and configuration files for the example programs can be found in the srcjsbookch5 subdirectory of JSTK installation. The idea is to get a feel for these features, without getting overwhelmed by the details.

Let us start the discussion with DisplayFile.java of the first example. This file can be found within the ex1 subdirectory within srcjsbookch5, the directory for the first example. This program takes a filename as a command line argument and displays its contents on the screen.

Listing 5-1a. Displaying contents of a file
// File: ex1DisplayFile.java
import java.io.*;
public class DisplayFile {
  public void disp(String fileName) throws IOException {
    String line = null;
    BufferedReader br = new BufferedReader(new FileReader(fileName));
    while ((line = br.readLine()) != null)
      System.out.println(line);
  }
  public static void main(String[] args) throws IOException {
    (new DisplayFile()).disp(args[0]);
  }
}

Compile the source file DisplayFile.java to produce class file DisplayFile.class and create jar file df.jar.

C:ch5ex1>javac DisplayFile.java
C:ch5ex1>jar cvf df.jar DisplayFile.class
					

Now run this program with the source file DisplayFile.java itself as an argument. Of course, you can try any other file as an argumjent.

C:ch5ex1>java –cp df.jar DisplayFile DisplayFile.java
					

As expected, this command displays the contents of the source file DisplayFile.java.

Now run the previous command with a slight modification.

C:ch5ex1>java -Djava.security.manager -cp df.jar DisplayFile 
DisplayFile.java

The –Djava.security.manager flag installs the default security manager and enables access control as per the default access control rules. What happens? The program fails with an access control exception.

Exception in thread "main" java.security.AccessControlException: 
access denied (java.io.FilePermission DisplayFile.java read)
  at java.security.AccessControlContext.checkPermission 
  (AccessControlContext.java:270)
  at java.security.AccessController.checkPermission 
  (AccessController.java:401)
... more output skipped ...

It is easy to infer that the default security manager and access control rules do not allow the code in df.jar to read file DisplayFile.java.

What would happen if the class was loaded from a .class file and not from a .jar file? To find out, try this command:

C:ch5ex1>java -Djava.security.manager DisplayFile DisplayFile.java
					

You may be surprised to find that this command does display the contents of file DisplayFile.java. This is explained by the fact that the code in class files loaded from a directory has read access to all other files in that directory and its subdirectories by default.

Access Control Based on Origin of Code

How can we grant read access to the code in df.jar? Within the Java platform, this is done by either modifying the default access control rules in default policy files or writing program specific policy files. We talk more about these policy files later. For the time being, let us write a specific policy file, df.policy.

Listing 5-2a. Policy file df.policy for Example #2
// File: ex2df.policy
grant codeBase "file:${user.dir}/df.jar" {
  permission java.io.FilePermission " ${user.dir}${/}*", "read";
};

This policy file gives read permission to code in df.jar of the current directory on all files in the current directory. This file is part of the second example, and can be found in srcjsbookch5ex2 directory. This directory also includes the source files of the previous example for completeness.

Let us run the previous command so that the access rules in this file are added[1] to the default access rules.

[1] It is possible to override the default policy file by specifying –Djava.security.policy==df.policy. Notice the use of double equal sign for overriding.

C:ch5ex2>java -Djava.security.manager 
							-Djava.security.policy=df.policy -cp df.jar DisplayFile DisplayFile.java
						

This command should display the contents of DisplayFile.java successfully.

Congratulations! You just ran your first Java program with security manager enabled and your own access rules.

Try the same command on files in different directories. Does it work?

Access Control Based on Code Signer

Let us modify the earlier policy file df.plicy a bit to include specific permissions for signed code. You can find the modified file in srcjsbookch5ex3, the directory for the third example.

Listing 5-2b. Policy file df.policy for Example #3
//File: ex3df.policy
keystore "file:${user.dir}${/}test.ks";

grant codeBase "file:${user.dir}/dfs.jar"
  signedBy "pankaj" {
  permission java.io.FilePermission "${user.dir}${/}*", "read";
};

This file specifies an additional constraint on the code: the files in the current directory can be read by code in the dfs.jar file, and this jar file must be signed by the subject of the X.509 certificate stored in the keystore file test.ks under the key entry associated with alias "pankaj". Recall that a keystore key entry can store a certificate and, optionally, the private key associated with the certificate. For signature verification, only the certificate having the public key is needed.

Let us first create a keystore with a private key and the corresponding self-signed certificate using the utility keytool. We use this keystore to sign the df.jar to produce signed jar file dfs.jar using jarsigner tool:

C:ch5ex3>keytool -genkey -alias pankaj -keystore test.ks 
							-storepass changeit -keypass changeit -dname 
							"CN=Pankaj Kumar,OU=OVBU,O=HP,L=Santa Clara,ST=CA,C=US"
						

Note the distinguished name used to create the self-signed certificate: "CN=Pankaj Kumar,OU=OVBU,O=HP,L=Santa Clara,ST=CA,C=US". As we have seen in the chapter PKI with Java, if you do not specify the –dname option, keytool prompts you for different field values.

We can now sign df.jar to create the signed jar file dfs.jar using the command line utility jarsigner. jarsigner is a tool to sign jar files and comes bundled with J2SE SDK v1.4:

C:ch5ex3> jarsigner -keystore test.ks -storepass changeit 
							–signedjar dfs.jar df.jar pankaj
						

We are now ready to use the signed jar file dfs.jar and df.policy file together to run DisplayFile program.

C:ch5ex3>java -Djava.security.manager 
							-Djava.security.policy=df.policy -cp dfs.jar 
							DisplayFile DisplayFile.java
						

You should see the contents of DisplayFile.java on the screen.

Access Control Based on User

We saw how one can specify access control rules based on the origin of code or code signer. But what about access control based on the user, the one who is executing the program? If you tried these commands on a multi-user operating system, you must have logged-in to your OS account and the program executes as a process with your privileges. So, if the argument filename corresponds to a file that is not readable by you, then the program will fail irrespective of whether the security manager is installed or not, and whether appropriate Java permissions are granted or not.

Within the boundaries of the OS access control, it is possible to run segments of a java program on behalf of an authenticated user and specify access control rules based on the identity of this user. The user must log into the program, perhaps by specifying a username and a password. This user could be an existing operating system user or a user managed outside the operating system using some other user management system. These details can be controlled through specific configurations. The overall privileges of the Java program would still be determined by the privileges assigned to the operating system user who invokes the program. Java access control rules are applied to assign specific permissions to users who log into the program within this boundary.

This capability of user authentication and authorization was added to J2SE as JAAS (Java Authentication and Authorization Service) API and is often referred to as the JAAS security model. J2SE v1.4 onward, it has been made part of J2SE.

Let us enhance our DisplayFile.java program and associated policy file to illustrate user-based access control. Be patient, as this is significantly more complex than the earlier examples of this chapter.

Understanding the sources of complexity would perhaps help in better understanding specific steps:

  • You must select a user management system and carry out the necessary configuration steps.

  • You must write the code to accept username and password and interact with the underlying user management system for login validation.

  • You must setup policy files for the code that does the user validation and for the code corresponding to the main program separately. The former needs more privilege and cannot be user based.

For the user management system, we select one based on keystore and use the test.ks file as the repository for user information. Recall that a keystore can have multiple key entries, each entry representing a user with the specified distinguished name. Also, an entry within a keystore is uniquely identified by an alias and can be password protected.

The information about the user management system is specified in a login configuration file login.conf.

Listing 5-3. Login configuration file login.conf
//File: ex4login.conf
DF {
    com.sun.security.auth.module.KeyStoreLoginModule required
    keyStoreURL="file:test.ks";
};

Here DF is essentially an identifier to refer to a particular entry within the login configuration file. The name of the file having the login configuration, i.e., login.conf, is passed to the program by setting the system property java.security.auth.login.config and the particular entry is referenced within the program through the identifier string DF.

The next step is to write the code that (a) creates an object encapsulating the information about the user management system and the callback handler to prompt for username and password; (b) invokes the method to initiate login sequence; (c) sets up a special kind of object, known as action object, to launch the DisplayFile program; and (d) run the action code, passing information about the logged-in user. This code is in source file DisplayFileLauncher.java. You can find this and all other related files in the directory srcjsbookch5ex4.

Listing 5-4. Launching the program to display file contents
// File: ex4DisplayFileLauncher
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import java.security.PrivilegedExceptionAction;

public class DisplayFileLauncher {
  public static void main(String[] args) throws Exception {
    final String fileName = args[0];
    LoginContext lc = new LoginContext("DF", new DFCallbackHandler());
    lc.login();
    PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
      public Object run() throws Exception{
        DisplayFile df = new DisplayFile();
        df.disp(fileName);
        return null;
      }
    };
    Subject.doAs(lc.getSubject(), action);
  }
}

Note that this program uses the developer supplied callback handler DFCallbackHandler. Invocation of login() method on LoginContext object runs this handler to prompt the user for username and password and read user input. Let us look at the code in DFCallbackHandler.java.

Listing 5-5. Collecting username and password from user
//File: ex4DFCallbackHandler
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;

public class DFCallbackHandler implements CallbackHandler {
  public void handle(Callback[] cb) {
    try {
      for (int i = 0; i < cb.length; i++){
        if (cb[i] instanceof NameCallback){
          NameCallback nc = (NameCallback)cb[i];
          System.out.print(nc.getPrompt() + " ");
          System.out.flush();
          String name = new BufferedReader(
              new InputStreamReader(System.in)).readLine();
          nc.setName(name);
        } else if (cb[i] instanceof PasswordCallback){
          PasswordCallback pc = (PasswordCallback)cb[i];
          System.out.print(pc.getPrompt() + " ");
          System.out.flush();
          String pw = new BufferedReader(
              new InputStreamReader(System.in)).readLine();
          pc.setPassword(pw.toCharArray());
          pw = null;
        }
      }
    } catch (IOException ioe){
      System.out.println("ioe = " + ioe);
    }
  }
}

You will notice that the handle() method is invoked with an array of Callback objects and it is the responsibility of the code therein to examine the individual objects, determine their type and carry out appropriate processing. The above method handles NameCallback to prompt for username and PasswordCallback to prompt for password. The user input is read from standard input.

Let us create a separate jar file dfl.jar for classes in files DisplayFileLauncher.java and DFCallbackHandler.java.

C:ch5ex4>javac DisplayFileLauncher.java DFCallbackHandler.java
C:ch5ex4>jar cvf dfl.jar DisplayFileLauncher*.class 
							DFCallbackHandler.class
						

Notice the * in DisplayFileLauncher*.class. This is to ensure that the class file corresponding to the inner class defined in DisplayFileLauncher.java is also included in dfl.jar. File DisplayFile.class , as in the second and third examples, is in the jar file df.jar.

To summarize, we have two jar files: df.jar has the code that displays the contents of a file and dfl.jar has the code to let a user login as per a configured login mechanism and launch the class in df.jar. We want to set up a policy file so that anyone can execute code in dfl.jar but the code in df.jar can be executed only by authenticated users. We also want to assign specific permissions to the code in dfl.jar so that it can execute methods required for letting a user log-in. Modified policy file df.policy has such access rules.

Listing 5-2c. Policy file df.policy for Example #4
// File: ex4df.policy
grant codeBase "file:${user.dir}/df.jar"
  Principal javax.security.auth.x500.X500Principal
    "CN=Pankaj Kumar,OU=OVBU,O=HP,L=Santa Clara,ST=CA,C=US" {
  permission java.io.FilePermission "${user.dir}${/}*", "read";
};
grant codeBase "file:${user.dir}/dfl.jar" {
  permission javax.security.auth.AuthPermission "createLoginContext";
  permission javax.security.auth.AuthPermission "doAs";
  permission java.io.FilePermission "${user.dir}${/}*", "read";
};

This policy file essentially says that dfl.jar loaded from the current directory has permission to run the code for user login and running code associated with the logged-in user. It also has the permission to read files in the current directory. In contrast, the code in df.jar has the permission to read files in the current directory only if it is running on behalf of the user identified by distinguished name "CN=Pankaj Kumar,OU=OVBU,O=HP,L=Santa Clara,ST=CA,C=US". Recall that this is the same name we used to create a self-signed certificate in keystore test.ks with the alias "pankaj".

We are now ready to run the program.

C:>java -Djava.security.manager -Djava.security.policy=df4.policy 
							-Djava.security.auth.login.config=login.conf -cp df.jar;dfl.jar 
							DisplayFileLauncher DisplayFile.java
Keystore alias:  pankaj
Keystore password:  changeit
Private key password (optional):  changeit
						

As with the earlier examples, this should display the contents of the file DisplayFile.java.

This concludes our brief tour of Java access control features. We are now ready to dive into a more complete discussion of specific features.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.179.161