5.3. The Policy ClassThe 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).
A policy class is constructed as follows:
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:
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:
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 PolicyThe 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 Definitionpolicy.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:
The policy files are designed to map code sources to sets of permissions. For example, this entry: Class Definitiongrant 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 Definitiongrant [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 Definitionkeystore ".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"). Copyright © 2001 O'Reilly & Associates. All rights reserved. |
|