Lecture notes by
Borislav Deianov
This lecture discusses the security mechanism in the Java object-oriented programming language and environment. For the discussion, the following Java concepts are necessary.
Example: A Java game applet. A typical Java game applet draws violence on the screen, reads/writes a highscore file on the disk, and sends information, such as shots fired, to a network game server. To assist with depicting graphic violence, the game may use a third-party library for 3D rendering, with everything running on top of a Java runtime system providing access the screen, file system, and network. In this example let's assume that the game comes from www.game.com, the 3D library from the SGI website www.sgi.com, and that the game server is server.game.com.
There is a new version of Java called Java2 (previously known as JDK 1.2) that is more flexible. The security mechanism in Java2 defines domains (of code), based on origin and signature. As Java has a global namespace, the code for a Java applet may have different origins (e.g., the Java system class files from the local disk, or the game class files from www.game.org), with origins varying in trustworthiness. Signatures are a cryptographic technique that will be discussed later in the course; for now, think of signatures in terms of their real-world analogues, except completely unforgeable.
In Java2, we have a policy file that grants permissions to domains. This policy file should be written by a security knowledgable person, perhaps the system administrator for a corporate Intranet. In our game example the policy file might look like this (using correct Java2 syntax):
grant CodeBase "http://www.game.com/-" { permission java.lang.RuntimePermission("Screen"); permission java.net.SocketPermission "server.game.com"; permission java.io.FilePermission "/home/highscore", "read,write"; } grant CodeBase "http://www.sgi.com/-", SignedBy "SGI" { permission java.lang.RuntimePermission("Screen"); permission java.io.FilePermission "/lib/3d-data/-", "read"; }The above policy file grants "Screen" permissions to the game and the 3D library (specifically, to any code fetched using HTTP from www.game.com and www.sgi.com, respectively); the permission to communicate via socket to a game server and the permission to read/write a highscore file to the game; and the permission to read 3D-data from a directory to the 3D library. (A minus at the end of a path denotes "and all information in all subdirectories")
But it is not enough to define a policy file: permissions also need to be checked. This is done by a programming call to AccessController.checkPermission(P), where P is a permission. For example, the drawPixel Java system method might check that its caller has the "Screen" permission by doing: screenChk: AccessController.checkPermission(new RuntimePermission("Screen")). This changes the picture above as follows:
The general rule is the all domains crossed by the current thread must have the required permission. In our example, the permissions held by each domain are as follows (the Java system has all permissions by definition):
This approach to security is called Stack Introspection, and its concrete Java2 implementation could be stated as the following pseudo-code:
boolean checkPermission(Permission toCheck) { Stack domainStack = getDomainStack(); // domain stack for current thread while( ! domainStack.empty() ) { Domain here = domainStack.pop(); if( ! here.implies(toCheck) ) return false; } return true; }The pseudo-code above shows how Java2 Stack Introspection, when making a security decision, traverses the call stack of a running thread, requiring that all code on the call stack be from domains granted the required permission. How does this mechanism relate to Access Control Matrices from last lecture?
Below we see the policy file above presented as an ACM, with x denoting that a permission is granted. The subjects, for which checkPermission is invoked, are either domains, or, when the execution thread has crossed more than one domain, nested domains. Our example screenChk is invoked for the subject game+3Dlib+system: after the call to drawBlood() a new subject game+3Dlib is created, containing only permissions common to the subjects game and 3Dlib; finally, after the call to drawPixel(), game+3Dlib+system is created, containing only the permissions common to all three subjects, and this is where screenChk is done.
Subject\Object | Screen | 3D data files | Highscore file | Socket to Server |
---|---|---|---|---|
game | x | x | x | |
3Dlib | x | x | ||
system | x | x | x | x |
game+3Dlib | x | |||
game+3Dlib+system | x |
Note that in Java2 the subject can change when a method is called (perhaps changing back to the same subject if the method called is in the same domain), and that the active subject always has permissions that correspond to the intersection of the permissions of all domains crossed by the running thread.
A question then arises: How can the 3D library access the 3D data files it requires? As described so far, Java2 should not allow the 3D library to access its data files when used by the game, because the game does not have permission to access those files. Granting the game that access does not seem the right solution to this problem, since it would violate the Principle of Least Privilege: the game domain doesn't need access to the files, only the game+3Dlib subject does.
Java2 solves this problem by providing a mechanism for doing Privilege Amplification: increasing the set of permissions held by a subject to the set of permissions granted to the currently executing domain. Below we can see a figure showing how privilege amplification might be used in our example: the game calls a 3D library routine to draw a house on the screen, and before doing so the 3D library loads the 3D data for the house from a file on disk, causing a checkPermission in the Java system that succeeds (!) because the 3D library is calling it from privileged code (shown as a box):
Object house = AccessController.doPrivileged( new PrivilegedAction() { public Object run() { return readObjectFromDataFiles("house"); } } );Implementing privilege amplification requires a change in our permission checking algorithm: now checkPermission only ensures that the right permission is held by all domains crossed by the executing thread, upto and including the first domain running inside a privileged block. The necessary change to the pseudo-code shown before is highlighted below in bold.
boolean checkPermission(Permission toCheck) { Stack domainStack = getDomainStack(); while( ! domainStack.empty() ) { Domain here = domainStack.pop(); if( ! here.implies(toCheck) ) return false; if( here.isPrivileged() ) return true; } return true; }Actually the pseudo-code above (and the one shown in class) is not quite correct: as more than one thread may be executing in a domain may be at any given time, isPrivileged must be a predicate on call-stack frames, not on domains. The corrected pseudo-code follows:
boolean checkPermission(Permission toCheck) { Stack callStack = getCallStack(); while( ! callStack.empty() ) { StackFrame here = callStack.pop(); Domain thisDomain = here.getDomain(); if( ! thisDomain.implies(toCheck) ) return false; if( here.isPrivileged() ) return true; } return true; }
The concept of privilege amplification has an important implication: there are now two persons that use the Java2 security mechanisms and must be careful when it comes to security: the policy-specification writer, and the programmer who creates libraries such as 3D library. These two people must even communicate their decisions, because the policy specification writer should know what code is executed inside doPrivileged blocks in libraries.
Another problem with Java2's mechanism is that it is fairly complicated. It is difficult to know exactly what to put in the policy file and exactly where it is necessary/safe to use doPrivileged blocks. Often users are intimidated by this complexity and prefer not to use the provided security mechanisms at all (e.g. by using a very liberal policy file or placing all their code in a big doPrivileged block).