Skip to main content

Java authorization internals

A guided tour of the Java 2 platform and JAAS authorization architectures

Abhijit Belapurkar (abhijit_belapurkar@infosys.com), Senior Technical Architect, Infosys Technologies Limited
Abhijit Belapurkar has a bachelor of technology degree in computer science from the Indian Institute of Technology (IIT), Delhi, India. He has worked in the areas of architectures and information security for distributed applications for almost 10 years and has used the Java platform to build n-tier applications for more than five years. He presently works as a senior technical architect in the J2EE space, with Infosys Technologies Limited, Bangalore, India.

Summary:  If you're the type who needs to know how a technology works from the inside out in order to use it effectively, you'll jump on this guided tour of the Java platform's authorization architectures. Follow along as Java architect Abhijit Belapurkar leads this detailed, behind-the-scenes introduction to two distinctly different (yet related) models of authorization: the code-centric model of the Java 2 platform security architecture and the user-centric model of the Java Authentication and Authorization Service.

Date:  04 May 2004
Level:  Intermediate
Activity:  4538 views

Authorization is the center of the universe when it comes to information security, because it is the process that controls the access rights of principals (that is, persons, processes, and machines) to system resources. Up until recently, the pertinent question in the context of the Java security architecture used to be "What access rights does this piece of running code have?" That changed with the introduction of the Java Authentication and Authorization Service (JAAS), first as a platform extension with JDK version 1.3 and then as part of the core JDK version 1.4 and above. Under JAAS, the pertinent question now is "What access rights does the authenticated user running this code have?"

In this article, you'll learn about both the old code-centric Java authorization architecture, and the newer user-centric one. I'll start you off with an overview of the Java 2 platform security architecture, focusing on how the architecture utilizes two essential concepts -- a security policy and protection domains -- to define, group, and aggregate static and/or dynamic access permissions. I'll then give you a detailed look at the mechanisms underlying the Java 2 platform security architecture's runtime access checking feature, including the stack inspection and traversal mechanism for determining whether or not a permission is to be granted. Once you understand how code-centric authorization works, I'll move on to the user-centric authorization model of the Java Authentication and Authorization Service (JAAS). Here, I'll focus on the notion of subject-based access control, and show how it has been implemented, in JAAS, on top of the original stack inspection mechanism of the Java 2 platform security architecture.

Note that this article assumes you are familiar with application programming on the Java platform (J2SE SDK 1.4), as well as the basic concepts of enterprise application security. All concepts related to the Java platform's code- and user-centric authorization architectures are treated as introductory.

Code-centric authorization

The Java platform was traditionally intended for running mobile code such as applets. To protect system resources from these arbitrary pieces of code downloaded from the network to the user's browser, applets were relegated to the sandbox, where they ran with a limited set of access rights. For local Java applications, on the other hand, the security manager that would have provided a similar sandboxed environment was rarely (if ever) installed. As a result, local applications were typically trusted with access to all system resources.

The biggest difference between the JDK 1.x model and the new security architecture of the Java 2 platform SDK version 1.2 was the introduction of a new configurable security policy, which allowed for finegrained and administrable access control. All code (whether local or downloaded, regardless of whether it was signed or not) could be subject to the constraints of a well-defined security policy granting sets of (possibly overlapping) permissions to different pieces of code. Also, with the work done on introducing multiprocessing capabilities to the JVM (see Resources) came the requirement for user-based access control.

The basic principle underpinning the Java 2 platform security architecture can be summarized as follows: A system-level security policy defines access permissions (per the needs of the application under consideration) for executing code grouped into protection domains. The security policy is used for access control checks, which are performed by the JVM at runtime. I'll elaborate upon these concepts one by one as I move further along the tour.


Access permissions as typed objects

In the Java 2 platform security architecture, all access permissions are typed and arranged in a hierarchy rooted at the abstract class java.security.Permission. Typically, a Permission consists of a target ("On whom will the action controlled by this permission be performed?") and an action ("What will be performed on the target, if so allowed by this permission?").

One important idea in the context of allowing a piece of running code to perform a specific "action" on a specific "target" is that the code does not have to be granted the exact Permission as requested. Instead, it is enough that the requested Permission can be deduced from or implied by one or more Permissions that are actually granted to the code. For example, if a piece of running code is granted the permission to read all files in the directory /x, then it doesn't need an explicit permission to perform a read action on the target file /x/in.txt, because the latter permission is implied by the former.

