Sponsored Link •
|
Advertisement
|
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:
checkConnect(String host, int port)
- open a socket
connection to the specified host and port number
checkConnect(String host, int port, Object
context)
- open a socket connection to the specified host and port number under the passed
security context
checkAccept(String host, int port)
- accept a socket connection from the specified host and
port number
checkCreateClassLoader()
- create a new class loader
checkAccess(Thread t)
- modify a thread (change its priority, stop it, etc...)
checkAccess(ThreadGroup t)
- modify a thread group (add a new thread, set deamonness, etc...)
checkExit()
- cause the application to exit
checkLink()
- load a dynamic library that contains native methods
checkRead(FileDescriptor fd)
- read from the specified file
checkRead(String file)
- read from the specified file
checkRead(String file, Object context)
- read from the
specified file under the passed security context
checkWrite(FileDescriptor fd)
- write to the specified file
checkWrite(String file)
- write to the specified file
checkDelete(String file)
- delete the specified file
checkListen(int port)
- wait for a connection on the specified
local port number
checkMulticast(InedAddress maddr)
- join, leave, send, or
receive IP multicast
checkMulticast(InedAddress maddr, byte ttl)
- join,
leave, send, or receive IP multicast
checkPropertiesAccess()
- access or modify system properties
in general
checkPropertiesAccess(String key)
- access or modify the
specified system property
checkTopLevelWindow(Object Window)
- bring up the
specified window without any warning
checkPrintJobAccess()
- initiate a print job request
checkSystemClipboardAccess()
- access the system clipboard
checkAWTEventQueueAccess()
- access the AWT event queue
checkPackageAccess(String pkg)
- access types from the
specified package (used by class loaders)
checkPackageDefinition(String pkg)
- add a new class to
the specified package (used by class loaders)
checkSetFactory()
- set the socket factory used by
ServerSocket
or Socket
or set the URL stream handler used by
URL
checkMemberAccess()
- access class information via the reflection
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()
:
checkPermission(Permission perm)
- take an action that
requires the specified permission
checkPermission(Permission perm, Object context)
- take an action that requires the specified permission under the passed security context
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
String
s "/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
.
Sponsored Links
|