Sponsored Link •
|
Advertisement
|
doPrivileged()
Method The basic algorithm illustrated so far in this chapter, in which the
AccessController
inspects the stack from top to bottom, stubbornly requiring that
every frame have permission to perform an action, prevents less trusted code from hiding behind more
trusted code. Because the AccessController
looks all the way down the call stack,
it will eventually find any method that isn't trusted to perform the requested action. For example, even
though the untrusted Stranger
object of Example2b
places the
trusted code of Friend
and TextFileDisplayer
between it
and the Java API method that attempts to open file answer.txt
, the untrusted
Stranger
code is unable to hide behind that trusted code. As shown in Figure 3-7,
although the AccessController
must look through eight frames that have
permission to read answer.txt
before it encounters frame two, it eventually reaches
frame two. And once it arrives at frame two, it discovers the doYourThing()
method
of class Stranger
, whose associated protection domain doesn't have permission to
read answer.txt
. As a result of this discovery, the
AccessController
throws an
AccessControllerException
, thereby disallowing the read.
The basic AccessController
algorithm prevents any code from performing or
causing to be performed any action that the code is not trusted to do. Methods belonging to a less powerful
protection domain, therefore, are unable to gain privileges by invoking methods belonging to more powerful
protection domains. The basic algorithm also implies that methods belonging to more powerful protection
domains must give up privileges when calling methods belonging to less powerful protection domains.
Although the basic algorithm provides behavior that is desirable in general, the
AccessController
's stubborn insistence that all frames on the call stack have
permission to perform the requested action can at times be a bit restrictive.
Sometimes code farther up the call stack (closer to the top of the stack) might wish to perform an action
that code farther down the call stack may not be allowed to do. For example, imagine that an untrusted
applet asks the Java API to render a string of text in bold Helvetica font on its applet panel. To fulfill this
request, the Java API may need to open a font file on the local disk to load a bold Helvetica font with which
to render the text on behalf of the applet. The class making the explicit request to open the font file, because
it belongs to the Java API, likely has permission to open the file. However, the code of the untrusted applet,
which is represented by a stack frame farther down the call stack, likely doesn't have permission to open the
file. Given just the basic algorithm, the AccessController
would prevent the
opening of the font file because the code for the untrusted applet, sitting somewhere on the stack, doesn't
have permission to open the file.
To enable trusted code to perform actions for which less trusted code farther down the call stack may
not have permission to do, the AccessController
class offers four overloaded
static methods named doPrivileged()
. Each of these methods accepts as a
parameter an object that implements either the
java.security.PrivilegedAction
or
java.security.PrivilegedExceptionAction
interface. Both of these
interfaces declare one method named run()
that takes no parameters and returns
void
. The only difference between these two interfaces is that whereas
PrivilegedExceptionAction
's run()
method declares
Exception
in its throws
clause,
PrivilegedAction
declares no throws
clause. To perform an
action despite the existence of less trusted code farther down the call stack, you create an object that
implements one of the PrivilegedAction
interfaces, whose
run()
method performs the action, and pass that object to doPrivileged()
.
When you invoke doPrivileged()
, as when you invoke any method, a new
frame is pushed onto the stack. In the context of a stack inspection by the
AccessController
, a frame for a doPrivileged()
method
invocation signals an early termination point for the inspection process. If the protection domain associated
with the method that invoked doPrivileged()
has permission to perform the
requested action, the AccessController
returns immediately. It allows the action
even if code farther down the stack doesn't have permission to perform the action.
If an untrusted applet asks the Java API to render a test string on its applet panel, therefore, the Java
API code can open the local font file by wrapping the file open action in a
doPrivileged()
call. The AccessController
will allow
such a request even though the untrusted applet code doesn't have permission to open the file. Because the
frame for the untrusted applet code is below the frame for the doPrivileged()
invocation by the Java API code, the AccessController
won't even consider the
permissions of the untrusted applet code.
For an example of a doPrivileged()
method invocation, consider again the
doYourThing()
method of class Friend
:
// On CD-ROM in file // security/ex2/com/artima/security/friend/Friend.java package com.artima.security.friend; import com.artima.security.doer.Doer; import java.security.AccessController; import java.security.PrivilegedAction; public class Friend implements Doer { private Doer next; private boolean direct; public Friend(Doer next, boolean direct) { this.next = next; this.direct = direct; } public void doYourThing() { if (direct) { next.doYourThing(); } else { AccessController.doPrivileged( new PrivilegedAction() { public Object run() { next.doYourThing(); return null; } } ); } } }
If the direct
instance variable is false
,
Friend
's doYourThing()
method will simply invoke
doYourThing()
directly on the next
reference. But if
direct
is true
, doYourThing()
will
wrap the invocation of doYourThing()
on the next
reference
in a doPrivileged()
call. To do so, Friend
instantiates an
anonymous inner class that implements PrivilegedAction
whose
run()
method invokes doYourThing()
on
next
, and passes that object to doPrivileged()
.
To see Friend
's doPrivileged()
invocation in action,
consider the Example2c
application from the security/ex2
directory of the CD-ROM:
// On CD-ROM in file security/ex2/Example2c.java import com.artima.security.friend.Friend; import com.artima.security.stranger.Stranger; // This succeeds because Friend code executes a // doPrivileged() call. (Passing false as // the second arg to Friend constructor causes // it to do a doPrivileged().) class Example2c { public static void main(String[] args) { TextFileDisplayer tfd = new TextFileDisplayer("answer.txt"); Friend friend = new Friend(tfd, false); Stranger stranger = new Stranger(friend, true); stranger.doYourThing(); } }
Only one difference exists between the main()
method of the
Example2c
application and the main()
method of the previous
example, Example2b
. When the Example2b
application
instantiated the Friend
object, it passed true
as the second
parameter. Example2c
passes false
. If you look back at the
code for Friend
(and Stranger
) shown earlier in this chapter,
you'll see that this parameter is used to decide whether to invoke doYourThing()
directly on the Doer
passed as the first parameter to the constructor. Because
Example3c
passes false
, the Friend
class
will not invoke doYourThing()
directly, but will invoke it indirectly via an
AccessController.doPrivileged()
invocation.
When the Example2c
program invokes doYourThing()
on the Stranger
object referenced from the stranger
variable,
the Stranger
object invokes doYourThing()
on the
Friend
object, which (because direct
is
false
) invokes doPrivileged()
, passing in the anonymous
inner class instance that implements PrivilegedAction
. The
doPrivileged()
method invokes run()
on the passed
PrivilegedAction
object, which invokes doYourThing()
on the TextFileDisplayer
object.
As in the previous example, TextFileDisplayer
's
doYourThing()
method attempts to open and read a file named
"answer.txt"
in the current directory and print its contents to the standard output.
When TextFileDisplayer
's doYourThing()
method
creates a new FileReader
object, the FileReader
constructor creates a new FileInputStream
, whose constructor checks to see
whether or not a security manager has been installed. Once again, the concrete
SecurityManager
has been installed, so the
FileInputStream
's constructor invokes checkRead()
on the
concrete SecurityManager
. The checkRead()
method
instantiates a new FilePermission
object representing permission to read file
answer.txt
and passes that object to the concrete
SecurityManager
's checkPermission()
method, which
passes the object on to the checkPermission()
method of the
AccessController
. The AccessController
's
checkPermission()
method performs the stack inspection to determine whether
this thread should be allowed to open file answer.txt
for reading. The stack appears
as shown in Figure 3-8.
Example2c
: stops at frame three.
The call stack to be inspected in Example2c
looks similar to the call stacks
inspected in Example2a
and Example2b
. The difference is that
Example2c
's call stack has two extra frames: frame four, which represents the
doPrivileged()
invocation, and frame five, which represents the
run()
invocation on the PrivilegedAction
object. As
always, stack inspection starts at the top of the stack and proceeds on down the stack towards frame one.
But once again, the inspection process will not actually reach frame one. When the
AccessController
reaches frame four, it discovers a
doPrivileged()
invocation. As a result of this discovery, the
AccessController
makes one more check: it checks that the code represented by
frame three, the code that invoked doPrivileged()
, has permission to read
answer.txt
. Because frame three is associated with the FRIEND protection domain,
that does have permission to read question.txt
, the
AccessController
's checkPermission()
method returns
normally. Because the AccessController
stopped its inspection at frame three, it
never considered frame two, which because it is associated with the STRANGER protection domain,
doesn't have permission to read answer.txt
. Thus, by invoking
doPrivileged()
the Friend
code was able to read file
answer.txt
, even though code beneath it on the call stack doesn't have permission to
open the file.
To get the Example2c
application to work as intended, you must, as with the
previous examples, start the application with an appropriate command. When using the
java
program from the Java 2 SDK version 1.2, the appropriate command takes the
form:
java -Djava.security.manager -Djava.security.policy=policyfile.txt -Dcom.artima.ijvm.cdrom.home=d:\books\InsideJVM\manuscript\cdrom -cp .;jars/friend.jar;jars/stranger.jar Example2c
This command, which is contained in the ex2c.bat
file in the security/ex2
directory of the CD-ROM, is an example of the kind of command you'll need to use to get the example to work.
As before, to execute Example2c
on your own system, you must set the com.artima.ijvm.cdrom.home
property to the security/ex2
directory of your CD-ROM, or to whatever directory you may have copied the
security/ex2
directory from the CD-ROM. When you run this program, it should print out the contents of
answer.txt
:
Complexity threatens security to a significant extent. The more complicated a security infrastructure becomes, the more likely parties responsible for configuring security will either make mistakes that open up security holes or avoid using the security infrastructure altogether.
Sponsored Links
|