8.7 Privileged Java 2 Code

 < Day Day Up > 

The Java 2 access-control model is very strict. By granting a thread of execution only to the intersection of the sets of Permission s implied by the ProtectionDomain s the thread traverses, this architecture guarantees that less trusted ProtectionDomain s do not get extra Permission s that had not been granted to them. Therefore, it is not possible for an application domain to steal Permission s from the system domain by making calls into it (see Figure 8.11 on page 269) or by being called by it (see Figure 8.12 on page 270).

However, there would be an intrinsic limitation in the model that we have just described if library code did not have the possibility of exempting its callers from requiring certain Permission s. Consider again the scenario depicted in Section 8.1.1 on page 254. There may be cases in which a trusted class, typically in a library, implements a security-sensitive operation but does not care about whether its callers have the Permission s to perform that operation.

This problem could be solved by granting application code the additional Permission s needed by the library. This solution, however, is not recommended, because it would violate the principle of least privilege , [2] which dictates that an entity be given no more privilege than necessary to perform an operation. Violating this principle would be unwise because (1) malicious application code could misuse the additional Permission and (2) the portability and flexibility of the library code would be limited if its callers were forced to be granted a Permission that they did not directly require.

[2] J. H. Saltzer and M. D. Schroeder. "The Protection of Information in Computer Systems ," Proceedings of the Institute of Electrical and Electronics Engineers 63, 9 (September 1975), 12781308.

To address the issue of trusted library code needing to temporarily extend some of its Permission s to its callers, the Java 2 language introduces the concept of privileged code. When a portion of code is wrapped into a call to AccessController.doPrivileged() , an annotation is made on the thread's stack frame, indicating that when the AccessController.checkPermission() method searches for ProtectionDomain s to see whether they imply the Permission being checked, the search stops at this stack frame (see Figure 8.14). As Figure 8.14 shows, the code that calls doPrivileged() and the code called from the privileged block downward have to have the Permission in order for the security-sensitive operation to succeed. However, the callers are exempted from being granted that particular Permission .

Figure 8.14. Effects of Calling doPrivileged() on the Thread's Stack

graphics/08fig14.gif

For example, the library code in Figure 8.2 on page 255 will need a SocketPermission and a FilePermission . However, if the log operation code is wrapped into a call to doPrivileged() , the calling application code will still need the SocketPermission but will be exempted from being granted the FilePermission to write to the log file.

To summarize, the general rule is that when a Permission checking is performed, all the ProtectionDomain s traversed by the thread of execution must have been granted the Permission being checked (see point 1 in Figure 8.15). However, if the code that effectively performs the restricted operation wraps the security-sensitive code into a call to doPrivileged() , the callers of that code are exempted from the Permission requirement (see point 2 in Figure 8.15).

Figure 8.15. Nonprivileged versus Privileged Code Scenario

graphics/08fig15.gif

8.7.1 Security Recommendations on Making Code Privileged

Inappropriate use of the privileged-code construct can create serious security holes.

  • To avoid inadvertently violating the principle of least privilege, the privileged-code section should be as small as possible and contain only the code that really needs to extend its Permission s to its callers.

  • Privileged code should be used only when the restricted operation leading to a call to AccessController.checkPermission() is not considered a security threat for the system. (See the discussions about the scenario depicted in Figure 8.2 on page 255.)

  • The call to doPrivileged() should be made in the code that has direct need to extend its Permission s. Utility classes that themselves call doPrivileged() should be avoided, as they could create security holes.

8.7.2 How to Write Privileged Code

Privileged code needs to wrapped into a call to AccessController.doPrivileged() . This method can take as an argument

  • A java.security.PrivilegedAction object if the privileged code is not supposed to throw any exceptions

  • A java.security.PrivilegedExceptionAction object if the privileged code could throw a checked exception , that is, an exception listed in the throws clause of a method

Besides these two versions of doPrivileged() , two additional versions of this method take an AccessControlContext object as an additional parameter. An AccessControlContext encapsulates an array of ProtectionDomain s. Passing an AccessControlContext to doPrivileged() besides the PrivilegedAction or PrivilegedExceptionAction object further restricts the privileges granted to a thread of execution. In fact, when an AccessControlContext is specified, the restricted action is performed with the intersection of the set of the Permission s implied by the caller's ProtectionDomain and the set of the Permission s implied by the ProtectionDomain s encapsulated in the AccessControlContext .

