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.
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.
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.
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 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.)
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.
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:
Themain()method of classC1is called ->main()method ofC1calls methodmC2of classC2->mC2method ofC2calls methodmC3of classC3-> ... ->mCn-1method of classCn-1calls methodmCnof classCn.
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
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.
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.
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).
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.
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 thedoPrivilegedcall to theAccessController. The reason for doing so should be clear from the discussion of thedoPrivilegedcall. - 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 sameProtectionDomain, theProtectionDomainwill 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.
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.
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.
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:
- The
AccessControlContextfor the current thread of execution is obtained by making a call to thegetContext()method ofAccessController. Note, of course, that this call stack will be optimized in the ways described earlier. - A
SubjectDomainCombinerencapsulating the authenticatedSubjectis created. - A new
AccessControlContextobject is then created with theAccessControlContextfrom step 1 and theSubjectDomainCombinerfrom step 2. - A call is made to the
doPrivileged()method of theAccessController, passing thePrivilegedActioninstance and theAccessControlContext(referred to as "privilegedAccessControlContext" henceforth) created in step 3 as parameters to it. - The privileged
AccessControlContextis saved internally by the runtime and therun()method of thePrivilegedActionobject is executed. During the execution of therun()method, as previously described, a call will be made to thecheckPermission()method of theAccessControllerclass when a protected resource needs to be accessed. - Internally, this call leads to the
AccessControllerasking for the current call stack (that is, theAccessControlContext). TheAccessControlContextthus returned by the runtime contains the privilegedAccessControlContextintroduced in step 4. - This
AccessControlContext, as was previously mentioned, must be optimized before checking theProtectionDomainsof its frames for the requested permission. As part of this optimization process, theSubjectDomainCombinerencapsulated in the privilegedAccessControlContextis asked to combine theProtectionDomainscurrently on the call stack and theProtectionDomainspresent inside the privilegedAccessControlContext. Thecombineprocess works as follows: - First, the
ProtectionDomainsof the privilegedAccessControlContextare optimized to remove all System and duplicate domains. - Next, the
ProtectionDomainson the current call stack are optimized to remove System domains as well as domains that are already present in the privilegedAccessControlContext. At this point, both arrays ofProtectionDomainsyou are left with are devoid of all System domains and contain only distinct domains. - For each optimized
ProtectionDomainthat you are left with in step b, a newProtectionDomainis created such that it is the replica of the original with respect to attributes such asCodeSourceandPermissionsbut contains in addition the list ofPrincipals associated with theSubjectcontained within thisSubjectDomainCombiner. - The optimized list of
ProtectionDomains (from step a) is appended to the newly created list ofProtectionDomains (from step c). A newAccessControllerContextis created with this combined list ofProtectionDomains and theSubjectDomainCombinerand returned. - Now that you have an optimized
AccessControlContext(in which the list ofPrincipals for thatSubjectare associated with each of theProtectionDomains on the current call stack), you can safely call thecheckPermission()method on it. - This call to the
checkPermission()method leads to the runtime to iterate over the list ofProtectionDomains contained in thisAccessControlContextas per the loop described above and check that each of thoseProtectionDomains imply the requestedPermission. A noteworthy fact here is that the list ofProtectionDomains checked will include both theProtectionDomains from the current call stack (that have been associated with thePrincipals contained within the authenticatedSubject) and theProtectionDomains from the privilegedAccessControlContext(the call stack immediately before the call to thedoAs()method) that have not been associated with thePrincipals contained within the authenticatedSubject. The requestedPermissionmust be implied by all of theseProtectionDomains.
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:
- Get a handle to the current
AccessControlContextvia a call to thegetContext()method of theAccessController. Internally, this method returns an optimizedAccessControlContextin the exact same manner as was obtained in step 7d above. - Call the static
getSubject()method of theSubjectclass, passing theAccessControlContextobtained above as input parameter. Internally, this checks that the caller has thejavax.security.auth.AuthPermissionfor thegetSubject()method before proceeding to the next step. - Internally, this call extracts the
SubjectDomainCombinercontained within theAccessControlContext, extracts theSubjectout from the extractedSubjectDomainCombinerand 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
ProtectionDomainson 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 requestedPermission(along with the server-side call stackProtectionDomains that have been associated with the user identity); or - Case II: Only the server-side call stack
ProtectionDomainsthat 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:
- An intermediate
AccessControlContextis created; it points to either the passed-inAccessControlContext(if it is non-null) or to a newly createdAccessControlContext(with an empty list ofProtectionDomains) if null. - 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 ofProtectionDomains on the server call stack that have been injected with thePrincipals list of the authenticated user plus the (unadorned)ProtectionDomains from the client call stack; or a list of onlyProtectionDomains on the server call stack that have been injected with thePrincipals 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.
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.
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.
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.
- Daniel Wallach and Edward Felten's "Understanding Java
Stack Inspection" (IEEE Symposium on Security and Privacy, May 1998) offers a formal look at the stack-inspection based access control algorithm described in this article.
- For a good introduction to Java security in general, and JAAS authentication and authorization in particular, see "Java security, Part 2: Authentication and authorization" (developerWorks, July 2002).
- For a more hands-on approach to understanding JAAS, try "Extend JAAS for class instance-level authorization" (developerWorks, April 2002).
- For an enterprise-level look at the J2EE Web application security architecture, see Kyle Gabhart's "Java security with JAAS and JSSE" (developerWorks, November 2003).
- You can learn more about the Java Secure Socket Extension with Greg Travis's "Using JSSE for secure socket communication"
(developerWorks, April 2002).
- Stuart Halloway provides a complete introduction to secure class loaders in his book, Component Development for the Java Platform (Addison-Wesley, First edition, December 2001).
- Jerome Saltzer and Michael Schroeder's "The Protection
of Information in Computer Systems" (Proceedings of the IEEE, September 1975) is an essential document in the history of information system security.
- You'll find articles about every aspect of Java programming in the developerWorks Java technology zone.
- Browse for books on these and other technical topics.
- Also see the Java technology zone tutorials page for a complete listing of free Java-focused tutorials from developerWorks.
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.





