In this chapter, we're going to examine Java's access controller. While the security manager is the key to the security model of the Java sandbox, the access controller is the mechanism that the security manager actually uses to enforce its protections. The security manager may be king, but the access controller is really the power behind the throne.
The access controller is actually somewhat redundant. The purpose of the security manager is to determine whether or not particular operations should be permitted or denied. The purpose of the access controller is really the same: it decides whether access to a critical system resource should be permitted or denied. Hence, the access controller can do everything the security manager can do.
The reason there is both an access controller and a security manager is mainly historical: the access controller is only available in Java 1.2 and subsequent releases. Before the access controller existed, the security manager had to rely on its internal logic to determine the security policy that should be in effect, and changing the security policy required changing the security manager itself. Starting with 1.2, the security manager is able to defer these decisions to the access controller. Since the security policy enforced by the access controller can be specified in a file, this allows a much more flexible mechanism for determining policies. The access controller also gives us a much simpler method of granting fine-grained, specific permissions to specific classes. That process was theoretically possibly with the security manager alone, but it was simply too hard to implement.
1.2 is now Java 2.
But the large body of pre-1.2 Java programs dictates that the primary interface to system security--that is, the security manager--cannot change; otherwise, existing code that implements or depends on the security manager would become obsolete. Hence, the introduction of the access controller did not replace the security manager--it supplemented the security manager. This relationship is illustrated in Figure 5-1. Typically, an operation proceeds through the program code into the Java API, through the security manager to the access controller, and finally into the operating system. In certain cases, however, the security manager may bypass the access controller. And native libraries are still outside the domain of either the security manager or the access controller (although the ability to load those libraries may be restricted, as we've seen).
The access controller plays another role in this picture as well: it allows a program to determine that access to any arbitrary resource must require explicit permission. A program that accesses employee payroll information from a corporate database may want to assign permission to each employee to access only his or her own data in the database. While global access to the database may be controlled by the security manager (e.g., because it's necessary to open a file or socket to get to the database), access to the particular record can be controlled by the access controller alone. Because the access controller (unlike the security manager) is easily extensible, it is simple for a program to use the same security framework to access both the general resources of the operating system and any specific resources of the program.
Keep in mind, however, that the core Java API never calls the access controller unless a security manager is in place, and that the access controller will not be initialized until it is called. If you call it directly for a program-specific resource, it will initialize itself automatically. But by default, Java applications run without a security manager will not use the access controller. We'll discuss later in this chapter and in Chapter 6, "Implementing Security Policies" the use of the -Djava.security.manager flag to install a security manager into the application, which will initialize the access controller for us.
In this chapter, then, we'll look into the access controller, including its implementation and its use. This will give us the necessary knowledge of how the access controller works, how it can be used to change the security of a Java program without requiring code changes, and how it is used to implement the security manager. This last point will also give us the necessary information to write our own security manager. In Java 1.2, there are only rare cases where such a task is necessary.
The access controller is built upon four concepts:
Code sources: An encapsulation of the location from which certain Java classes were obtained
Permissions: An encapsulation of a request to perform a particular operation
Policies: An encapsulation of all the specific permissions that should be granted to specific code sources
Protectiondomains: An encapsulation of a particular code source and the permissions granted to that code source
Before we examine the access controller itself, we'll look each of these building blocks.
When we examined class loaders, we introduced the notion of a code source. A code source is a simple object that merely reflects the URL from which a class was loaded and the keys (if any) that were used to sign that class. The SecureClassLoader class (and its subclasses) are responsible for creating and manipulating these code source objects.
The CodeSource class (java.security.CodeSource) has a few interesting methods:
Create a code source object for code that has been loaded from the specified URL. The optional array of certificates is the array of public keys that have signed the code that was loaded from this URL. These certificates are typically obtained from reading a signed JAR file, which we'll show in Chapter 12, "Digital Signatures"; if the code was not signed, this argument should be null.
Two code source objects are considered equal if they were loaded from the same URL (that is, the equals() method for the URL of the objects returns true) and the array of certificates is equal (that is, a comparison of each certificate in the array of certificates will return true).
Return the URL that was passed to the constructor of this object.
Return a copy of the array of certificates that were used to construct this code source object. The original certificates are not returned so that they cannot be modified accidentally (or maliciously).
Determine if the code source implies the parameter according to the rules of the Permission class (see later in this chapter). One code source implies another if it contains all the certificates of the parameter and if the URL of the parameter is implied by the URL of the target.
That's the extent of the CodeSource class. When we discussed the SecureClassLoader class in Chapter 3, "Java Class Loaders", we showed that the defineClass() method expected a CodeSource object as a parameter. It's up to the implementor of the SecureClassLoader to provide this object. In the URLClassLoader class, this happens automatically, based on the URL where the class was actually located. By default, each URL in the URLClassLoader class will have its own distinct code source object, so all classes that are loaded from that URL are considered to have the same code source. This does not have to be the case (though it's much simpler); you could have a different code source for each class, or even different code sources for sets of classes from the same URL (although we question the wisdom of doing that).
In Chapter 3, "Java Class Loaders", we didn't bother to create code sources, which meant that our classes were assigned to a default code source. For the time being, we'll create code sources in an URL-based class loader simply based on the URL we used to construct the class loader; these classes will all be unsigned classes as a result. In Chapter 12, we'll show how you can get the correct certificates with which to construct a code source object for a signed class.
Copyright © 2001 O'Reilly & Associates. All rights reserved.
|This HTML Help has been published using the chm2web software.