Wednesday, May 24, 2006

Enforcing Security

Java Security Series Part 3

java.lang.SecurityManager

In the previous parts to this series, we have seen how security policies can be associated with code. However, to enforce security, checking still needs to be performed at runtime to make sure that only "privileged" code can access the protected resouce or the protection domain guarentees are enforced.

In Java 1.2, this enforcement is done by java.lang.SecurityManager class. All system resources are protected using SecurityManager instance as the code snippet below explains -


SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(new FilePermission(file,
SecurityConstants.FILE_DELETE_ACTION));
}

So, SecurityManager is the first interface to be used while checking for permissions.
The default implementation delegates all checking to java.security.AccessController class which is the main driver for Java out-of-box security infrastructure. If users have any special needs which cannot be satisfied by the provided SecurityManager, then, SecurityManager could be sub-classed and an user appropriate implementation of checkPermission could be provided. This SecurityManager can then be installed using java.lang.System.setSecurityManager method.

If users need to customize resources, then as we discussed earlier, java.security.Permission needs to be subclassed to represent custom resource permissions. While accessing the resource then, a security manager checking as shown above needs to be performed.

java.security.AccessController and java.security.AccessControlContext

As said earlier, the default implementation of SecurityManager deleagtes all the permission checking to java.security.AccessController object. This provides the default and only security infrastructure available in Java. According to the authors of Inside Java 2 Platform Security, this infrastructure should suffice most of the industry needs, and a need for subclassing SecurityManager should be rare if none.

The checking of permission is done against the protection domain of the entire execution context. What this means is that the permission is checked against the protection domains of each and every class in the calling stack. This execution context is abstracted by java.security.AccessControlContext class.

Typically, there are three scenarios for the calling stack -

Vanilla Calling Stack privileges
This is the simplest of calling stacks and can arise when the main thread does a series of operations. In this case the AccessControlContext that gets verified for the permission contains the permission domains of only the classes in the calling stack. A request for access is granted if and only if evety protection domain in the current execution context is granted the said permission, ie, the code and principal specified by each protection domain are granted permission. The permission checking logic can be summarized by the following pseudo-code (assume m is the stack depth) -

i = m;
while (i > 0) {
if (caller i's domain does not have permission)
throw AccessControlException;
i--;
}


Access privileges of spawned thread
If an application spawns a thread, then the thread has two execution contexts - (1) of its own (2) execution snap shot of the spawning thread at the time of spawning. In this case the permission checking logic changes as follows -

i = m;
while (i > 0) {
if (caller i's domain does not have permission)
throw AccessControlException;
i--;
}
inheritedContext.checkPermission(permission);


Privileged Action
Sometimes it becomes necessary to do some privileged action from a point in the calling stack.

Consider for example, we have a jar called passwd.jar which has logic to change the password. For this logic to work, it needs to access a password file and hence needs FilePermissions. Assuming that this jar file is trusted, you grant it FilePermission to access the password file. Also, to execute the password change logic, it requires the calling entity to have a custom PasswordChangePermission. Now another application in application.jar is also trusted and we trust it to change the password. So, we give this jar the PasswordChangePermission. But we dont trust it enough to give FilePermission. So, now when the application.jar tries to change the password, even though the passwd.jar has FilePermission, the files cannot be accessed, as ultimately the permission of all protection domains is checked when trying to check the file permissions. In this scenario, there is a need to just make the stack from the passwd.jar privileged, so that the permission checking only considers this stack and ignores the further stack. In pseudo-code terms, we need something like this -

i = m;
while (i > 0) {
if (caller i's domain does not have permission)
throw AccessControlException;
if (caller i is marked as privileged)
return;
i--;
}


To explain further, consider the following stack belonging to the example given -

*Frame 1* Application::getUserInputAndChangePassword() // Inside application.jar
*Frame 2* PasswordAdmin::changePassword(String old, String new) // Inside passwd.jar
*Frame 3* FileInputStream::read() // system class

Protection domain of application.jar has PasswordChangePermission and passwd.jar has FilePermission. So, fo this logic to work, we need to make frame 2 privileged for the above logic to work. This can be done by using the doPrivileged method in AccessController. For example -

class PasswordAdmin {
...
void changePassword(String old, String new) {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
// both the ops need FilePermissions
if (openPasswordFileAndCheckOldPassword(old)) {
changePasswordFileWithNewPassword(new);
}
}
});
}
};


Sometimes, while calling doPrivileged operation, we might need to make sure certain other execution context also has the permission. This can be done by passing another additional set of AccessControlContext as parameter in doPrivileged operation.

Full Access Control Algorithm

i = m;
while (i > 0) {
if (caller i's domain does not have permission)
throw AccessControlException;
if (caller i is marked as privileged) {
if (additional context needs priv checking) {
additionalContext.checkPermission(permission);
}
return;
}
i--;
}
inheritedContext.checkPermission(permission);

0 Comments:

Post a Comment

<< Home