< 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
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 AuthorizationBasic 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
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.
9.3.2 Adding a Subject to a ThreadA 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:
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 ScenarioThe 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.javapackage 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 .
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 Demo3LoginModulejava -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 FileIn 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 Filegrant 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.javapackage 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.
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.
9.3.4 Examples of the Subject-Based Authorization AlgorithmJava 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
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 ScenarioThe 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.javapackage 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.javapackage 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 Demo3LoginModulejava -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
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()
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:
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
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
9.3.5 Additional Observations about JAASIt 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()
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]
Figure 9.8. Effect of Calling AccessController.doPrivileged() after Subject.doAs()
|
< Day Day Up > |