8.6 The Basic Java 2 Access-Control Model

 < Day Day Up > 

In the Java platform, all protected resources, such as the file system, networking facility, printing system, AWT, and keyboard, are accessible only via the system domain, as shown in Figure 8.9. At various points during a Java program's execution, access to protected resources may be requested . This includes network I/O attempts, local file I/O, and attempts to create a new ClassLoader or to access a program-defined resource.

Figure 8.9. ProtectionDomain Composition of a Java Application Environment

graphics/08fig09.gif

To verify whether the running program is allowed to perform the operation, the library routine makes a call to the method SecurityManager.checkPermission() . This method takes a Permission object argument and determines whether that Permission is granted to the running program. As we pointed out in Section 7.4.1 on page 238, for backward compatibility with older Java versions, other check methods in the SecurityManager , such as checkRead() or checkConnect() , are still available, and it is possible that the library routine calls them instead of calling checkPermission() . These methods, however, turn the request into a Permission and end up invoking checkPermission() anyway.

How does SecurityManager.checkPermission() determine whether the running program has the Permission to execute the protected action? SecurityManager.checkPermission() is in reality only an interface between the library routines and the underlying Java access-control mechanism. In fact, it relies on AccessController.checkPermission() to verify whether the Permission has been granted to the running program. SecurityManager.checkPermission() invokes AccessController.checkPermission() , passing it the same Permission parameter it was passed. Therefore, it remains to be understood how AccessController.checkPermission() performs this verification.

In the Java language, a thread of execution may occur completely within a single ProtectionDomain the system domainor may involve one or more application domains and also the system domain. Logically, each thread in the JVM contains a number of stack frames . Simply stated, each frame contains the method instance variables for one of the methods called in the current thread. Therefore, there must be a stack frame for each method invocation. Because Permission s are granted to CodeSource s and implicitly to classes but not to methods, for the purpose of Permission verification, each stack frame is mapped to the class in which the method is declared.

The AccessController.checkPermission() method walks back through the current thread's stack frames, getting the ProtectionDomain for each class on the thread's stack. All these ProtectionDomain s are collected into a java.security.AccessControlContext object. As each ProtectionDomain in the thread's stack is located, the implies() method on that ProtectionDomain is invoked with the Permission object to check as a parameter (see Section 8.5.1 on page 266). For each stack frame, if the checked Permission is implied by the ProtectionDomain , testing of the Permission continues with the ProtectionDomain associated with the next frame on the stack.

This testing repeats until the end of the stack is reached. That is, all the classes in the thread have the Permission to perform the operation. Thus, the access-control check succeeds, typically meaning that the requested operation is able to proceed. However, if even only one ProtectionDomain in the thread's stack does not imply the checked Permission which means that the checked Permission is not granted to all classes on the stacka SecurityException is thrown, and access to the resource is denied .

Therefore, for a restricted operation to succeed, the Permission to perform the operation must be implied by all the ProtectionDomain s traversed by the thread of execution. We can conclude that the Permission set of an execution thread is the intersection of the sets of the Permission s implied by all the ProtectionDomain s traversed by the executing thread. Figure 8.10 shows the Permission s granted to the thread of execution as the intersection of the Permission s implied by the N ProtectionDomain s traversed by the thread.

Figure 8.10. Permission s Granted to a Thread Traversing N ProtectionDomain s

graphics/08fig10.gif

The reason for this access-control model is simple. Java security has been designed to prevent a less powerful ProtectionDomain from gaining additional Permission s as a result of calling or being called by a more powerful ProtectionDomain . Let us consider the scenario shown in Figure 8.11.

Figure 8.11. ProtectionDomain Interaction from Less Powerful to More Powerful

graphics/08fig11.gif

An application that prints a message interacts with the java.lang.System class in the system domain. The application domain is untrusted, whereas the system domain is fully trusted. Both the application domain and the system domain are traversed by the thread of execution. If AccessController.checkPermission() verified only the Permission s of the System class, any operation would be allowed, as the System class is fully trusted. As a consequence, the application could force the System class to perform restricted operations on its behalf , bypassing the security restrictions imposed by the system's security policy.

The way it has been designed, the Java 2 access-control mechanism grants this thread of execution the intersection of the set of the Permission s implied by the application domain and the set of the Permission s implied by the system domain. Because the set of the Permission s implied by the application domain is a subset of the set of the Permission s implied by the system domain, that granted Permission set coincides with the set of the Permission s implied by the application domain. Therefore, the application domain does not gain any additional privilege from calling a more trusted class.

It is also possible that it is the system domain that calls classes in the application domain. For example, a servlet container's system domain may call a servlet's init() method to initialize the servlet, as shown in Figure 8.12. From what we have explained before, the effective access rights of the servlet are the same as the current rights enabled in its application domain.

Figure 8.12. ProtectionDomain Interaction from More Powerful to Less Powerful

graphics/08fig12.gif

In more complex scenarios, many ProtectionDomain s may be involved in a particular thread of execution. By granting the thread only the intersection of the sets of the Permission s implied by each of the ProtectionDomain s involved, the Java security model guarantees that less trusted classes will not gain additional privileges.

8.6.1 Scenario: Simple Check of the Current Thread

Let us consider Listing 8.3, a Java application called GetProperty.

Listing 8.3. GetProperty.java
 import java.security.*; class GetProperty {    public static void main(String[] args)    {       String s;       try       {          if (args.length > 0)          {             s = System.getProperty(args[0], "name " + args[0]                + " not specified");             System.out.println(args[0] +                " property value is: " + s);          }          else             System.out.println("Property name required");       }       catch(Exception e)       {          System.err.println("Caught exception " +             e.toString());       }    } } 

