Book Home Java Security Search this book

5.3. The Policy Class

The third building block for the access controller is the facility to specify which permissions should apply to which code sources. We call this global set of permissions the security policy; it is encapsulated by the Policy class (java.security.Policy).

public abstract class Policy figure

Establish the security policy for a Java program. The policy encapsulates a mapping between code sources and permission objects in such a way that classes loaded from particular locations or signed by specific individuals have the set of specified permissions.

A policy class is constructed as follows:

public Policy() figure

Create a policy class. The constructor should initialize the policy object according to its internal rules (e.g., by reading the java.policy file, as we'll describe later).

Like the security manager, only a single instance of the policy class can be installed in the virtual machine at any time. However, unlike the security manager, the actual instance of the policy class can be replaced. These two methods install and retrieve the policy:

public static Policy getPolicy() figure

Return the currently installed policy object.

public static void setPolicy(Policy p) figure

Install the given policy object, replacing whatever policy object was previously installed.

Getting and setting the policy object requires going through the checkProperty() method of the security manager. By default, this succeeds only if you already have been granted a security permission with the name of getPolicy or setPolicy (as appropriate). There's a bootstrapping issue involved when setting the policy, since granting permissions requires the policy to have been set. Hence, the initial policy is typically set by a class in the core API, as those classes always have permission to perform any operation.

There are two other methods in the Policy class:

public abstract Permissions getPermissions(CodeSource cs) figure

Create a permissions object that contains the set of permissions that should be granted to classes that came from the given code source (i.e., loaded from the code source's URL and signed by the keys in the code source).

public abstract void refresh() figure

Refresh the policy object. For example, if the initial policy came from a file, re-read the file and install a new policy object based on the (presumably changed) information from the file.

In programmatic terms, writing a policy class involves implementing these methods. The default policy class is provided by the PolicyFile class (sun.security.provider.PolicyFile), which constructs permissions based on information found in a file on the user's local disk (a process we're just about to examine).

Unfortunately, the PolicyFile class that parses that file and builds up the set of permissions is a file in the sun package class; it is not accessible to us as programmers. Hence, while it's possible to write your own Policy class, it is a fairly involved process. You might want to write your own Policy class if you want to define a set of permissions through some other mechanism than a URL (e.g., loading the permissions via a policy server database). That implementation is fairly straightforward: you need only provide a mechanism to map code sources to a set of permissions. Then, for each code source, construct each of the individual permission objects and aggregate them into a permissions object to be returned by the getPermissions() method.

5.3.1. The Default Policy

The Policy and PolicyFile classes give system administrators or end users the ability to define in a file a security policy for any Java program; this allows changes to the security model for the program without modifying the program's code. The policy that you can specify in this file is extremely flexible, since it's based on the permission model we examined earlier. If you want a Java program to be able to read a single directory, you can specify the appropriate file permission in the policy file. If you want a Java program to be able to connect to particular hosts on the network, you can specify the appropriate socket permissions in the policy file. And if you want a Java program to be able to administer payroll records, you can specify the appropriate payroll permissions in the policy file.

By default, the policy for a Java program is read from two locations, but this is controlled by the system security file. This file is a set of properties that apply to the security package in general; it is named $JAVAHOME/lib/security/java.security.

In terms of the Policy class, here are the relevant entries in the java.security file:

Class Definition

policy.provider=sun.security.provider.PolicyFile
policy.expandProperties=true
policy.allowSystemProperty=true
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy

The first of these properties defines the class that should be instantiated to provide the initial instance of the Policy class: in this case, the PolicyFile class (which implements the behavior we're now describing). Here's the algorithm that the PolicyFile class uses to read in policy files. The entire set of entries in the resulting policy is composed of all the specific entries read from all of the following files:

  1. If the policy.allowSystemProperty property in the java.security file is set to true (which it is by default), then the first file to be read is a file specified on the command line with the -Djava.security.policy argument, which must be used with the -Djava.security.manager option. For example, the following command would first load the policy file from /globalfiles/java.policy:

    Class Definition

    piccolo% java -Djava.security.manager \
              -Djava.security.policy=/globalfiles/java.policy Cat /etc/passwd
    

    If the policy.allowSystemProperty property is set to false, then the -Djava.security.manager file will be ignored. On the other hand, if this property is set to true and the filename given as the -Djava.security.manager argument begins with an equals sign:

    Class Definition

    piccolo% java -Djava.security.manager \
              -Djava.security.policy==/globalfiles/java.policy Cat /etc/passwd
    

    then the given file is the only policy file that will be read (and hence the only file that will define permissions).

    Note that you may also specify the -Djava.security.manager flag with no additional arguments, in which case the policy files from the java.security file (see the next step) are used and no additional files are consulted:

    Class Definition

    piccolo% java -Djava.security.manager Cat /etc/passwd
    

    This last example is the typical usage. Any of these examples set up the default sandbox for us in Java 1.2--the parameters of this sandbox are defined by the entries in the policy file.

  2. Next, the PolicyFile class looks for properties of the form policy.url.n where n is an integer starting with 1. As it finds each property, it reads in the policy from the given URL; in the default set of properties we listed above, this means that the first URL to be read is the java.policy file in the $JAVAHOME/lib/security directory and the second URL to be read is the .java.policy file in the user's home directory. You may specify as many or as few of these URLs as desired, but they must be numbered consecutively starting with 1.

  3. If no files have been loaded (because there was no -Djava.security.policy argument and there were no policy.url properties), then an internal static set of permissions is loaded (which is the same set of permissions defined by the default java.policy file we list below).

The policy files are designed to map code sources to sets of permissions. For example, this entry:

Class Definition

grant codeBase http://www.xyz.com/ {
	permission java.io.FilePermssion "${user.home}${/}docs${/}-",
											             "read, write, delete";
};

means that any code loaded from the top-level directory of www.xyz.com is granted permission to use any files under the user's docs directory. The code base in this case is used to construct a code source with no public keys.

The above example is one case of a policy entry, also called a grant entry, and a policy file is a collection of policy entries. Each entry is specific to one code source and should list all the permissions for that code source--but a single policy file can have several entries and thus work effectively for code that originated from multiple sources. The syntax of a policy entry is as follows:

Class Definition

grant [signedBy <signer>] [, codeBase <code source>] {
		permission <class> [<name> [, <action list>]];
		...
		permission <class> [<name> [, <action list>]];
};

As indicated by the bracket syntax, the signedBy and codeBase entries are optional. If both are missing, the list of permissions applies to a class with any code source. The signer entry should be a name that matches an entry in the system's key management system--a concept we'll explore in Chapter 11, "Key Management". The codeBase should be the URL that applies to the location from which the classes were loaded--including a file-based or HTTP-based URL.

Note that omitting the signedBy and codeBase fields in the policy file means that the given permissions should apply to all code sources. It does not mean that the listed permissions should apply only to classes that had a code source with no URL and no public key. This point about the code source is important: permissions given within the policy file apply only to classes that have a code source. Classes that are loaded by the primordial class loader do not have a code source--these classes are given permission to perform any operation. Hence, the Java API itself has no restrictions placed upon what operations it may perform.

The permissions themselves should have the fully package-qualified class name for the permission--including any permission classes (like the XYZPayrollPermission class) that you may have defined for your own application. The name will be used to construct the permission, along with the action list (if present). An internal (private) method of the PolicyFile class is used to construct the permission object; this method expects to find a constructor that takes both a name and an action. If the action is not present, then null will be passed to the constructor. This requirement forces you to include a constructor with both arguments in all your permission classes, including those that are extensions of the BasicPermission class.

Here's the default policy file that comes with the Java 1.2. This is the system security file (i.e., the one loaded from $JAVAHOME/lib/security/java.policy); there is no default file for each user. This is also the set that will be loaded when no policy files are found:

Class Definition

// Standard extensions get all permissions by default
grant codeBase "file:${java.home}/lib/ext/" {
	permission java.security.AllPermission;
};

// default permissions granted to all domains
grant { 
	// 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";
	permission java.util.PropertyPermission
								"java.class.version", "read";
	permission java.util.PropertyPermission "os.name", "read";
	permission java.util.PropertyPermission "os.version", "read";
	permission java.util.PropertyPermission "os.arch", "read";
	permission java.util.PropertyPermission "file.separator", "read";
	permission java.util.PropertyPermission "path.separator", "read";
	permission java.util.PropertyPermission "line.separator", "read";

	permission java.util.PropertyPermission
								"java.specification.version", "read";
	permission java.util.PropertyPermission
								"java.specification.vendor", "read";
	permission java.util.PropertyPermission
								"java.specification.name", "read";

	permission java.util.PropertyPermission
							"java.vm.specification.version", "read";
	permission java.util.PropertyPermission
							"java.vm.specification.vendor", "read";
	permission java.util.PropertyPermission
							"java.vm.specification.name", "read";
	permission java.util.PropertyPermission "java.vm.version", "read";
	permission java.util.PropertyPermission "java.vm.vendor", "read";
	permission java.util.PropertyPermission "java.vm.name", "read";
	permission java.lang.RuntimePermission "stopThread";
};

When you use this policy file, then, all classes that are loaded from the Java extensions directory will be granted all permissions. All other non-system classes will have read access to the system properties listed as well as being able to listen on a socket with a port number of 1024 or greater (which means that the class will be able to create a server socket on an unprivileged port). All other classes will also be able to call the stop() method on a thread.

A policy file may contain an additional entry:

Class Definition

keystore ".keystore";

This entry specifies the name of the URL that will be used to process the keystore in which public keys for the signers listed in the policy file should be found. This entry is missing from the default policy file, as it does not contain any entries that are signed. The name of this file is relative to the URL that was used to load the file; if the policy.url property was file:/${user.home}/.java.policy, the URL to load the keystore will be file:/${user.home}/.keystore. The keystore entry may be an absolute URL if desired.

Policy files may be constructed by hand, or you may use the policytool application that comes with the JDK to administer those files (see Appendix A, "Security Tools").



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.