The policy for an SDK installation specifies what permissions -- which types of system resource accesses -- are allowed for code from specified code sources. A "code source" (of type CodeSource) essentially consists of the code location (URL) and a reference to the certificate(s) containing the public key(s) corresponding to the private key(s) used to sign the code (if it was signed).
The policy is represented by a Policy object. More specifically,
it is represented by a Policy
subclass providing an
implementation of the abstract methods in the Policy
class (which is in the java.security
package).
The source location for the policy information utilized by the Policy object is up to the Policy implementation. The Policy reference implementation obtains its information from policy configuration files. See Default Policy Implementation and Policy File Syntax for information about the Policy reference implementation and the syntax that must be used in policy files it reads. For information about using the Policy Tool to create a policy file (without needing to know the required syntax), see the Policy Tool documentation (for Solaris) (for Windows).
A "protection domain" encompasses a CodeSource and the permissions granted to code from that CodeSource, as determined by the security policy currently in effect. Thus, classes signed by the same keys and from the same URL are placed in the same domain, and a class belongs to one and only one protection domain. Classes that have the same permissions but are from different code sources belong to different domains.
Today all code shipped as part of the SDK is considered system code and run inside the unique system domain. System code automatically has all permissions.
Each applet or application runs in its appropriate domain, determined by its code source. In order for an applet (or an application running under a security manager) to be allowed to perform a secured action (such as reading or writing a file), the applet or application must be granted permission for that particular action.
More specifically, whenever a resource access is attempted,
all code traversed by the execution thread up to that point
must have permission for that resource access, unless some code
on the thread has been marked as "privileged". That is, suppose
access control checking occurs in a thread of execution that has a
chain of multiple callers. (Think of this as multiple method calls
that potentially cross the protection domain boundaries.) When the
AccessController
checkPermission
method
is invoked by the most recent caller, the basic algorithm for
deciding whether to allow or deny the requested access is as
follows:
If the code for any caller in the call chain does not have the requested permission, AccessControlException is thrown, unless the following is true - a caller whose code is granted the said permission has been marked as "privileged" (see below) and all parties subsequently called by this caller (directly or indirectly) all have the said permission.
Marking code as "privileged" enables a piece of trusted code to temporarily enable access to more resources than are available directly to the code that called it. This is necessary in some situations. For example, an application may not be allowed direct access to files that contain fonts, but the system utility to display a document must obtain those fonts, on behalf of the user. In order to do this, the system utility becomes privileged while obtaining the fonts.
If you don't need to return a value from within the "privileged"
block, your call to doPrivileged
can look like the
following:
somemethod() { ...normal code here... AccessController.doPrivileged(new PrivilegedAction() { public Object run() { // privileged code goes here, for example: System.loadLibrary("awt"); return null; // nothing to return } }); ...normal code here... }
The AccessController.doPrivileged
method takes an
object of type java.security.PrivilegedAction
and
invokes its run
method in privileged mode. The
implementation guarantees that privileges will be revoked after the
run
method is executed, even if execution of
doPrivileged
is interrupted by an asynchronous
exception.
PrivilegedAction
is an interface with a single
method, named run
, that returns an Object. The above
example shows creation of an implementation of that interface using
an anonymous inner class; a concrete implementation of the
run
method is supplied. When the call to
doPrivileged
is made, an instance of the
PrivilegedAction implementation is passed to it. The
doPrivileged
method calls the run
method
from the PrivilegedAction implementation after enabling privileges,
and returns the run
method's return value as the
doPrivileged
return value (which is ignored in this
example).
Note that depending on what "privileged code" actually consisted of, you might have to make some changes due to the way inner classes work. For example, if "privileged code" throws an exception or attempts to access local variables then you will have to make some changes, as shown in subsequent sections of this document.
You can also call doPrivileged
without using an
anonymous inner class, as in:
somemethod() { ...normal code here... MyAction mya = new MyAction(); // become privileged: AccessController.doPrivileged(mya); ...normal code here... } class MyAction implements PrivilegedAction { public MyAction() {}; public Object run() { // privileged code goes here, for example: System.loadLibrary("awt"); return null; // nothing to return } }
Be very careful in your use of the "privileged"
construct, and always remember to make the privileged code section
as small as possible, that is, try and limit the code within the
run
method to only the code that needs to be run with
privileges, and do more general things outside the run
method. Also note that the call to doPrivileged
should
be made in the code that wants to enable its privileges. Do not be
tempted to write a utility class that itself calls
doPrivileged
as that could lead to security holes. You
can write utility classes for PrivilegedAction
classes
though, as shown in the example above.
If you need to return a value, you can do something like the following:
somemethod() { ...normal code here... String user = (String) AccessController.doPrivileged( new PrivilegedAction() { public Object run() { return System.getProperty("user.name"); } } ); ...normal code here... }
Note that this usage requires a dynamic cast on the value
returned by doPrivileged
. An alternative is to use a
final
local variable:
somemethod() { ...normal code here... final String user[] = {null}; AccessController.doPrivileged( new PrivilegedAction() { public Object run() { user[0] = System.getProperty("user.name"); return null; // still need this } } ); ...normal code here... }Yet another alternative would be to write a non-anonymous class that safely handles types for you:
somemethod() { ...normal code here... GetPropertyAction gpa = new GetPropertyAction("user.name"); AccessController.doPrivileged(gpa); String user = gpa.getValue(); ...normal code here... } class GetPropertyAction implements PrivilegedAction { private String property; private String value; public GetPropertyAction(String prop) { property = prop;} public Object run() { value = System.getProperty(property); return value; } public String getValue() {return value;} }
Note there are now no type-casts involved, although the
run
method still returns a value, so you could still
have a "one-liner" if you wanted to:
somemethod() { ...normal code here... String user = (String) AccessController.doPrivileged( new GetPropertyAction("user.name")); ...normal code here... }
If you are using an anonymous inner class, any local variables
you access must be final
. For example:
somemethod() { ...normal code here... final String lib = "awt"; AccessController.doPrivileged(new PrivilegedAction() { public Object run() { // privileged code goes here, for example: System.loadLibrary(lib); return null; // nothing to return } }); ...normal code here... }
The variable lib
must be declared
final
if you intend to use it within the privileged
block. See the "Inner
Classes" spec for more information on this topic.
If there are cases where you can't make an existing variable
final
(because it gets set multiple times), then you
can create a new final
variable right before invoking
doPrivileged
, and set that variable equal to the other
variable. For example:
somemethod() { ...normal code here... String lib; ... // lib gets set multiple times so we can't make it final ... // create a final String that we can use inside of the run method final String fLib = lib; AccessController.doPrivileged(new PrivilegedAction() { public Object run() { // privileged code goes here, for example: System.loadLibrary(fLib); return null; // nothing to return } }); ...normal code here... }
If the action performed in your run
method could
throw a "checked" exception (one that must be listed in the
throws
clause of a method), then you need to use the
PrivilegedExceptionAction
interface instead of the
PrivilegedAction
interface:
somemethod() throws FileNotFoundException {
...normal code here...
try {
FileInputStream fis = (FileInputStream) AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws FileNotFoundException {
return new FileInputStream("someFile");
}
}
);
} catch (PrivilegedActionException e) {
// e.getException() should be an instance of FileNotFoundException,
// as only "checked" exceptions will be "wrapped" in a
// PrivilegedActionException
.
throw (FileNotFoundException) e.getException();
}
...normal code here...
}
If a checked exception is thrown during execution of the
run
method, it is placed in a
PrivilegedActionException "wrapper" exception that is then thrown
and should be caught by your code, as illustrated in the above
example..
doPrivileged()
method can be
invoked reflectively using
java.lang.reflect.Method.invoke()
. In this case, the
privileges granted in privileged mode are not those of
Method.invoke()
but of the non-reflective code that
invoked it. Otherwise, system privileges could erroneously (or
maliciously) be conferred on user code. Note that similar
requirements exist when using the existing API via reflection.