9.3 Authorization Overview

 < Day Day Up > 

The challenge in developing of JAAS was to add principal-based authorization to J2SE in a way that would augment, and not disturb, the existing Java authorization mechanisms. This requirement was accomplished by

  • Associating a Subject and its set of Principal s with a thread of execution

  • Logically extending the java.security.ProtectionDomain s of executing code to include the java.security.Permission s associated with the Subject 's Principal s (see Section 8.5 on page 265 and Section 8.2 on page 258)

Here, we provide a brief review of the J2SE ProtectionDomain -based authorization algorithm, including authorization calls to java.security.AccessController.doPrivileged() (see Section 8.7 on page 274). We then explain how to add a Subject to a thread of execution to extend authorization to an authenticated Principal requesting access to a restricted resource. Finally, we show how the basic J2SE authorization algorithm (see Section 8.6 on page 267) is modified to support authorizing Subject s.

9.3.1 A Brief Review of J2SE ProtectionDomain -Based Authorization

Basic J2SE authorization involves the authorization of classes and their methods to perform privileged operations. Authorization consists of determining whether each of the classes whose methods are executing in a thread is authorized to perform the privileged operation. If one or more of the classes is not authorized, the authorization request fails, resulting in a java.lang.SecurityException being thrown. This basic algorithm is slightly modified when a method chooses to enter a privileged mode by calling the AccessController.doPrivileged() method. When doPrivileged() is called, all methods and their associated classes on the thread runtime stack prior to the method's calling doPrivileged() are excluded from the authorization test. Figure 9.1 shows the basic concepts.

Figure 9.1. Thread Stack Frames , ProtectionDomain s, and AccessControlContext

graphics/09fig01.gif

To enable authorization, each class needs to be granted a set of authorizations, represented as Permission objects. Each class is loaded into the Java runtime via a java.security.SecureClassLoader , such as a java.net.URLClassLoader . The ClassLoader associates a ProtectionDomain with each class. The ProtectionDomain references the class's java.security.CodeSource , representing the URL location from which the class has been loaded and the entities that digitally signed the class's code. The set of Permission s to associate with a ProtectionDomain are defined by the Policy object and are dynamically calculated during each Permission check. This dynamic behavior allows for Policy refresh without restarting the Java runtime.

When access to a restricted resource is requested , an authorization test is performed by making a call to AccessController.checkPermission() . In essence, the following steps are performed.

  1. A Permission object instance is passed to checkPermission() . This Permission object represents the right to access the restricted resource. For example:

     AccessController.checkPermission(new java.util.PropertyPermission("user.home", "read")); 
  2. A java.security.AccessControlContext is constructed by collecting the ProtectionDomain s for all the classes whose methods are on the thread stack. This is achieved by walking back through each of the thread stack frames, starting at the checkPermission() method. If a doPrivileged() call is encountered in the thread stack, the thread stack walkback stops at the method/class just prior to the doPrivileged() call. The result of the walkback is a set of unique ProtectionDomain s. System classesthose loaded by the primordial class loader via the boot class path are considered to be fully trusted and essentially are granted java.security.AllPermission . In the Sun Microsystems reference implementation of the Java runtime, a ProtectionDomain is not included in the AccessControlContext for the system classes.

    Figure 9.1 shows a calling sequence that includes a call to doPrivileged() and checkPermission() . When checkPermission() is called, the AccessControlContext used for the authorization testing contains only ProtectionDomain2 and ProtectionDomain3 . ProtectionDomain1 is excluded, as the method in Class1 is called prior to the method that called doPrivileged() . The system domain is also excluded, because it is considered to be fully trusted.

  3. Authorization is performed by taking the Permission object passed to the checkPermission() call and calling ProtectionDomain.implies() with the same Permission object from step 1 for each ProtectionDomain in the AccessControlContext . The result is that each ProtectionDomain in the AccessControlContext is queried as to whether it contains a Permission that implies is equivalent to or stronger thanthe Permission checked by the call to AccessController.checkPermission() . If all the ProtectionDomain s in the AccessControlContext imply the Permission passed to the AccessController.checkPermission() method, the authorization succeeds. If at least one ProtectionDomain does not imply the required Permission , authorization fails, and a SecurityException is thrown.

    In Figure 9.1, if both ProtectionDomain2 and ProtectionDomain3 imply the Permission passed as an argument to checkPermission() , the authorization succeeds. If either ProtectionDomain.implies() method fails because it lacks a Permission that implies the Permission passed as an argument to checkPermission() , the authorization fails.

9.3.2 Adding a Subject to a Thread