PrivilegedAction and PrivilegedExceptionAction are two interfaces. They have only one method, run() , which when implemented will contain the security-sensitive code. This method returns a java.lang.Object . AccessController.doPrivileged() invokes the run() method on the PrivilegedAction or PrivilegedExceptionAction that was passed to it as a parameter and returns the run() method's return value, which could be null if there is nothing to return. If the return value is not null , an explicit casting may be necessary.

Listing 8.4 is an example of PrivilegedAction use when the run() method's return value is null .

Listing 8.4. Use of PrivilegedAction within a Privileged Block with no Return Value
 someMethod() {    // unprivileged code here...    AccessController.doPrivileged(new PrivilegedAction()    {       public Object run()       {          // privileged code goes here, for example:          System.loadLibrary("awt");          return null; // nothing to return       }    });    // unprivileged code here... } 

If a return value is required, the code should be written as in Listing 8.5.

Listing 8.5. Use of PrivilegedAction within a Privileged Block with a Return Value
 someMethod() {    // unprivileged code here...    String user = (String) AccessController.doPrivileged(new PrivilegedAction()    {       public Object run()       {          // privileged code goes here, for example:          return System.getProperty("user.name");       }    });    // unprivileged code here... } 

When using the PrivilegedExceptionAction interface, a java.security.PrivilegedActionException must be caught in a try{} catch(){} block, as in Listing 8.6.

Listing 8.6. Use of PrivilegedExceptionAction and PrivilegedActionException
 someMethod() throws FileNotFoundException {    // unprivileged code here...    try    {       FileInputStream fis = (FileInputStream)          AccessController.doPrivileged(new PrivilegedExceptionAction()       {          public Object run() throws FileNotFoundException          {             // privileged code goes here, for example:             return new FileInputStream("someFile");          }       });    }    catch(PrivilegedActionException e)    {       throw (FileNotFoundException) e.getException();    }    // unprivileged code here... } 

Note that the getException() method for PrivilegedActionException returns an Exception object. Therefore, you must catch this Exception to the type of the specific Exception to be thrown, as only a checked exception will be wrapped in a PrivilegedActionException . In fact, PrivilegedActionException is a wrapper for an Exception thrown by a PrivilegedExceptionAction . In Listing 8.6, the Exception that needs to be thrown is a FileNotFoundException .

8.7.3 Privileged-Code Scenario

Let us consider the scenario shown in Figure 8.16. Here, we have two applications, CountFileCaller1 and CountFileCaller2, attempting to get indirect read access to a local file through the use of library code. The purpose of these applications is to count the number of characters in that file. CountFileCaller1 invokes the CountFile1 API to access the file, whereas CountFileCaller2 uses the CountFile2 API. Both CountFile1 and CountFile2 are granted the FilePermission necessary to read the file. However, CountFile1 does that through a call to doPrivileged() ; CountFile2 does not. Therefore, CountFileCaller1 is exempted from being granted the FilePermission to read the file; CountFileCaller2 is not.

Figure 8.16. Graphical Representation of the Privileged-Code Scenario

graphics/08fig16.gif

Listing 8.7 shows the CountFile1 API code. Note that CountFile1 uses the privileged-code mechanism to exempt its callers from the FilePermission requirement.

Listing 8.7. CountFile1.java
 import java.io.FileInputStream; import java.io.FileNotFoundException; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; import java.security.AccessController; class MyPrivilegedExceptionAction    implements PrivilegedExceptionAction {    public Object run() throws FileNotFoundException    {       FileInputStream fis = new          FileInputStream("C:\AUTOEXEC.BAT");       try       {          int count = 0;          while (fis.read() != -1)             count++;          System.out.println("Counted " + count + " chars.");       }       catch (Exception e)       {          System.out.println("Exception " + e);       }       return null;    } } public class CountFile1 {    public CountFile1() throws FileNotFoundException    {       try       {          AccessController.doPrivileged(new             MyPrivilegedExceptionAction());       }       catch (PrivilegedActionException e)       {          throw (FileNotFoundException) e.getException();       }    } } 

CountFile1 is invoked by CountFileCaller1, whose code is given in Listing 8.8.

Listing 8.8. CountFileCaller1.java
 public class CountFileCaller1 {    public static void main(String[] args)    {       try       {          System.out.println("Instantiating CountFile...");          CountFile1 cf = new CountFile1();       }       catch(Exception e)       {          System.out.println("Exception " + e.toString());          e.printStackTrace();       }    } } 

The CountFileCaller2 API gets access to the local file system directly, without using privileged code. The source code is shown in Listing 8.9.

Listing 8.9. CountFile2.java
 import java.io.FileInputStream; public class CountFile2 {    int count = 0;    public void countChars() throws Exception    {       FileInputStream fis = new          FileInputStream("C:\AUTOEXEC.BAT");       try       {          while (fis.read() != -1)             count++;          System.out.println("Counted " + count + " chars.");       }       catch (Exception e)       {          System.out.println("No characters counted");          System.out.println("Exception " + e);       }    } } 

CountFile2 is invoked by the CountFileCaller2 application, whose code is shown in Listing 8.10.

Listing 8.10. CountFileCaller2.java
 public class CountFileCaller2 {    public static void main(String[] args)    {       try       {          System.out.println("Instantiating CountFile2...");          CountFile2 cf = new CountFile2();          cf.countChars();       }       catch(Exception e)       {          System.out.println("" + e.toString());          e.printStackTrace();       }    } } 

To apply the Java 2 access-control mechanism, the CountFileCaller1 application must be invoked with an active SecurityManager . Section 8.6.1 on page 271 shows how to invoke an application with the default SecurityManager and, in addition, how to activate the Java debugger, which gives details on how AccessController works behind the scenes. Here are the details of the access-control flow as they are revealed by the Java debugger.

  1. CountFileCaller1.main() calls the constructor of CountFile1.

  2. The constructor of CountFile1 calls AccessController.doPrivileged() with a parameter of type MyPrivilegedExceptionAction (see Listing 8.7 on page 280).

  3. AccessController.doPrivileged() invokes the run() method on the instance of MyPrivilegedExceptionAction that it was passed as a parameter.

  4. The run() method of the instance of MyPrivilegedExceptionAction calls the java.io.FileInputStream constructor.

  5. The constructor of FileInputStream invokes SecurityManager.checkRead() , passing it the name of the file that has to be accessed, "C:\\AUTOEXEC.BAT" .

  6. SecurityManager.checkRead() turns the file name into a java.io.FilePermission "C:\\AUTOEXEC.BAT", "read" object, and passes this FilePermission object to SecurityManager.checkPermission() .

  7. SecurityManager.checkPermission() invokes AccessController.checkPermission() , passing it the same FilePermission object.

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

    1. Walks back through the stack frames of the current thread up to the first call to AccessContoller.doPrivileged()

    2. Obtains the ProtectionDomain s of all the classes on the thread's stack up to the caller of doPrivileged()

    3. Verifies that all the ProtectionDomain s obtained in point b imply java.io.FilePermission "C:\\AUTOEXEC.BAT", "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.17, which shows the thread's stack during the execution of the CountFileCaller1 application.

Figure 8.17. Thread's Stack for the CountFileCaller1 Application

graphics/08fig17.jpg

When it examines the thread's stack, AccessController.checkPermission() starts with the last method call in the calling sequence and proceeds back ward up to the top of the stack. For each frame on the stack, AccessController.checkPermission() verifies that the ProtectionDomain of the class containing the method implies the FilePermission to read the local file C:\AUTOEXEC.BAT . However, not all the ProtectionDomain s are checked. As soon as a call to doPrivileged() is encountered , AccessController.checkPermission() stops the ProtectionDomain examination with the caller to doPrivileged() . The details of the process are as follows .

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

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

  3. Class SecurityManager is in the system domain; the FilePermission is implicitly granted. Proceed to the next frame on the thread's stack.

  4. Class FileInputStream is in the system domain; the FilePermission is implicitly granted. Proceed to the next frame on the thread's stack.

  5. Class MyPrivilegedExceptionAction is in the application domainIs the FilePermission granted?

    • If yes, proceed to the next frame on the thread's stack.

    • If no, throw a SecurityException .

  6. Class AccessController is in the system domainthe FilePermission is implicitly granted. Proceed to the next frame on the thread's stack.

  7. Class CountFile1 is in the application domainIs the FilePermission granted?

    • If yes, because CountFile1 is the first doPrivileged() caller encountered, the FilePermission is granted to the entire thread of execution, and no further verification is required.

    • If no, throw a SecurityException .

The ProtectionDomain for CountFileCaller1 is not checked. The restricted action is authorized even if CountFileCaller1 does not have the FilePermission to read the file C:\AUTOEXEC.BAT .

 < 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