Clearly, the definition of whether a certain Permission is implied by another will depend on the way in which the two Permissions are defined. At the least, the two Permissions must be of the same type. However, the runtime cannot be expected to make any further judgments and must delegate this implication reasoning logic to the Permission class in question. The runtime queries the Permission class about its implication reasoning logic by calling a method, aptly named implies.

Aggregated permissions

The new security architecture also introduced the concept of aggregation. In the Java 2 platform, it is possible to aggregate multiple instances of Permission objects of the same type. A collection of this type is called a PermissionCollection. For example, a PermissionCollection might consist of two java.io.FilePermission instances, representing the privilege of reading two different files.

Typed objects such as these cleanly encapsulate the responsibility of creating and maintaining a collection and iterating over that collection. Instead of calling the implies() method on each item individually every time a permission has to be checked, the Java runtime simply calls the implies() method provided by the PermissionCollection object and waits for its response. You can define a new PermissionCollection type for every custom Permission that you create. The actual implementation of the implies() method in the PermissionCollection will, of course, depend on the nature of the given Permission object.

The Permissions object

Any given piece of running code will have different types of Permissions granted to it, in addition to having multiple instances of a specific Permission type. The Java 2 platform security architecture provides for such a collection of PermissionCollection objects in the form of a Permissions object. A Permissions object is a collection of collections of individual Permission instances. The Permissions class also provides an implies() method. However, you will remember that in order for one Permission to imply another the two must be of the same type. Therefore, calling the implies() method on the Permissions object causes the latter to first locate the correct PermissionCollection instance (one that holds a collection of Permission objects of the correct type) from within its internal collection, and then call the implies() method on the PermissionCollection object thus obtained, passing to it the Permission to be checked.


The security policy and protection domains

The security policy applicable to a system is in essence a well-defined "repository" of assertions regarding access permissions granted to the different entities in that system. According to the classical definition of a protection domain (see Resources), a domain is scoped by the set of objects that is currently directly accessible by an entity in the system to which authorizations have been granted. (Given this definition, you could actually think of the Java sandbox in JDK version 1.1 as a protection domain with a fixed boundary.) Building on this, the Java 2 platform security policy was designed to grant access permissions in terms of ProtectionDomains, rather than granting such permissions to individual pieces of running code. Therefore, each class or object "belongs to" a ProtectionDomain, to which certain access permissions have been granted by the security policy. Restated from the point of view of ProtectionDomains, a specific ProtectionDomain encloses a set of classes (for example, all classes loaded from a certain location and signed by a certain key) whose instances will all be granted the same set of permissions.