A key JAAS feature is principal-based authorization, which allows an authenticated Principal , referenced by a Subject instance, to access restricted resources regardless of the authorization granted to the executing code. As shown in Section 9.2 on page 292, it is possible to use LoginContext s and LoginModule s to create Subject s and attach Principal objects to them. The next step is to associate a Subject with a thread of execution. This is accomplished via the doAs() and doAsPrivileged() static methods in class javax.security.auth.Subject .

Syntactically, associating the Subject with a thread is similar to calling AccessController.doPrivileged() . The Subject.doAs() method is called with two parameters:

  1. A Subject instance containing authenticated Principal s

  2. A PrivilegedAction or PrivilegedExceptionAction instance

PrivilegedAction and PrivilegedExceptionAction are the two java.security package interfaces that are also used for passing privileged code to an AccessController.doPrivileged() call. They have a run() method that, when implemented, will contain code to be executed with the Subject 's Permission s. The PrivilegedExceptionAction interface should be used when the code embedded into the run() method can throw a checked Exception , or an Exception listed in the throws clause of a method. If there are no checked Exception s, PrivilegedAction should be used. When invoked, Subject.doAs() calls the run() method in the PrivilegedAction or the PrivilegedExceptionAction object, executes the code with the set of Permission s granted to the thread of execution unioned with the set of Permission s granted to the Subject 's Principal s, and returns the run() method's return value.

As we explained in Section 8.6 on page 267, the set of Permission s granted to a thread of execution is obtained by intersecting the sets of Permission s implied by the ProtectionDomain s in the thread's AccessControlContext . When Subject.doAs() is called, it adds the Permission s granted to the Subject 's Principal s to the ProtectionDomain s at the stack frames after the call to Subject.doAs() .

The Subject API offers another method to enable a Subject 's Principal s' Permission s, Subject.doAsPrivileged() . Like doAs() , doAsPrivileged() takes a Subject and a PrivilegedAction or a PrivilegedExceptionAction parameter, as well as an AccessControlContext parameter. Subject.doAsPrivileged() behaves exactly like Subject.doAs() but uses the AccessControlContext provided as the third parameter instead of retrieving the current thread's AccessControlContext . If the provided AccessControlContext is null , this method instantiates a new AccessControlContext with an empty collection of ProtectionDomain s.

Note that adding a Subject 's Permission s to a thread of execution is considered a security-sensitive operation. Therefore, if a SecurityManager is installed, the Subject API will consult it to verify whether the code attempting to perform such an operation is authorized to do so. Specifically, calling Subject.doAs() or Subject.doAsPrivileged() requires a javax.security.auth.AuthPermission . The Permission target is the String "doAs" for Subject.doAs() ; the String "doAsPrivileged" for Subject.doAsPrivileged() .

9.3.2.1 Fourth Scenario

The following application, Main2 (Listing 9.14), shows how to use Subject.doAs() to run a thread of execution under a particular authenticated Subject .

