The Artima Developer Community
Sponsored Link

Chapter 3 of Inside the Java Virtual Machine
Security
by Bill Venners

<<  Page 6 of 17  >>

Advertisement

The Security Manager and the Java API

The first three prongs of Java's security model -- the class loader architecture, class file verifier, and safety features built into Java -- all work together to achieve a common goal: to protect the internal integrity of a Java virtual machine instance and the application it is running from malicious or buggy code it may load. By contrast, the fourth prong of the security model, the security manager, is geared towards protecting assets external to the virtual machine from malicious or buggy code running within the virtual machine. The security manager is a single object that serves as the central point for access control -- the controlling of access to external assets -- within a running Java virtual machine.

The security manager defines the outer boundaries of the sandbox. Because it is customizable, the security manager allows a custom security policy to be established for an application. The Java API enforces the custom security policy by asking the security manager for permission before it takes any action that is potentially unsafe. To ask the security manager for permission, the methods of the Java API invoke "check methods" on the security manager object. These methods are called check methods because their names all begin with the substring "check." For example, the security manager's checkRead() method determines whether or not a thread is allowed to read to a specified file. The checkWrite() method determines whether or not a thread is allowed to write to a specified file. The implementation of these methods is what defines the custom security policy of the application.

Because the Java API always checks with the security manager before it performs a potentially unsafe action, the Java API will not perform any action forbidden under the security policy established by the security manager. If the security manager forbids an action, the Java API won't perform that action.

When a Java application starts, it has no security manager, but the application can install one at its option by passing a reference to an instance of java.lang.SecurityManager or one of its subclasses to setSecurityManager(), a static method of class java.lang.System. If an application does not install a security manager, there are no restrictions placed on any activities requested of the Java API--the Java API will do whatever it is asked. (This is why Java applications, by default, do not have any security restrictions such as those that limit the activities of untrusted applets.) If the application does install a security manager, then in 1.0 or 1.1 that security manager will be in charge for the entire remainder of the lifetime of that application. It can't be replaced, extended, or changed. From that point on, the Java API will only fulfill those requests that are sanctioned by the security manager. In 1.2, however, the currently installed security manager can be replaced by code that has permission to replace it by invoking System.setSecurityManager() with a reference to a different security manager object.

In general, a "check" method of the security manager throws a security exception if the checked upon activity is forbidden, and simply returns if the activity is permitted. Therefore, the procedure a Java API method generally follows when it is about to perform a potentially unsafe activity involves two steps. First, the Java API code checks whether a security manager has been installed. If not, it skips step two and goes ahead with the potentially unsafe action. Otherwise, as step two, it calls the appropriate "check" method in the security manager. If the action is forbidden, the "check" method will throw a security exception, which will cause the Java API method to immediately abort. The potentially unsafe action will never be taken. If, on the other hand, the action is permitted, the "check" method will simply return. In this case, the Java API method carries on and performs the potentially unsafe action.

As mentioned earlier in this chapter, the security manager is responsible for two things: for specifying a security policy and for enforcing that policy. The security policy, which states what kind of code will be allowed to take what kind of actions, is defined by the code of the security manager's check methods. The policy is enforced by the behavior of the check methods when they are invoked.

Prior to 1.2, java.lang.SecurityManager was an abstract class. To establish a custom security policy in 1.0 or 1.1, you had to write your own security manager by subclassing SecurityManager and implementing its check methods. Your application would instantiate and install the security manager, which from that point forward for the remainder of the life of the application would enforce the security policy you defined in the code of its check methods.

Although the customizability of the security manager was one of the greatest strengths of Java's security model, it was also a potential point of weakness. Writing a security manager is a complicated and error prone task. Any mistakes made when implementing the check methods of a security manager could potentially translate into security holes at runtime. To help make it easier and less error prone for developers and end-users to establish fine-grained security policies based on signed code, the java.lang.SecurityManager class in the version 1.2 is a concrete class that provides a default implementation of the security manager. (In the remainder of this book, this default implementation of the security manager provided with version 1.2 will be called the "concrete SecurityManager.") Your application can instantiate and install this security manager explicitly, or allow it to be installed automatically. In Sun's Java 2 SDK version 1.2, for example, you can specify that the concrete SecurityManager be installed by using the -Djava.security.manager option on the command line.

The concrete SecurityManager class allows you to define your custom policy not in Java code, but in an ASCII file called a policy file. In the policy file, you grant permissions to code sources. Permissions are defined in terms of classes that are subclasses of java.security.Permission. For example, java.io.FilePermission represents permission to read, write, execute, or delete a file. Code sources are composed of a codebase URL from which the code was loaded and a set of signers that vouched for the code. When the security manager is created, it parses the policy file and creates CodeSource and Permission objects. These objects are encapsulated in a single Policy object that expresses the policy at runtime. Only one Policy object can be installed at any one time.

Class loaders place types into protection domains, which encapsulate all the permissions granted to the code source represented by the loaded type. Each type loaded into a 1.2 virtual machine belongs to one and only one protection domain. The protection domain is remembered and is used when deciding whether or not the code will be allowed to take potentially unsafe actions.

When the check methods of the concrete SecurityManager are invoked, most of them pass the request on to a class called the AccessController. The AccessController, using the information contained in the protection domain objects of the classes whose methods are on the call stack, performs stack inspection to determine whether the action should be allowed.

The security manager has undergone quite a bit of change in 1.2. In versions 1.0 and 1.1, each check method indicates what is being checked in its method name. To check whether or not it is OK to read a certain file, the Java API invokes the checkRead() method on the security manager and passes and the path name of the file to read as a parameter. For example, before attempting to read a file named /tmp/finances.dat, the security manager invokes checkRead("/tmp/finances.dat") on the security manager. The security manager declares 28 of these check methods, which in the remainder of this chapter will be referred to as "legacy check methods." Although new methods were added to the security manager in 1.2 that would otherwise render these legacy check methods obsolete, to maintain backwards compatibility the Java API continues to call the legacy check methods just as it did in prior releases.

The 28 legacy check methods are listed here along with the potentially unsafe action that trigger's their invocation by the code of the Java API:

In 1.2, a set of permission classes was defined whose instances represent actions code is allowed to take. A new pair of check methods were added in 1.2 to class java.lang.SecurityManager, both named checkPermission():

The checkPermission() methods accept a reference to a Permission object, which indicates the action that is being requested. Thus, this method provides an alternative way to ask the security manager if it is OK to perform a potentially unsafe action. For example, to determine whether it is OK to read file /tmp/finances.dat, the Java API in 1.2 could take either of two approaches. The Java API could take the old fashioned approach, and invoke the legacy method checkRead() passing the String "/tmp/finances.dat" as a parameter. Or, the Java API could take the fresh new approach. It could create a java.io.FilePermission object, passing Strings "/tmp/finances.dat" and "read" to the FilePermission constructor. The Java API could then pass this Permission object to the security manager's checkPermission() method.

Both the old fashioned approach of invoking a legacy check method and the fresh new approach of creating a permission object and invoking checkPermission() should yield the same result. To maintain backwards compatibility with security managers that were written for 1.0 or 1.1, however, the 1.2 Java API continues to take the old fashioned approach. The 1.2 Java API continues to call the 28 legacy check methods. Nevertheless, in the concrete SecurityManager class, the legacy methods are for the most part implemented in terms of the new checkPermission() method. So by invoking the legacy method on the concrete SecurityManager, the Java API is indirectly invoking the checkPermission() method anyway. For example, the checkRead() method implementation in the concrete SecurityManager simply instantiates a new FilePermission object, passing the pathname String passed to it to the FilePermission's constructor, along with the String "read". The checkRead() method then invokes checkPermission(), passing a reference to the FilePermission object.

The Java API may at times also invoke checkPermission() directly. For new concepts of potentially unsafe actions introduced in 1.2 and later versions, no legacy check methods exist. Thus, in some situations, the Java API may create a new Permission object for which no relevant check methods exists, and pass that Permission object directly to the security manager's checkPermission() method.

In the concrete SecurityManager class, the checkPermission() method also delegates the job of deciding whether or not to allow the action to another method. The concrete SecurityManager's checkPermission() method simply invokes the static checkPermission() method of class java.security.AccessController, passing along the permission object. The AccessController class, therefore, is the actual entity responsible for enforcing the security policy when you use the concrete SecurityManager.

All of these changes in 1.2 are backwards compatible with 1.1 and 1.0. If you created a security manager for 1.1, it should still work as expected in 1.2. You can still create a custom security manager in 1.2 as well, which allows anyone with special security needs that aren't adequately addressed by the concrete SecurityManager implementation to create a different kind of security infrastructure. Most people's security needs, however, will be likely met by taking advantage of the flexibility and extensibility built into the concrete SecurityManager.

<<  Page 6 of 17  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use