GetProperty expects the name of a system property passed on the command line and, as a result, displays the value of that property. For example, if

 java GetProperty java.version 

is entered on the command line of a system running Java 2 SDK V1.4.0, the result would be

 java.version property value is: 1.4.0 

To enforce access-control restrictions in this scenario, a SecurityManager needs to be activated. Remember that in the Java 2 reference implementation, Java applications, being local programs, do not run by default under any SecurityManager . In fact, no access-control mechanism is applied unless the application itself programmatically sets a SecurityManager instance as the active SecurityManager or the JVM is launched with the -Djava.security.manager option (see Section 7.4.2 on page 240). Therefore, we assume that the default SecurityManager , the one in package java.lang , is activated from the command line. In addition, to really understand how the access-control mechanism works behind the scenes, it is advisable to activate the Java debugger. Therefore, the recommended full command line is

 java -Djava.security.manager -Djava.security.debug=all GetProperty java.version 

The output of the debugger clarifies the steps necessary to verify whether the current thread has access to the system property java.version .

  1. System.getProperty() calls SecurityManager.checkPropertyAccess() with argument "java.version" .

  2. SecurityManager.checkPropertyAccess() invokes AccessController.checkPermission with the parameter java.util.PropertyPermission "java.version", "read" .

  3. AccessController.checkPermission() performs the following operations:

    1. Walks back through the stack frames of the current thread

    2. Obtains the ProtectionDomain s of all the classes on the thread's stack

    3. Verifies that all the ProtectionDomain s obtained in point b imply java.util.PropertyPermission "java.version", "read" .

To examine in detail the steps performed by AccessController when it walks back through the stack frames of the current thread, let us consider Figure 8.13, which shows a graphical representation of the thread's stack during the execution of the GetProperty application. When it examines the thread's stack, AccessController.checkPermission() starts with the last method call in the calling sequence and proceeds backward up to the top of the stack. For each frame on the stack, the method verifies that the ProtectionDomain of the class containing the method implies the PropertyPermission to read the system property java.version . The details of the process follow.

  1. Class AccessController is in the system domain the PropertyPermission is implicitly granted. Proceed to the next frame on the thread's stack.

  2. Class SecurityManager is in the system domain the PropertyPermission is implicitly granted. Proceed to the next frame on the thread's stack.

  3. Class System is in the system domain the PropertyPermission is implicitly granted. Proceed to the next frame on the thread's stack.

  4. Class GetProperty is in an application domain Is the PropertyPermission granted?

    • If yes, because this is the top of the stack, the PropertyPermission is granted to the entire thread of execution.

    • If no, throw a SecurityException .

Figure 8.13. Thread of Execution for the GetProperty Application

graphics/08fig13.gif

In practice, as this scenario involves only two ProtectionDomain s, one of which is the system domain, step 4 is the only crucial point where the access-control decision is made. In fact, this is the point where GetProperty's application domain is interrogated to see whether it implies the required PropertyPermission .

8.6.2 SecurityManager and AccessController

In Chapter 7, we defined the class-loading system, class file verifier, and SecurityManager as the foundations of Java 2 security. However, we have just seen that another Java 2 class, AccessController , complements SecurityManager by playing a crucial role in enforcing access-control decisions. This section clarifies how SecurityManager and AccessController and AccessController share responsibility for enforcing access control based on the security policy configuration.

Before the AccessController class was introduced in the Java 2 platform, the SecurityManager class had to rely on its own internal logic to determine the security policy needed to be in effect, and any change in the security policy meant changing the SecurityManager . Therefore, prior to the Java 2 platform, implementing customized security policies was possible with the SecurityManager alone, but it took a great deal of effort. Now, the SecurityManager can defer access-control decisions to the AccessController . Determining Java 2 security policies is much more flexible, as the policy to be enforced by the SecurityManager can be specified in a file. The AccessController provides a simple procedure for giving specific Permission s to specific code. For backward-compatibility reasons, the Java 2 API still calls the methods of the SecurityManager to enforce system security, but most of these methods call the AccessController . The large body of Java programs built on pre-Java 2 versions dictates that the SecurityManager not be changed but instead be supplemented by the AccessController , which provides a simple method for implementing fine-grained access control.

Even though newly written library code could enforce access-control decisions by directly calling AccessController.checkPermission() , this is not considered to be a good programming choice. SecurityManager.checkPermission() should always be the primary interface between programs and AccessController.checkPermission() , for several reasons.

  • The AccessController implementation may change in future Java releases. By sticking to the SecurityManager 's methods, programmers know that their code will not break.

  • The SecurityManager can be made active from the command line; the AccessController cannot. As a consequence, an application can be run without an active SecurityManager , but security would still be partially enforced if AccessController.checkPermission() were invoked directly. This could lead to inconsistent security enforcements.

  • Once it has been set as the system's current SecurityManager , a SecurityManager instance becomes the unique SecurityManager for the entire JVM system. This instance can, however, be replaced with a different SecurityManager instance by programs that are authorized to do so. A change in the active SecurityManager may imply a change in the measure the security policy is enforced; a new SecurityManager could be, in spite of the security policy, stricter than the previous one, by denying specific Permission s, or more relaxed , by allowing other Permissions . If some library code calls AccessController.checkPermission() directly, without using SecurityManager.checkPermission() as an interface, a change in the SecurityManager will inconsistently reflect in a corresponding change in the policy enforcement.

 < Day Day Up > 


Enterprise Java Security. Building Secure J2EE Applications
Enterprise Javaв„ў Security: Building Secure J2EEв„ў Applications
ISBN: 0321118898
EAN: 2147483647
Year: 2004
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net