Listing 9.14. Main2.java
 package jaasexample; import javax.security.auth.Subject; /**  * Main2 extends the function of Main by overriding the  * doSomethingInteresting() method so that it calls  * Subject.doAs() with an instance of Demo4PrivilegedAction.  */ public class Main2 extends Main {    public static void main( String[] args)    {       (new Main2()).run(args);    }    /**     * Associate a Subject with the current thread, calling     * a Demo4PrivilegedAction that will retrieve the Subject     * currently executing, and then display the Principals     * that were active when Demo4PrivilegedAction.run()     * was executing.     *     * @param subject is the Subject to associate with the     *        current thread.     */    protected void doSomethingInteresting(Subject subject)    {       System.out.println();       System.out.println("Calling " +          "Demo4PrivilegedAction.run()");       System.out.println();       // Associate the subject argument passed to this       // method with the current thread, then call       // Demo4PrivilegedAction.run().       // Demo4PrivilegedAction will return the thread's       // current Subject, which will be the subject passed       // as an argument to this method.       Subject threadSubject = (Subject) Subject.doAs          (subject, new Demo4PrivilegedAction());       showPrincipals(threadSubject.getPrincipals());       System.out.println();    } } 

Main2 subclasses Main (see Listing 9.2 on page 296) and reuses Demo3LoginModule (see Listing 9.10 on page 309). In particular, Main2 calls Main 's run() method to perform the authentication. As we saw in Section 9.2.2.3 on page 308, Demo3LoginModule creates the Subject with two associated Principal s Manager and Agent the String "Louise" as the public credential, and the String "Private DB access password" as the private credential. Main2 overrides the doSomethingInteresting() method, which is called by Main 's run() method after the Subject has been created. Main2.doSomethingInteresting() associates the Subject with the current thread of execution and runs a PrivilegedAction , called Demo4PrivilegedAction , from within a doAs() block. Finally, Main.showPrincipals() displays the Principal s for the current Subject .

Listing 9.15 shows the Demo4PrivilegedAction code, which gets the Subject from the current thread by passing the current AccessControlContext to the static method Subject.getSubject() , which returns the thread's current Subject . This operation will be executed with the Subject 's Permission s. Calling Subject.getSubject() requires a javax.security.auth.AuthPermission "getSubject" , which must be granted to the application's CodeSource or the Subject .

The command line arguments to run this scenario are as follows .

  • -Djava . security.manager indicates that the default SecurityManager , in package java.lang , should be installed by the Java virtual machine launcher.

    Listing 9.15. Demo4PrivilegedAction.java
     package jaasexample; import java.security.AccessController; import java.security.PrivilegedAction; import javax.security.auth.Subject; /**  * A simple class to demonstrate how to get  * the Subject from the current thread.  */ public class Demo4PrivilegedAction    implements PrivilegedAction {    /**     * This method is indirectly called by a Subject.doAs()     * method, where the current instance of this class is     * passed as an argument to the Subject.doAs() method.     * The current thread's Subject is returned by this     * method.     *     * @return the current thread's Subject.     */    public Object run()    {       // The AccessControlContext contains the current       // Subject       return Subject.getSubject          (AccessController.getContext());    } } 
  • -Djava.security.policy specifies the location of the Java 2 security policy file, which in this scenario is jaasexample/jaasexample.policy .

  • -Djava.security.auth.login.config indicates the location of the LoginModule configuration file, which in this scenario is jaasexample/jaasexample.config .

  • jaasexample.Main2 is the program to run.

  • Demo3 is the argument to the Main2 program. Demo3 is the name of the stanza in the LoginModule configuration file to use for creating LoginModule s (see Listing 9.13 on page 312).

Listing 9.16 shows, as expected, that Manager and Agent are the two Principal s associated with the current thread and that the String s "Louise" and "Private DB access password" are the public and private credentials, respectively. The example displays the Principal s as they are constructed. Subsequently, Demo4PrivilegedAction.run() demonstrates that the thread's current Subject has the same Principal s.

Listing 9.16. Running Main2 with Demo3LoginModule
  java -Djava.security.manager -Djava.security.   policy==jaasexample/jaasexample.policy -Djava.security.auth.   login.config==jaasexample/jaasexample.config jaasexample.Main2   Demo3  Demo1LoginModule.initialize Demo1LoginModule.login: true Name:  demo  Password:  demo  Demo3LoginModule.commit: true Login succeeded? true Principals Principal: Manager     Class: jaasexample.DemoPrincipal Principal: Agent     Class: jaasexample.DemoPrincipal Public credentials:     Value: Louise     Class: java.lang.String Private credentials:     Value: Private DB access password     Class: java.lang.String Inside Demo4PrivilegedAction.run() Principals Principal: Manager     Class: jaasexample.DemoPrincipal Principal: Agent     Class: jaasexample.DemoPrincipal Demo3LoginModule.logout 

As we said, getting the Subject associated with the current thread of execution is considered a security-sensitive operation that requires a special Permission to be executed. In the Java 2 language, Permission s must be granted to the application's CodeSource . Alternatively, JAAS allows granting Permission s to the Principal that represents the Subject associated with the thread of execution. The Java system administrator grants Permission s to CodeSource s and/or Principal s by configuring a security policy database. In Section 8.3 on page 261, we saw that the default implementation of the policy database is file based, and we studied how Permission s can be granted based on the CodeSource . As a result of the integration of JAAS with the Java 2 SDK V1.4, the Java security Policy now considers the Permission s granted to Principal s representing Subject s associated with threads of execution.

9.3.3 Security Authorization Policy File

In the Java 2 SDK V1.4, the security policy includes authorization grant entries that describe CodeSource s and/or Principal s authorized to perform privileged operations. The default implementation of the Policy is a flat text file. The sample policy file in Listing 9.17 shows four grant statements, each granting a CodeSource and/or a Principal access to restricted resources.

Listing 9.17. Sample Authorization Policy File
 grant codeBase "file:./" {    permission javax.security.auth.AuthPermission       "createLoginContext.Demo3";    permission javax.security.auth.AuthPermission       "modifyPrincipals";    permission javax.security.auth.AuthPermission       "modifyPublicCredentials";    permission javax.security.auth.AuthPermission       "modifyPrivateCredentials";    permission javax.security.auth.PrivateCredentialPermission       "java.lang.String jaasexample.DemoPrincipal          \"Manager\"", "read";    permission javax.security.auth.AuthPermission "doAs";    permission javax.security.auth.AuthPermission       "getSubject";    permission javax.security.auth.AuthPermission       "doAsPrivileged"; }; grant codeBase "file:./", Principal jaasexample.DemoPrincipal    "Manager" {    permission jaasexample.DemoPermission "Test";    permission jaasexample.DemoPermission       "deploy1:EJB:TravellerCreditCard.*";    permission jaasexample.DemoPermission       "deploy1:EJB:WebAccess.lookup(String)"; }; grant Principal jaasexample.DemoPrincipal "Agent" { }; grant {    permission jaasexample.DemoPermission       "deploy1:EJB:WebAccess.bookmark(int)"; }; 

In Listing 9.17, grant stanzas for Permission jaasexample.DemoPermission exist for authorization for two DemoPrincipal s: Manager and Agent . This policy file requires J2SE V1.4. Earlier versions of J2SE require multiple policy files. See the corresponding J2SE documentation for setting up the policy files.

The source code for DemoPermission is shown in Listing 9.18.

Listing 9.18. DemoPermission.java
 package jaasexample; import java.security.BasicPermission; /**  * A class to demonstrate a J2EE-like Permission.  */ public class DemoPermission extends BasicPermission {    /**     * Required constructor for use when the security policy     * is read.     *     * @param target a String representing the target     * resource.     */    public DemoPermission(String target)    {       super(target);    }    /**     * Each deployment has resource types (for example,     * <code>WEB</code> or <code>EJB</code>), and the name of     * the resource. The actual target object for the     * Permission is a String obtained by colon-concatenating     * the three parts, deployment, resourceType and     * resource.     *     * @param deployment is a String representing the name of     *       the deployment (for example,             <code>deploy1</code>).     * @param resourceType is either the String     *       <code>WEB</code> or the String     *       <code>EJB</code>.     * @param resource is a String representing the name of     *       the resource, such as a URL or an EJB method.     */    public DemoPermission(String deployment,       String resourceType, String resource)    {       super(deployment + ":" + resourceType + ":" +          resource);    } } 

DemoPermission is a simple custom Permission that subclasses BasicPermission in package java.security . As we noted in Section 8.2 on page 258, BasicPermission is a nonabstract class that offers a concrete implementation of the implies() method. BasicPermission is the ideal superclass for custom named Permission s, which are those Permission s that, just like DemoPermission , have a target but not an action list.

The DemoPermission target is a String composed of three parts that we will loosely define around the J2EE authorization model. In this example, the protected resource is access to EJB methods.

  1. The first part is the name of the container instance running the application: for example, deploy1 .

  2. The second part, EJB , indicates that the resource is an EJB method.

  3. The third part is the target method signature, as in WebAccess.lookup(String) . Instead of the method name, it is possible to use a wildcard character, as in TravellerCreditCard.* , to indicate that the Principal is authorized for all EJB methods in the target class.

Details of EJB authorization are covered in Section 5.2 on page 159 and Section 5.4 on page 184.

For running the examples in this chapter, the code needs to be authorized to perform privileged operations, including operations on the Subject and Principal s, and invoking Subject.doAs() . A detailed explanation of the policy file of Listing 9.17 follows.

  1. The first stanza in the security policy file grants all code whose codebase URL is file:./ authorization to perform a set of operations requiring AuthPermission and PrivateCredentialPermission in package javax.security.auth . These Permission s are required to enable the sample code in this chapter to run. Most of the authorizations are for JAAS methods that perform logins and operations on the Subject . The stanza implies that all Principal s are granted these Permission s.

  2. The second stanza grants three DemoPermission s to all code run by DemoPrincipal Manager and whose codebase URL is file:./ . The three DemoPermission s are distinguished by their respective targets: "deploy1: EJB:WebAccess.lookup(String)" , "Test" , and "deploy1:EJB:TravellerCreditCard.*" .

  3. The third stanza, for DemoPrincipal Agent , does not grant any authorizations. This stanza demonstrates that it is possible to have a grant stanza with no Permission s. This stanza could have been omitted because by default, Java Permission s are denied unless explicitly granted by the security policy.

  4. The final stanza grants all code and Principal s a DemoPermission whose target is "deploy1:EJB:WebAccess.bookmark(int)" .

9.3.4 Examples of the Subject-Based Authorization Algorithm

Java 2 authorization is performed by the AccessController . As described in Section 9.3.1 on page 313, AccessController.getContext() creates an AccessControlContext containing the ProtectionDomain s of the classes whose methods are on the thread stack at the time AccessController.getContext() is called. Subsequently, AccessController.checkPermission() tests these ProtectionDomain s to see whether each of them contains a Permission that implies the Permission to access the requested resource. If so, the authorization test succeeds. If any ProtectionDomain in the AccessControlContext does not have a Permission that implies the Permission to access the requested resource, the authorization test fails, and a SecurityException is thrown.

With JAAS, the basic Java 2 authorization concepts are extended to Subject s. Each Subject has a (possibly empty) set of Principal s associated with it. When Subject.doAs() is called, the Permission s associated with each Principal in the specified Subject are added tounioned witheach subsequent stack frame's Permission s. Specifically, the Subject 's Permission s are added to the Permission s of the ProtectionDomain s associated with each of the stack frames after the call to Subject.doAs() .

Figure 9.2 shows the calling sequence for Main2, including the Permissions associated with the methods and their classes. The Permission set in Figure 9.2 indicated as {default Permission s} contains the Permission s associated with all CodeSource s, where the grant stanza in the security policy file did not specify a codebase or Principal (see point 4 on page 323). The Permission set { AllPermission } is for system classes, which are authorized to perform all privileged operations within the Java runtime. As for the rest, each class on the thread stack gets the Permission s granted to its CodeSource , which in this case is reduced to the simple codebase, and those granted to the Principal s.

Figure 9.2. Main2 Thread Stack and Permissions by Stack Frame

graphics/09fig02.gif

Figure 9.2 also shows the effect of the call to Subject.doAs() . At each stack frame after the call to Subject.doAs() , the Subject 's Permission s for DemoPrincipal s Manager and Agent are added to the Permission s associated with the method's class.

From an implementation point of view, Subject.doAs() gets the AccessControlContext associated with the current thread of execution and combines it with the Permission s granted to the Subject . This is done by adding the Subject 's Permission s to the ProtectionDomain s associated with each of the stack frames after the call to Subject.doAs() . The ProtectionDomain s prior to the Subject . doAs() call remain unaltered in the new AccessControlContext that is created. Then, Subject.doAs() calls AccessController.doPrivileged() , passing it the PrivilegedAction or PrivilegedExceptionAction along with the modified AccessControlContext . The PrivilegedAction 's or PrivilegedExceptionAction 's run() method is executed.

Note that even though Subject.doAs() calls AccessController.doPrivileged() , this does not mean that the ProtectionDomain s of the stack frames prior to the call to Subject.doAs() are not tested by AccessController.checkPermission() . In fact, those ProtectionDomain s appear unaltered in the modified AccessControlContext that Subject.doAs() passes to AccessController.doPrivileged() .

In conclusion, when an AccessController.checkPermission() operation is performed, the basic authorization algorithm works the same as before, but the test of the ProtectionDomain for each of the stack frames now includes the Permission s added owing to Subject.doAs() , as we will see in Section 9.3.4.1. In addition, the Subject is now associated with the thread and can be obtained by getting the thread's AccessControlContext and passing it to Subject.getSubject() , as is shown in Listing 9.16 on page 318 and Listing 9.17 on page 319.

9.3.4.1 Fifth Scenario

The program Main3 shown in this section is similar to the Main2 program shown in Listing 9.15 on page 317 but also includes authorization tests in the doSomethingInteresting() method, as well as in the PrivilegedAction . The purpose of Main3 is to show the effect of performing authorization tests at various points, including from within Main3 itself and the PrivilegedAction . The PrivilegedAction implementation used by Main3 is called Demo5PrivilegedAction . In particular, Demo5PrivilegedAction is called via Subject.doAs() and Subject.doAsPrivileged() ; in both cases, the Subject contains the Manager and Agent DemoPrincipal s. Main3 also shows the effect of calling Demo5PrivilegedAction via Subject.doAsPrivileged() with a Subject containing no Principal s. The code of the Main3 application is shown in Listing 9.19.

Listing 9.19. Main3.java
 package jaasexample; import java.util.Iterator; import java.util.Map; import java.security.PrivilegedAction; import javax.security.auth.Subject; /**  * Main3 extends Main by implementing the  * doSomethingInteresting() method  * to perform authorization tests.  */ public class Main3 extends Main {    /**     * The main method of this application.     *     * @param args an array of Strings to be passed on the     *        command line. The first argument tells the     *        LoginContext which LoginContext stanza to use     *        when selecting LoginModules. The second     *        argument is the CallBackHandler to be used by     *        all of the LoginModules.     */    public static void main(String[] args)    {       (new Main3()).run(args);    }    /**     * Demonstrate authorization with and without the Subject     * being associated with the current thread, and by using     * Subject.doAs() and Subject.doAsPrivileged().     *  * @param subject is the Subject to be associated with     *        the current thread.     */    protected void doSomethingInteresting(Subject subject)    {       SecurityManager sm = System.getSecurityManager();       // This scenario requires a SecurityManager       // installed.       if (sm == null)          throw new RuntimeException             ("Please, rerun with a SecurityManager");       // Test whether the current thread is authorized for       // DemoPermission("TestDeploy", "EJB", "Test"). The       // test should fail.       boolean succeeded;       Permission perm = new DemoPermission          ("TestDeploy", "EJB", "Test");       try       {          sm.checkPermission(perm);          succeeded = true;       }       catch (SecurityException se)       {          succeeded = false;       }       System.out.println();       System.out.println          ("DemoPermission(\"TestDeploy\", \"EJB\", " +          "\"Test\") " + "succeeded? " + succeeded);       System.out.println();       // The Demo5PrivilegedAction will be used 3 times       // below       PrivilegedAction paDemo5PA =          new Demo5PrivilegedAction();       // A Map that holds the result of the       // Demo5PrivilegedAction.run() call.       Map resultMap;       // Use doAs() with the Subject.       // Demo5PrivilegedAction.run() will be called.       // Authorization requests will fail because the       // classes in the thread prior to doAs() are not       // authorized. The exception is for access to       // WebAccess.bookmark(int), where all of the code in       // the thread is authorized for access.       System.out.println("doAs() with the Subject:");       resultMap = (Map) Subject.doAs(subject, paDemo5PA);       showResult(resultMap);       // Add the Subject and its Principals to the thread       // so that any Permissions granted to the Principals       // will be added to all ProtectionDomains on the       // stack when an authorization test is performed.       // Demo5PrivilegedAction.run() will be called.       // doAsPrivileged() with a null third argument will       // exclude all ProtectionDomains prior to       // Demo5PrivilegedAction.run() from the       // AccessControlContext when the authorization test       // is performed. In effect, this method and those       // prior to it in the thread will not be considered       // in the authorization test.       System.out.println          ("doAsPrivileged() with the Subject:");       resultMap = (Map) Subject.doAsPrivileged          (subject, paDemo5PA, null);       showResult(resultMap);       // Perform the authorization request using a Subject       // without any Principals. The authorization requests       // will fail except where all of the code in the       // thread is authorized: WebAccess.bookmark(int).       System.out.println          ("doAsPrivileged() using an empty Subject:");       Subject emptySubject = new Subject();       resultMap = (Map) Subject.doAsPrivileged          (emptySubject, paDemo5PA, null);       showResult(resultMap);    }    /**     * Show the contents of the Map returned by the     * Demo5PrivilegedAction.run().     *     * @param map is a Map representing results of     *        authorization tests. For each entry in the Map,     *        the key is a String representing the target for     *        an authorization test, and the value is the     *        boolean <code>true</code> if the authorization     *        test succeeded and the boolean     *        <code>false</code> if the authorization test     *        failed.     */    private void showResult(Map resultMap)    {       Iterator resultIter =          resultMap.entrySet().iterator();       while (resultIter.hasNext())       {          Map.Entry entry = (Map.Entry) resultIter.next();          System.out.println("Method permission: " +             entry.getKey());          System.out.println("   Succeeded? " +             entry.getValue());       }       System.out.println();    } } 

The code of Demo5PrivilegedAction is shown in Listing 9.20.

Listing 9.20. Demo5PrivilegedAction.java
 package jaasexample; import java.util.TreeMap; import java.security.PrivilegedAction; import java.security.Permission; /**  * This class demonstrates authorization system calls (calls  * to the SecurityManager). Instances of this class are  * used by Subject.doAs(), which associates a Subject with  * the current thread.  */ public class Demo5PrivilegedAction    implements PrivilegedAction {    /**     * The set of target EJB methods for which authorization     * is being tested.     */    String[] methodPerms =    {       "TravellerCreditCard.balance(),"       "TravellerCreditCard.credit(int),"       "TravellerCreditCard.debit(int),"       "WebAccess.bookmark(int),"       "WebAccess.lookup(String)"    };    /**     * Perform an authorization test for each target EJB     * method in the methodPerms array.     *     * @return a Map representing results of authorization     *         tests. For each entry in the Map, the key is     *         a String representing the target for an     *         authorization test, and the value is the     *         String <code>true</code> if the authorization     *         test succeeded and the String     *         <code>false</code> if the authorization test     *         failed.     */    public Object run()    {       // Get the SecurityManager to call its       // checkPermission() method.       SecurityManager sm = System.getSecurityManager();       // This scenario requires a SecurityManager       // installed.       if (sm == null)          throw new RuntimeException             ("Please, rerun with a SecurityManager");       // To record the result of the authorization tests.       TreeMap resultMap = new TreeMap();       // Perform an authorization test on each resource       // listed in methodPerms.       for (int i=0; i < methodPerms.length; i++)       {          // Create a DemoPermission with the specified          // target method          Permission perm = new DemoPermission             ("deploy1", "EJB", methodPerms[i]);          try          {             // See if the current Subject/thread classes             // are authorized             sm.checkPermission(perm);             // We reached here if authorization succeeded             resultMap.put(methodPerms[i], "true");          }          catch (SecurityException se)          {             // We reached here if authorization failed             resultMap.put(methodPerms[i], "false");          }       }       // Return the set of authorizations/failures.       return resultMap;    } } 

Listing 9.21 shows the output produced by Main3 when it is run with Demo3LoginModule (see Listing 9.22 on page 330). For an explanation of the command line arguments to run this scenario, see the list on page 319.

Listing 9.21. Running Main3 with Demo3LoginModule
  java -Djava.security.manager -Djava.security.   policy==jaasexample/jaasexample.policy -Djava.security.auth.   login.config==jaasexample/jaasexample.config jaasexample.Main3   Demo3  DemoLoginModule.initialize DemoLoginModule.login: true Name:  demo  Password:  demo  Demo3LoginModule.commit: true Login succeeded? true Principals Principal: Manager     Class: jaasexample.DemoPrincipal Principal: Agent     Class: jaasexample.DemoPrincipal Public credentials:    Value: Louise    Class: java.lang.String Private credentials:    Value: Private DB access password    Class: java.lang.String DemoPermission "Test" succeeded? false doAs() with the Subject: Inside Demo5PrivilegedAction.run() Method permission: deploy1:EJB:TravellerCreditCard.debit(int)    Succeeded? false Method permission: deploy1:EJB:TravellerCreditCard.credit(int)    Succeeded? false Method permission: deploy1:EJB:TravellerCreditCard.balance()    Succeeded? false Method permission: deploy1:EJB:WebAccess.lookup(String)    Succeeded? false Method permission: deploy1:EJB:WebAccess.bookmark(int)    Succeeded? true doAsPrivileged() with the Subject: Inside Demo5PrivilegedAction.run() Method permission: deploy1:EJB:TravellerCreditCard.debit(int)    Succeeded? true Method permission: deploy1:EJB:TravellerCreditCard.credit(int)    Succeeded? true Method permission: deploy1:EJB:TravellerCreditCard.balance()    Succeeded? true Method permission: deploy1:EJB:WebAccess.lookup(String)    Succeeded? true Method permission: deploy1:EJB:WebAccess.bookmark(int)    Succeeded? true doAsPrivileged() using an empty Subject: Inside Demo5PrivilegedAction.run() Method permission: deploy1:EJB:TravellerCreditCard.debit(int)    Succeeded? false Method permission: deploy1:EJB:TravellerCreditCard.credit(int)    Succeeded? false Method permission: deploy1:EJB:TravellerCreditCard.balance()    Succeeded? false Method permission: deploy1:EJB:WebAccess.lookup(String)    Succeeded? false Method permission: deploy1:EJB:WebAccess.bookmark(int)    Succeeded? true Demo3LoginModule.logout 

In Main3, an authorization test is performed by calling SecurityManager . checkPermission() with a DemoPermission whose target resource is called "Test" . As shown in Figure 9.3, not all the thread AccessControlContext ProtectionDomain s, at the point the authorization test is performed, imply jaasexample.DemoPermission "Test" . Therefore, the authorization test fails.

Figure 9.3. Authorization Test from within Main3

graphics/09fig03.gif

Next is a series of authorization tests within Demo5PrivilegedAction . The authenticated Subject contains the Manager and Agent DemoPrincipal s and is associated with the thread via the Subject.doAs() call performed in Main3. Figure 9.4 shows the Permission s associated with each of the stack frames during the call to Demo5PrivilegedAction .

Figure 9.4. Authorization Test after the Call to Subject.doAs()

graphics/09fig04.gif

Note that the call to AccessController.doPrivileged() is made to associate the Subject 's Permission s with all the stack frames after the Subject.doAs() call. As we have observed , the call to doPrivileged() does not exclude any of the ProtectionDomain s from the stack frames prior to the call to Subject.doAs() . Therefore, the authorization test includes checking all the stack frames prior to the call to Subject.doAs() . The ProtectionDomain s of the classes prior to Subject.doAs() call do not imply jaasexample.DemoPermission with any of the following targets:

  • "deploy1:EJB:TravellerCreditCard.debit(int)"

  • "deploy1:EJB:TravellerCreditCard.credit(int)"

  • "deploy1:EJB:TravellerCreditCard.balance()"

  • "deploy1:EJB:WebAccess.lookup(String)"

Therefore, all these authorization tests fail. The last authorization test in Demo5PrivilegedAction , jassexample.DemoPermission with target resource "deploy1:EJB:WebAccess.bookmark(int)" , passes because the authorization policy file contains a stanza that grants authorization to all classes and Principal s (see point 4 on page 323).

Including all the stack frames prior to the Subject.doAs() method in an authorization test is not always satisfactory. In J2EE security, the resource authorization process involves defining a Subject , associating it with a thread, and determining whether it is authorized to use a resource. In this case, we want to make an authorization decision regardless of the authorizations possessed by the code in the thread. We need a means by which the Subject is associated with a thread but ignores the callers to the method that associated the Subject with the thread. The effect is to perform the more traditional principal-based authorization typically found in servers. For example, with J2EE resource-access authorizations, such as to access to EJB methods, we are interested in the authorizations for a Subject , not the code's access rights.

To accommodate traditional principal-based authorization, Subject.doAsPrivileged() is called with the Subject , a PrivilegedAction or a Privileged-ExceptionAction , and an AccessControlContext . Main3 passes Subject.doAsPrivileged() a null AccessControlContext . As shown in Figure 9.5, this call has the same effect as Subject.doAs() but also marks the thread stack as though an AccessController.doPrivileged() was called to truncate the authorization test at the stack frame just prior to the AccessController.doPrivileged() call. In effect, the authorization test is truncated at the Subject.doAsPrivileged() stack frame. When the authorization tests are performed in Demo5PrivilegedAction , they all succeed, as all the thread stack frames checked during a call to AccessController.checkPermission() contain authorizations for the requested resources. The reason behind this behavior is that the AccessControlContext passed to doAsPrivileged() was null . If the value were a reference to an actual AccessControlContext , it would behave as though there were a call to AccessController.doPrivileged() with the AccessControlContext passed as the second parameter, as described in Section 8.7.2 on page 277.

Figure 9.5. Authorization Test after the Call to Subject.doAsPrivileged() with a null AccessControlContext

graphics/09fig05.gif

The final set of authorization tests in Main3 demonstrates a call to Subject.doAsPrivileged() with the same PrivilegedAction but with a Subject lacking Principal s authorized for the resources requested in the PrivilegedAction . In particular, the Subject in this example does not contain any Principal s, although the effect would be the same even if it did contain Principal s that lacked authorization for access to the requested resources. As shown in Figure 9.6, the authorization test fails because not all the methods on the stack whose ProtectionDomain s are being tested have the required Permission s. In particular, for those methods, neither the CodeSource nor the Subject 's Principal s have the required authorizations.

Figure 9.6. Authorization Test after the Call to Subject.doAsPrivileged() with a null AccessControlContext and a Subject Lacking Authorized Principal s

graphics/09fig06.gif

9.3.5 Additional Observations about JAAS

It is possible to call Subject.doAs() multiple times within a thread, but only one Subject may be active at a time. As shown in Figure 9.7, when Subject.doAs() is called with Subject s1 and PrivilegedAction or PrivilegedExceptionAction pa1 , followed by a call to Subject.doAs() with Subject s2 and PrivilegedAction or PrivilegedExceptionAction pa2 , pa2 will be executed with s2 's Permission s, not s1 's Permission s. The effect of the second call to Subject.doAs() is to obscure s1 's Permission s until the second Subject.doAs() completes and returns to its caller, method1 . Similar considerations apply to Subject.doAsPrivileged() .

Figure 9.7. Effect of Multiple Calls to Subject.doAs()

graphics/09fig07.gif

JAAS is integrated with the J2SE security and authorization mechanisms, including AccessController.doPrivileged() . In the absence of Subject s associated with a thread, a call to doPrivileged() will mark the thread stack to limit the number of stack frames searched for ProtectionDomain s and Permission s when constructing an AccessControlContext . The same is true for JAAS and Subject s. If a doPrivileged() call is made after a call to Subject.doAs() , the Subject is not visible when an authorization test is performed by AccessController.checkPermission() . The effect is as if the Subject.doAs() method were never called, as is shown in Figure 9.8. Similar considerations apply to Subject.doAsPrivileged() . [3]

[3] This behavior may change in future Java releases, whereby the Subject will not be removed from the thread after a call to AccessController.doPrivileged() . Therefore, the Subject will be inspected during authorization tests.

Figure 9.8. Effect of Calling AccessController.doPrivileged() after Subject.doAs()

graphics/09fig08.gif

 < 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