The reason behind this one level of indirection (that is, permissions are not granted to classes and objects directly) is extensibility -- it should be possible to change and/or refine the definition of what makes up a ProtectionDomain without impacting the granting of permissions. (Indeed, whereas a pre-JAAS ProtectionDomain was characterized only by the code "belonging" to it, a post-JAAS ProtectionDomain is additionally characterized by the authenticated user running the code. Performing user authentication enables a given piece of code to run with a different set of permissions based on the currently authenticated user on account of the fact that each user is allocated to a particular ProtectionDomain, in which his or her permissions are set. I'll describe all of this in greater detail when I begin discussing the JAAS authorization architecture.)

Protection domains and code source

Clearly, it must be possible to uniquely identify a piece of running code to ensure that there is no confusion as to its access permissions. The set of uniquely identifying properties for running code consists of two items: the origin of the code (specified by the URL from which the code was loaded into memory) and the signer entity for the code (specified by the set of public keys corresponding to the digital signature of the running code). The combination of these two characteristics is codified in the Java 2 platform security architecture as the CodeSource of any given piece of running code. You can now provide a stricter definition of ProtectionDomains: A ProtectionDomain is a grouping of a CodeSource and its access permissions. To put it another way, a ProtectionDomain represents all the permissions that are granted to a particular CodeSource.

The Java runtime sets up the mapping between a ProtectionDomain and the permissions granted to it via (a concrete extension of) the class called java.security.Policy. The default extension of this class is sun.security.provider.PolicyFile. As its name would indicate, sun.security.provider.PolicyFile obtains the mapping between CodeSource (identified by a location URL and the signer identifier aliases) and the permissions granted to it from a file. The location of this file can be provided as input to the JVM via the environment variable java.security.policy. The Policy class provides a method called getPermissions() that can be called to obtain the set of permissions granted to a specific CodeSource.

The SecureClassLoader

The mapping between a class and its ProtectionDomain is set up when the class is first loaded and will not change until the class is garbage collected. A class is typically loaded by a special class called the SecureClassLoader. The SecureClassLoader starts by loading the bytes of the class from the appropriate URL and verifying the digital signature of the enclosing archive file, if required. It then obtains a heterogenous PermissionCollection populated with statically bound permissions granted to the class's CodeSource, by calling the abovementioned getPermissions() method. The SecureClassLoader then creates a new ProtectionDomain passing the CodeSource and its associated permissions as arguments to its constructor. (This, of course, assumes that a ProtectionDomain does not already exist for the given CodeSource. If you load a class from an existing CodeSource then its already established ProtectionDomain will be reused.) Finally, the loaded class bytes are used to define a class to the JVM, with a reference pointer being maintained to the associated ProtectionDomain.

One ProtectionDomain is created by default and is treated "special," in that the code belonging to this domain is deemed trusted and can be granted special privileges. This is called the system domain and includes classes loaded by the system (application) loader, the extensions loader, and the bootstrap loader. (See Resources for more about Java class loaders.)

Dynamic permissions

Up until the Java platform 1.3, you could only create a ProtectionDomain by using the constructor (described above) that accepted the CodeSource and the associated permissions as arguments. This implied that all the permissions granted to a specific ProtectionDomain had to be known at construction time and there was no flexibility in being able to dynamically refresh the set of granted permissions. With the Java 2 platform SDK 1.4, however, a ProtectionDomain can encapsulate both static permissions (the set passed via its constructor) and dynamic permissions.

Dynamic permissions are granted by the policy in effect at the instant of a permission check and are implicitly handled by the ProtectionDomain. The ProtectionDomain calls the getPolicyNoCheck() method on the installed Policy class whenever the implies() method is called on it (essentially, when a permission is to be checked). The Policy class thereby is given the option of refreshing the set of granted permissions and returning this refreshed set to the caller ProtectionDomain. This ensures that the requested permission is checked against the combination of the PermissionCollection supplied at construction and the Policy binding at that instant.


Runtime access checks

The responsibility of enforcing a system security policy lies with a class called SecurityManager. The security manager is not installed by default and has to be explicitly specified via an environment variable called java.security.manager passed to the JVM on startup. Any application can ask for the installed SecurityManager and call the appropriate check<XXX> method on it. If the permission in question is granted in the given runtime context, the call will return quietly. If the permission is not granted a java.security.AccessControlException will be thrown.

In the days of Java 1.1, the SecurityManager was responsible for managing all the permissions itself via its internal logic. Therefore, any application requiring custom logic for making access decisions had to implement and install a custom SecurityManager. The Java 2 platform security architecture made this simpler and more extensible by introducing a new class called AccessController. The purpose of this class is the same as that of SecurityManager; namely, it is responsible for making access decisions. The SecurityManager class has, of course, been retained for backward compatibility, but its newer implementation delegates to the underlying AccessController. All check<XXX> method calls made on the SecurityManager class are translated to an appropriate Permission object, which is then passed as the input parameter to the checkPermission() method of the AccessController class.

Threads of execution in Java programs

Access to "protected" resources may be requested at different times during the execution of a Java program. When I speak of an executing Java program I mean a thread of execution that starts in a certain class C1 (within a specific method thereof, such as main()), passes through classes C2 through Cn-1 and "ends" in class Cn. Here's a typical flow of control for an executing Java program:

The main() method of class C1 is called -> main() method of C1 calls method mC2 of class C2 -> mC2 method of C2 calls method mC3 of class C3 -> ... -> mCn-1 method of class Cn-1 calls method mCn of class Cn.

Assuming that the method mCn must access a protected resource in order to proceed with its functionality, it calls upon the AccessController in effect in the system to confirm whether it is okay to continue with the requested access to the specified "protected" resource. If the AccessController gives the go-ahead, the concerned activity is performed and control returns to the caller (the method mCn-1 of class Cn-1) which, in turn, returns control to its caller (the method mCn-2 of class Cn-2) and so on and so forth.

The flow of control followed by the thread is represented within the JVM as a stack of frames. Each frame basically maintains information about the specific method mCk, the owning class Ck, and the variables/parameters for that method call. A typical call stack is shown in Figure 1.


Figure 1. Snapshot of a typical call stack
A flow of control represented as a stack of frames

Each class in the above stack belongs to a ProtectionDomain where it is uniquely identified by its CodeSource. In general, the set of ProtectionDomains thus traversed will contain <=n elements. (As you will recall, each ProtectionDomain in the set has an associated set of permissions -- Pi.) A call stack snapshot like the one shown in Figure 1 would be codified as an AccessControlContext and returned by native method calls provided via the AccessController object.


Access checking internals

The basic algorithm for arriving at the final, applicable set of permissions is to compute the intersection of all Pis. In other words, a specific permission is applicable to a given thread of execution only if that permission is associated with the ProtectionDomain corresponding to each and every class Ci present on the execution stack of that thread at that particular instant.

The correctness of this algorithm is intuitively clear. By computing the intersection of the permission sets associated with all the ProtectionDomains on the call stack, it is ensured that a system class (typically associated with a larger, if not complete, set of permissions) does not "leak" permissions to an application class (typically associated with a smaller set of permissions) by virtue of one of the two classes (system / application) calling into the other. Essentially, a class belonging to a less powerful domain cannot become more powerful by calling into a class belonging to a more powerful domain; and a class belonging to a more powerful domain loses its additional powers when calling into a less powerful one. See Resources for a formal proof of the algorithm.

Access control methods

The algorithm for determining the intersection of permission sets is indirectly implemented in the checkPermission method of the AccessController class. In essence, what happens upon calling this method is that a snapshot of the state of the call stack is taken at that very instant and the sets of permissions intersected with each other. The requested permission must be contained within or implied by the intersection outcome. If this check evaluates to true, the checkPermission() method returns quietly; if not, an exception is thrown. (As must be obvious, the last frame in the call stack depicted in Figure 1 is actually the call to the checkPermission() method of the AccessController class.)

Note that until now I have made no mention of the origins of the thread whose call stack was depicted in Figure 1. This thread T2 may have been created by another thread T1 when the latter was at a certain point in its call stack, given that the JVM maintains a separate call stack for each thread of execution in the system. It is intuitive to assume that T2 will inherit the T1 call stack (only the part that T1 has already run through) in order to ensure that the permission sets of the inherited ProtectionDomains are also intersected with the permission sets of the ProtectionDomains of T2's own call stack. This will guarantee that a child thread (T2 in this case) doesn't inadvertently get a certain permission that was denied to its parent (T1 in this case).

Cross-domain calling issues

When a class belonging to a less powerful domain calls into a class belonging to a more powerful one, a peculiar situation can result. The more powerful domain(class), for example Cn, which does have the permission to access the "protected" resource in question, will be prevented from doing so only because it happened to be called by a less powerful domain(class), Cn-1, which doesn't have the relevant permission. What happens if Cn absolutely must get access to the protected resource in order to work? Shouldn't there be a mechanism by which Cn can tell the security system to ignore the permissions set of its caller (and its caller in turn, and so on and so forth, up to the class at the top of the call stack) in determining the effective set of permissions?

As it turns out, the Java 2 platform security architecture provides a mechanism to do precisely that. The AccessController class has a method called doPrivileged (actually many variations of this method are provided, but the basic idea remains the same) that marks the relevant frame in the call stack with a special flag. When a call is made to the checkPermission method in that thread of execution, the permissions sets of classes occurring only on and below this stack frame are intersected with each other. The permissions sets of the caller class and its ancestors (that is, all stack frames above this one) are not included in computing the intersection set.

It isn't hard to see why the permissions sets of all classes occurring below in the call stack are included in the intersection operation: You need to account for the case where a class belonging to a more powerful domain calls into a class belonging to a less powerful one. More explicitly, you need to prevent a more powerful domain (Ck) passing on its additional powers to a less powerful domain (Ck+1).

All variations of the doPrivileged method accept an object of type PrivilegedAction as input. This object must have a method called run(), which is executed by the runtime once the current frame in the call stack is specially marked as described above. Therefore, any time you have some code that you want to be executed such that its permissions are temporarily granted to the code in the preceding call stack frames, you must wrap up code in the form of a PrivilegedAction and call the doPrivileged() method of the AccessController with this object as input.

Eager versus lazy access checks

The access checking algorithm has been described as computing an intersection of the sets of permissions of all the ProtectionDomains on the call stack. You can compute this intersection using an eager approach or a lazy approach. For details on these two methods, see the accompanying Eager versus lazy access checks sidefile.

Call stack optimization

The call stack snapshot you saw in Figure 1 (or the AccessControlContext) is obtained when a checkPermission call is made on the AccessController. Internally, the AccessController makes a number of optimizations while determining this call stack in order to make the access check loop as fast as possible. These optimizations include the following:

  • The ProtectionDomains returned are only up to (and including) the first stack frame that is specially marked via the doPrivileged call to the AccessController. The reason for doing so should be clear from the discussion of the doPrivileged call.

  • The ProtectionDomains returned do not include the system domain. The system domain has all permissions by definition, so there is no need to check if the requested permission is so "implied" (it will always be).

  • The ProtectionDomains returned are all unique (that is, if multiple stack frames correspond to the same ProtectionDomain, the ProtectionDomain will be returned only once).

After the current AccessControlContext is exhaustively searched without an AccessControlException being thrown, the same search is begun on the AccessControlContext that was "inherited" by this thread from its parent thread at the time of its creation (AccessControlContext is inherited, that is, a grandchild thread will inherit the call stacks of all its ancestors).

A variation of the doPrivileged() method

You saw previously that a call to the doPrivileged() method of the AccessController marks the current frame in the call stack with a special flag indicating that all preceding frames in the control flow should not be considered for access checking. You also saw that the call stack snapshot (or the AccessControlContext) is obtained when a checkPermission call is made on the AccessController. However, this AccessControlContext may not always be the right one to use for determining whether the requested permission is to be granted or not. For example, the request may have been initiated by a client and sent to a server for processing. The server will typically execute the request fulfillment code on behalf of the client.

As part of its processing to fulfill the request, if the server were to make a call to AccessController then the call stack returned would be that of the server. Clearly, you do not want to use (only) the AccessControlContext of the server in granting permissions to the client. (Of course, you do want to ensure that the server code itself has the relevant permission for whatever protected resource it is trying to access; however, it is more important to ensure that the client has the relevant permission for the protected resource that the server is about to access on its behalf). Servers are typically run with the relevant permissions granted to them already; therefore, you really want to use the call stack that existed on the client side at the point of time it sent the request to the server.

The AccessController class provides another variant of the doPrivileged() method that accepts an instance of AccessControlContext as input. Assuming that the client manages to obtain a copy of its AccessControlContext (the AccessController class provides a method for this purpose) and pass it to the server, the server can execute the request fulfillment code as a PrivilegedAction via a doPrivileged call that accepts the above AccessControlContext obtained from the client as input.

In this case, the algorithm for permission checking (assuming that somewhere down the line, following the call to the run() method of the PrivilegedAction, a checkPermission call is made) proceeds by executing the loop described above until the specially marked frame is encountered in the call stack; at this point, a call is made to the checkPermission() method on the AccessControlContext object that was passed as input. This call leads to essentially the same algorithm being executed, but on the call stack encapsulated in this AccessControlContext (belonging to the client).


Wherefore user-centric authorization?

The code-centric authorization model of the Java 2 platform security architecture was based on the assumption that the user had to be protected from the world. In order to ensure that a malicious Java program (written by the evil dudes of the world) could do no damage to the user's system, all mobile code was deemed untrusted and required to possess special access permissions in order to perform even the most benign activity.

In contrast, the user-centric authorization model of JAAS was developed with the idea of protecting the world from the user. With increased mobility and the emergence of enterprise networks came a revised definition of the concept of trust. In real life, if I trust person X more than I trust person Y, I will allow X greater liberties than I will Y. Similarly, given the fact that a Java application will be used by multiple users (some of whom may, in fact, be evil dudes), it is best to extend access permissions on a per-user basis. Under this new model, each user is authorized to use an application's range of functionality according to the degree to which he or she is trusted.

In the sections that follow, I'll focus on the user-centric authorization model of the Java Authentication and Authorization Service. While JAAS represented a sea change in the values of the Java platform security architecture (that is, it moved us from a code-based model to a user-based one), you'll find that many of its underlying components are familiar, although they've been updated to meet a new set of requirements.


The JAAS authorization architecture

JAAS was first introduced as an extension to the JDK and became part of the core JDK with version 1.4. Given that the purpose of JAAS is to enable you to control what any given piece of code can do on a per-user basis, it makes sense that you must first be able to precisely and uniquely identify users; in other words, you must be able to authenticate them. While I won't spend much time on the "Authentication" part of JAAS here (see Resources for further references on the topic), I will be focusing on one of its core components: the Subject class.

JAAS is, as has been previously stated, a user-centric approach to authorization. Under JAAS, the pertinent question is no longer (as it was in the Java 2 platform security architecture) "What can this code do?"; but rather, "What are the access permissions for this authenticated user?". As such, I will focus the remainder of this article on learning about the role of the Subject class in JAAS, with an in-depth discussion of subject-based access control.


Subject-based access control

The Subject class is used to represent an authenticated user in a given system (that is, a populated Subject is the outcome of the JAAS authentication process). Internally, a Subject contains (among other information about the user) an array of Principal objects where each Principal represents a different "identity" of the same user. For example, one Principal could be my user ID on an end system and another could be a "group" that I belong to on the same system.

I talked earlier about how the Policy in effect in a system sets up a mapping between a ProtectionDomain (along with the classes that "belong" to it as identified by the associated CodeSource) and the permissions granted to it. JAAS introduced an enhancement to this concept by requiring that a ProtectionDomain could be further characterized (over and above the CodeSource) by a set of Principals. After a system Policy sets up a mapping between such a ProtectionDomain (that is, one that is characterized by a set of Principals in addition to a CodeSource) and the permissions granted to it, the Principal objects contained in the Subject corresponding to the authenticated user running the code must match the Principal objects contained in that ProtectionDomain if the latter's permissions are to be used to check whether a certain requested permission should be granted to the user.

Given that the Java 2 platform already had a clean, efficient authorization implementation using the call stack (via AccessControlContext) it was much easier to retain it and simply provide a mechanism to "inject" the identity of the user who was running the code (as provided by the Subject for the user) into the ProtectionDomains on the call stack at the instant of the permission check.

For this purpose, the JAAS Subject class provides two static methods called doAs and doAsPrivileged. As input, these methods expect the Subject instance of the authenticated user and an instance of PrivilegedAction (whose run() method should contain the business logic that needs to access the protected resource). The basic idea is that the application should first and foremost try to authenticate the user; once a Subject has been established for the authenticated user, each and every action that the user might want to perform will be wrapped as a PrivilegedAction and executed by the application as the Subject (as indicated by the name of the method itself -- doAs()!). There are subtle but important differences between the two methods, as you shall see shortly.

In order to be able to perform the action as the Subject, the Subject must be introduced (or injected) into the ProtectionDomains on the call stack. This is achieved with the help of a dedicated interface called DomainCombiner, which I will discuss briefly before going into the internals of the doAs() and doAsPrivileged() methods.

The DomainCombiner

As previously mentioned, given an AccessControlContext (a call stack), the injection of the Subject into the ProtectionDomains on the stack is handled in JAAS via implementations of the DomainCombiner interface (one specific implementation being a SubjectDomainCombiner).

The injection is performed during the construction of the AccessControlContext by passing in the SubjectDomainCombiner as a constructor parameter. (The Subject that was passed as a parameter to the doAs call is encapsulated within the SubjectDomainCombiner object by being passed as a constructor parameter when the latter is being created.) However, the real work is performed in the combine() method of the SubjectDomainCombiner. You'll see what happens inside this method in a moment.


The Subject.doAs() method

An application may be expected to call the Subject.doAs() method after authenticating the user (that is, once a Subject is available for the user). Internally, the following activities happen as a result of this call:

  1. The AccessControlContext for the current thread of execution is obtained by making a call to the getContext() method of AccessController. Note, of course, that this call stack will be optimized in the ways described earlier.

  2. A SubjectDomainCombiner encapsulating the authenticated Subject is created.

  3. A new AccessControlContext object is then created with the AccessControlContext from step 1 and the SubjectDomainCombiner from step 2.

  4. A call is made to the doPrivileged() method of the AccessController, passing the PrivilegedAction instance and the AccessControlContext (referred to as "privileged AccessControlContext" henceforth) created in step 3 as parameters to it.

  5. The privileged AccessControlContext is saved internally by the runtime and the run() method of the PrivilegedAction object is executed. During the execution of the run() method, as previously described, a call will be made to the checkPermission() method of the AccessController class when a protected resource needs to be accessed.

  6. Internally, this call leads to the AccessController asking for the current call stack (that is, the AccessControlContext). The AccessControlContext thus returned by the runtime contains the privileged AccessControlContext introduced in step 4.

  7. This AccessControlContext, as was previously mentioned, must be optimized before checking the ProtectionDomains of its frames for the requested permission. As part of this optimization process, the SubjectDomainCombiner encapsulated in the privileged AccessControlContext is asked to combine the ProtectionDomains currently on the call stack and the ProtectionDomains present inside the privileged AccessControlContext. The combine process works as follows:

    1. First, the ProtectionDomains of the privileged AccessControlContext are optimized to remove all System and duplicate domains.

    2. Next, the ProtectionDomains on the current call stack are optimized to remove System domains as well as domains that are already present in the privileged AccessControlContext. At this point, both arrays of ProtectionDomains you are left with are devoid of all System domains and contain only distinct domains.

    3. For each optimized ProtectionDomain that you are left with in step b, a new ProtectionDomain is created such that it is the replica of the original with respect to attributes such as CodeSource and Permissions but contains in addition the list of Principals associated with the Subject contained within this SubjectDomainCombiner.

    4. The optimized list of ProtectionDomains (from step a) is appended to the newly created list of ProtectionDomains (from step c). A new AccessControllerContext is created with this combined list of ProtectionDomains and the SubjectDomainCombiner and returned.
  8. Now that you have an optimized AccessControlContext (in which the list of Principals for that Subject are associated with each of the ProtectionDomains on the current call stack), you can safely call the checkPermission() method on it.

  9. This call to the checkPermission() method leads to the runtime to iterate over the list of ProtectionDomains contained in this AccessControlContext as per the loop described above and check that each of those ProtectionDomains imply the requested Permission. A noteworthy fact here is that the list of ProtectionDomains checked will include both the ProtectionDomains from the current call stack (that have been associated with the Principals contained within the authenticated Subject) and the ProtectionDomains from the privileged AccessControlContext (the call stack immediately before the call to the doAs() method) that have not been associated with the Principals contained within the authenticated Subject. The requested Permission must be implied by all of these ProtectionDomains.

Another effect of calling the Subject.doAs() method is that the identity of the authenticated user (that is, the Subject) is available in any code that is (directly or indirectly) reached via the run() method of the PrivilegedAction. The way to get the Subject is as follows:

  1. Get a handle to the current AccessControlContext via a call to the getContext() method of the AccessController. Internally, this method returns an optimized AccessControlContext in the exact same manner as was obtained in step 7d above.

  2. Call the static getSubject() method of the Subject class, passing the AccessControlContext obtained above as input parameter. Internally, this checks that the caller has the javax.security.auth.AuthPermission for the getSubject() method before proceeding to the next step.

  3. Internally, this call extracts the SubjectDomainCombiner contained within the AccessControlContext, extracts the Subject out from the extracted SubjectDomainCombiner and returns it.

The Subject thus returned reveals the identity of the authenticated user that can be used for logging and/or data-level authorization purposes, etc.


The Subject.doAsPrivileged() method

As you saw in the case of the doAs() method, the requested Permission must be implied by the ProtectionDomains that were present on the call stack even before the call to doAs happened. This may not always be desirable for a reason that must be familiar by now.

As mentioned during the discussion on the variant of the doPrivileged() method of the AccessController class (the one that accepts an AccessControlContext for permission checking), the PrivilegedAction may actually represent some activity that the server is performing on behalf of a client (and more specifically, as the client; that is, as if the server had assumed the identity of the client on whose behalf it was performing the action). In such a situation, the snapshot of the call stack before the call to doAs will contain ProtectionDomains for the server's internal code and it clearly doesn't make sense for these ProtectionDomains to have to imply an arbitrary requested Permission. What is desirable, however, is one of the following:

  • Case I: The ProtectionDomains on the call stack on the client side (the snapshot at the instant when the client sent the request to the server) should be used for checking for the requested Permission (along with the server-side call stack ProtectionDomains that have been associated with the user identity); or

  • Case II: Only the server-side call stack ProtectionDomains that have been associated with the user identity should be used for the permission check.

This facility is provided via the static doAsPrivileged() method of the Subject class. This method accepts a Subject and a PrivilegedAction as inputs (just like the doAs() method); however, it accepts an additional AccessControlContext. So, a client can arrange to take a snapshot of the AccessControlContext on its side and send it to the server so that it can be passed in the doAsPrivileged call. This enables handling of Case I above. Otherwise, the call to doAsPrivileged can pass null in place of the AccessControlContext, which enables handling of Case II above.

Internally, the steps of the doAsPrivileged() method are as follows:

  1. An intermediate AccessControlContext is created; it points to either the passed-in AccessControlContext (if it is non-null) or to a newly created AccessControlContext (with an empty list of ProtectionDomains) if null.

  2. All steps numbered 2 through 9 are the same as before. As should be clear, when you reach step 9, the list of ProtectionDomains that will be ultimately used for permission checking will be either a combined list of ProtectionDomains on the server call stack that have been injected with the Principals list of the authenticated user plus the (unadorned) ProtectionDomains from the client call stack; or a list of only ProtectionDomains on the server call stack that have been injected with the Principals list of the authenticated user. This is what you set out to achieve.

A discrepancy in the authorization model

I've covered a lot of ground in this guided tour of Java authorization internals. You've learned the underpinnings of both the code-based authorization model of the original Java 2 platform security architecture and the user-based authorization framework introduced with JAAS. In this final leg of the tour, you'll learn about a discrepancy in the JAAS authorization model and I'll describe a practical method for addressing it.

Hey, where did my Subject go?

Suppose that an application has authenticated the user and set up a Subject for her. The user requests a certain function, so the app makes a call to the doAsPrivileged() method and passes in the authenticated Subject and the PrivilegedAction that incorporates the desired functionality. The AccessControlContext is passed in as null, ensuring that the permission checking is done only with respect to the ProtectionDomains on the call stack after the call to doAsPrivileged.

Consider the execution of the run() method of the PrivilegedAction instance. As you saw previously, a piece of code inside this PrivilegedAction should be able to ask for and get the authenticated Subject. Now suppose that somewhere during the flow of control through this method, a call gets made to the doPrivileged() method of the AccessController (specifically the doPrivileged that accepts only a PrivilegedAction instance) and the PrivilegedAction that is executed within this (embedded) doPrivileged call also needs to get the identity of the authenticated user.

As before, the first step is to get a handle to the current AccessControlContext via a call to the getContext() method of the AccessController. As you will remember from the discussion previously on the Subject.doAs() method, there was a privileged AccessControlContext (containing the SubjectDomainCombiner encapsulating the authenticated Subject) returned along with the current call stack, so the optimization process could actually inject the list of Principals from the Subject into the final list of ProtectionDomains. However, because a fresh call has been made to the doPrivileged() method of AccessController, a new privileged element is allocated and the current thread of execution updated with this element as the topmost privileged element. Because there is no AccessControlContext passed to the doPrivileged call, this privileged element doesn't have any privileged AccessControlContext associated with it, unlike the previously mentioned case. The call to getContext essentially returns the call stack going only up to this topmost privileged element; hence, the information about the authenticated Subject is unavailable for the duration of this execution.

Of course, once the execution of the inner PrivilegedAction finishes, this privileged element is popped off the stack and any call to getContext will again go back to returning the AccessControlContext that contains the privileged AccessControlContext (which in turn contains the SubjectDomainCombiner encapsulating the authenticated Subject). Therefore, the authenticated Subject will once again be obtainable after the PrivilegedAction called from inside the Subject.doAs() method finishes.

A practical workaround

One way of getting around this problem is to create a custom SubjectHolder class that wraps a static ThreadLocal to store the current Subject. The authenticated Subject can be stored in this SubjectHolder immediately following authentication and before calling the doAs() method. Once this is done, any code that is executed (directly or indirectly, whether or not it is wrapped within another PrivilegedAction) will be able to get the authenticated Subject simply by asking the SubjectHolder to return the contents of the ThreadLocal variable.

The WebSphere Application Server offers one example of this workaround. The application server provides a helper class, WSSubject, which has static doAs() and doAsPrivileged() methods with identical signatures as the Subject class. The WSSubject.doAs() method basically associates the user credentials with the current execution thread (that can be used for Enterprise JavaBeans (EJB) components invocation) before invoking the corresponding Subject.doAs() method. The original credential is restored and associated with the execution thread upon leaving the WSSubject.doAs() method.


Conclusion

This article has presented an in-depth look at Java authorization under both the Java 2 platform security architecture and JAAS. Upon completing the article (or tour) you should have a complete understanding of both the conceptual basis of each authorization framework and its underlying mechanisms.

Together, the Java 2 platform security architecture and JAAS comprise the current Java authorization model. I've documented a discrepancy in the JAAS authorization model and also described a practical workaround, offering a real-world implementation as its example.


Resources

About the author

Abhijit Belapurkar has a bachelor of technology degree in computer science from the Indian Institute of Technology (IIT), Delhi, India. He has worked in the areas of architectures and information security for distributed applications for almost 10 years and has used the Java platform to build n-tier applications for more than five years. He presently works as a senior technical architect in the J2EE space, with Infosys Technologies Limited, Bangalore, India.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=10938
ArticleTitle=Java authorization internals
publish-date=05042004
author1-email=abhijit_belapurkar@infosys.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers