Wednesday, May 31, 2006

User Centric Security Model

Java Security Series Part 5

Till now we have been looking at the security model with the code perspective. The focus of this perspective was to prevent malicious code from causing harm to our secure systems. However, in a distributed system, not only is it important to run trusted code, but also we need to trust the user of the code - and this is the focus of User Centric Security model.

In Java 1.3, this model was introduced using the JAAS framework - Java Authorization and Authentication Service. However, with 1.4, it has been merged into Core Java.

Authentication

For protecting a resource against unwarranted users, the system should first be able to "authenticate" the user. The system should first verify in a secure fashion that the user of the system trying to access a protected resource is a known entity which it trusts and this process of verification is called Authentication.

The user of the system should first tell the system that he/she is a "somebody" - a particular name - who the system recognizes and based on this recognition, the system may further grant access to the protected resource. The "somebody" could be an identifier for the user such as user name, Employee Id, Certificate or any token which the system recognizes. The verification process will need the user to provide to the system enough proof that he/she is really the "somebody".

On successful authentication, the user is referred to as the Subject and the name with which he/she is identified as the Principal. A point implicit here is that a subject can have multiple principals attached to it because of multiple authentication processes. This may be needed by a system which has multiple sub-systems each of which recognizes the subject with multiple identities.

Authentication Model

JAAS authentication model is based on the Pluggable Authentication Model (PAM) framework. This architecture decouples the actual authenication logic from the application and thus allows the flexibility of configuring the application to any security requirements later, by just plugging in a different authentication mechanism. The application does not have to change or be modified to support a newer authentication mechanism. This model is also extensible to support multiple authentication mechanisms at the same time, allowing stacking of the authentication mechanisms. Stack attributes to the authentication mechanisms can then be specified in the configuration affecting the stack ordering thus allowing multiple overall authentiction result. The
values of the stack attributes are -

  • Required - An authentication mechansism with this value should succeed for the overall authentication to succeed. The next mechanism in the list is evaluated by the login process in any case.
  • Requisite - An authentication mechanism with this value should succeed for the overall authentication to succeed. The next mechanism in the list is evaluated only if this succeeds. Other wise the overall authentication result is said to be failed and the control returns to the application.
  • Sufficient - An authentication mechanism with this value is not required for the overall authentication to succeed. The next mechanism in the list is evaluated only if this fails. Other wise the overall authentication result is said to be succeeded and the control is immediately returned to the application.
  • Optional - An authentication mechanism with this value is not required for the overall authentication to succeed. The next mechanism in the list is evaluated by the login process in any case.
The overall authentication succeeds only if all Required and Requisite LoginModules succeed. If a Sufficient LoginModule is configured and succeeds, then only the Required and Requisite LoginModules prior to that Sufficient LoginModule need to have succeeded for the overall authentication to succeed. If no Required or Requisite LoginModules are configured for an application, then at least one Sufficient or Optional LoginModule must succeed.

Authentication using LoginContext and LoginModule

javax.security.auth.login.LoginContext abstracts the PAM framework for authentication. When an application needs to authenticate a user, it uses this class to start the authentication process. An instance of this class is created passing the "Authentication Realm" to which the login is required. The authentication realm abstracts a set of login configuration as abstracted by javax.security.auth.login.Configuration to get the configuration of authentication mechanisms to use. The configuration basically contains a collection of authentication mechanism configurations -

  • Authentication mechanism class name
  • Authentication mechanism specific configuration details in name-value pairs
  • Stack config property - Required Requisite Sufficient Optional
These sets of configuration information are indexed by strings which is the authentication realm. The sample below gives an example of a typical configuration for an auth realm called MyLogin -

MyLogin {
com.sun.security.auth.module.UnixLoginModule required;
com.sun.security.auth.module.Krb5LoginModule optional
useTicketCache="true"
ticketCache="${user.home}${/}tickets";
};
After creating the LoginContext object, the user calls the login method. LoginContext object is
as shown below -

public final class LoginContext {
// important constructors
public LoginContext(String authRealm) {...}
public LoginContext(String authRealm, CallbackHandler h) {...}
// two phase auth process
public void login() {...}
public void logout() {...}
// get the authenticated Subject
public Subject getSubject() {...}
}
The login method kicks starts the two-phase authentication process (detailed in the next
section). It calls login() and commit() method on each of the configured login mechanism objects. These login mechanism objects need to implement the interface
javax.security.auth.spi.LoginModule as shown below -

public interface LoginModule {
// 1st authentication phase
boolean login();
// 2nd authentication phase
boolean commit();
boolean abort();
boolean logout();
}

The LoginContext object passes in all the authentication specific configuration settings from the configuration as a Map of name-value pairs which the LoginModule can use for the actuual authentication - possibly to connect to database store etc to validate the passwords. Some times the individual LoginModule object may need to communicate with the user to get some details for the authentication purpose - for example prompt the user to enter user name and password. This communication with the user is achieved through the javax.security.auth.callback.CallbackHandler object. The application needs to implement this object and pass its reference to the LoginModule object in its constructor. The LoginModule will then request whatever it needs by calling its handle method by passing a list of callback objects abstracting the LoginModules's needs. A typical callback handler implementation could be as shown below -

public interface CallbackHandler {
public void handle(Callback[] callbacks) {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
// prompt the user for a username
NameCallback nc = (NameCallback)callbacks[i];
// ignore the provided defaultName
System.err.print(nc.getPrompt());
System.err.flush();
nc.setName((new BufferedReader
(new InputStreamReader(System.in))).readLine());
}
else if (callbacks[i] instanceof PasswordCallback) {
// prompt the user for sensitive information
PasswordCallback pc = (PasswordCallback)callbacks[i];
System.err.print(pc.getPrompt());
System.err.flush();
pc.setPassword(readPassword(System.in));
}
else {
throw new UnsupportedCallbackException
(callbacks[i], "Unrecognized Callback");
}
}
}
}

2-Phase authentication

The 2-phase authentication involves -
  • login() call on all LoginModules
  • On successful overall authentication (see above), commit() call on all LoginModules
  • On unsuccessful overall authentication, abort() call on all LoginModules
LoginContext object calls login() on each of the configured the LoginModule objects. LoginModule objects are expected to implement the authentication process, but not update the Subject with the Principal. LoginModules are assumed to store the result privately. LoginModule objects after authenticating, either returns true indicating a successful authentication or throws LoginException indicating authentication failure. A false return indicates that the LoginModule is not really in the context of this Login process and can be ignored.

A point to note here is that if a LoginModule has Stack flag Requisite and its login() operation was a failure, or the Stack flag is Sufficient and the login() operation of the LoginModule is a success, login() is not called on subsequent LoginModules. This is as required by the Stack flag meaning (see above). However, looking at the code, if any LoginModule throws LoginException during the login() phase, it seems that login() of subsequent LoginModules is not called. This may be a bug.

If overall authentication is successful, the authentication process then proceeds to call commit() on all LoginModule objects. LoginModules are now expected to update the Subject with principals specific to LoginModules. On successful completion of this step, the user can get a successfully authenticated subject from the LoginContext and the login process is considered completed successfully.

On the other hand, if overall authentication is not successful, then abort() is called on each of the LoginModules. Each of the LoginModules are expected to cleanup anything relevant to it. abort() may also be called on each of the LoginModules if the commit() phase fails after a successful login() phase. After calling abort(), the original LoginException is thrown back.

Friday, May 26, 2006

Establishing Trust

Java Security Series Part 4

Establishing trust is an essential ingredient when building reliable systems, as, based on trust the various access privileges are provided. And to establish trust we need to have a secure means to authenticate the trusted entity.

For example, the Java 2 security model works on providing access to trusted protection domain. As we saw in the previous parts in the series, a protection domain encapsulates among other things, code source which is nothing but a combination of codebase URL and its signers. So, if we trust code to be downloaded from sandeshudupa.blogspot.com and trust its author, then we need to make sure that when we download the code, it really is written by it trusted author and not an imposter. To perform this authentication, we use Digital Signatures.

Before we start off on digital signatures, we need to understand some basic concepts related to Crytpography.

Cryptography
This pertains to the technology of encoding and decoding information. Cryptoanalysis, on the other hand is the reverse of cryptography and deals with breaking secretly encoded information without the knowledge of encryption keys. Some of the commonly used cryptography concepts in security systems are -



  • One way hash functions
  • Symmetric Ciphers
  • Asymmetric Ciphers
One way hash functions and Message Digests
One way hash functions are used to help achieve data integrity. Two properties of one way hash functions aid in helping achieving data integrity -



  • It is easy to compute, but very difficult to reverse. Meaning given any information, computing one way hash is easy, but given the hash, it is difficult to get back the information.
  • No two pieces of information can have the same one way hash.
Using the above two features, information and its hash, also referred to as Message Digest can be sent to the user, and user could recompute the hash of the information. If the computed hash is the same as the hash that was sent along with the information, then that means that the information was not tampered with in its transit. So, one way hash provides a means to specify data/information integrity.



Data --> HashFunction --> HashValue (Message Digest)

However, providing only this means is not good enough. What if a dubious entity in the transit changed both the information and its digest. The receiving entity will get changed information and on using the one way hashing will confirm that the data integrity is not broken. But the problem is that the data received is totally different than what was sent. So, there has to be a means to prove the integrity of the message digest. This is done by Digitial Signatures.

Symmetric Ciphers
Symmetric ciphers transform input aka plaintext to output aka ciphertext using a secret key so that only those entities which know the secret key can convert back ciphertext to plaintext. These are also called Secret-key Ciphers as both the parties need to know the secret key. Some of the used Symmetric cipher technologies are Caesar Ciphers, DES (Data encryption standard) and AES (Advanced Encryption Standard).



PlainText --> EncipherEngine (SecretKey) --> CipherText

PlainText <-- DecipherEngine (SecretKey) <-- CipherText

A problem with symmetric ciphers is that the secret key needs to be safely shared between both the encrypting and decrypting entities. This problem is solved by Asymmetric Ciphers.

If the receiving party knows the secret key of the sending entity, then it knows for sure that the information received is from where it is expecting. The cipher text here is the digital signature of plaintext.

Asymmetric Ciphers
These ciphers are also used for the same means as that of symmetric ciphers, but instead of using a secret key, they use a pair of keys - Public/Private key pairs. These key pairs have a mathematical relationship between them such that given a private key, it is possible to deduce the public key, but the reverse is very difficult.

Using this property of these key pairs, it is then possible to encrypt plaintext to ciphertext using the public key and decipher the plaintext back from ciphertext using the private key. Another interesting point here is that some asymmetric keys also have the property that using the private key and decipher engine, plain text can be converted to ciphertext and then using the encipher engine and public key, the ciphertext can be converted to plaintext.



PlainText <--> EncipherEngine (PublicKey) <--> CipherText

PlainText <--> DecipherEngine (PrivateKey) <--> CipherText

So, the sending party can use the decipher engine and its private keys to create ciphertext. The recieving party using the public key of the sending party can get the plain text out of ciphertext. As with symmetric ciphers, the cipher text here is the digital signature of plaintext.

Some of the available asymmetric systems are (1) RSA named after its inventors (2) DSA (Digital Signature Algo)

Digitial Signatures
Using the above techniques, we can make sure that the information that we receive


  • is not changed in transit
  • is from the entity from where we expect the information.
Typically, to achieve this, the following is done by the sender


  • Create a message digest for the message being sent. This will ensure the message integrity.
  • Digitally sign the message digest with PrivateKey. This will ensure the integrity of the message digest.
  • Send the message, message digest and signature.
The receiving end will


  • Use the sender's PublicKey and encipher engine to get the message digest back. Compare the content of the message digest thus enciphered and the message digest sent. If they are the same, then the message digest was really sent by the entity from we were expecting.
  • Next compute the message digest of the actual message. If the computed message digest is the same as that sent, then the message itself has not been tampered with.
These same priciples are applied in Java, when trusting code and forms the basis of signing code.
JAR Signing

To establish trust in code, code needs to be signed and this is done by JAR signing. The following components in the JAR file allow for signing of JARs -
META-INF directory
This is a directory in the archive which contains other control related files.
MANIFEST.MF file
Contains sections for various files in the archive and each section is separated from one another by empty lines. All files that needs to be signed need to be listed in this file in a section using a name:value pair semantics. An entry with name Name and value path or URL to the actual file followed by another attribute with name of the message digest format and value message digest of the file. For example


Name: test/classes/ClassOne.class
SHA1-Digest: TD1GZt8G11dXY2p4olSZPc5Rj64=

Signature Instruction file
For each signer of JAR a signature instruction file and a corresponding signature block file is created. This file also contains entries like that in the MANIFEST.MF file, with a Name entry pointing to a file and an attribute entry with name being the message digest algorithm name and value being the message digest of the section in the manifest file for the named file. For example


Signature-Version: 1.0
SHA1-Digest-Manifest: h1yS+K9T7DyHtZrtI+LxvgqaMYM=
Created-By: SignatureFile JDK 1.2

Name: test/classes/ClassOne.class
SHA1-Digest: fcav7ShIG6i86xPepmitOVo4vWY=

Name: test/classes/ClassTwo.class
SHA1-Digest: xrQem9snnPhLySDiZyclMlsFdtM=

Name: test/images/ImageOne.gif
SHA1-Digest: kdHbE7kL9ZHLgK7akHttYV4XIa0=

Name: test/images/ImageTwo.gif
SHA1-Digest: mF0D5zpk68R4oaxEqoS9Q7nhm60=

Here the message digest is the digest of the manifest file entry for this file. Also, the digest of the entire manifest file is stored.
Signature Block file
A signature block file is created for evey signature instruction file with same name but extension dependent on the algorithm used to sign. The contents are the (1) digital signature of the Signature Information File and (2) public key and certificates

KeyTool and Jarsigner tools

Keytool utility is used to create the set of keys needed for signing. The generated keys and a self signed certificate are stored in a key store which is ofcourse password protected. The following command can create a keypair along with a self signed certificate with alias sandesh.

keytool -genkey -alias sandesh -keypass pass1 -validity 80
-keystore sandeshKeyStore -storepass pass2

Then a jar file, say sandesh.jar could be signed as follows -

jarsigner -keystore sandeshKeyStore -storepass pass2
-keypass pass1 -signedjar ssandesh.jar sandesh.jar sandesh

The signed jar could then be verified as follows -

jarsigner -verify ssandesh.jar

JAR Verification Proecess

Typically the following needs to occur while JAR verification. The steps may be optimized by the runtime, but in essence, this needs to happen -
  • Certificate/Certificate Path in the Signature Block file is first validated first to trust the Public Key of the Signer
  • Using the Public Key, the Digital Signature of the Signature Info File is decrypted. If the decrypted Sig Info is the same as the Sig Info file in the META-INF directory of the JAR, then the signature is considered verified.
  • Digest entry of each section in the MANIFEST.MF file is then computed and checked against the Digest entries present in the Sig Info file. If this comparison succeeds, then it means that the integrity of the file digests is not compromised.
  • Then the digests of each file is computed and compared against the digest value in entry in the manifest file.
If all the above pass, then the verification is considered successful.

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);

Monday, May 01, 2006

Code Centric Security Model

Java Security Series Part 2

Security model

One of the important goals in a secure system is to protect critical resourses. In this context a protection domain is a concept which defines all the entities that can access a particular or group of critical resourses. This concept is extrapolated in Java and is abstracted in java.security.ProtectionDomain class. The protection domain in Java encompasses two details -
  • What (code) can access critical resourse - Code Centric Security model
  • Who (user) can access critical resourse - User Centric Security model

Code Centric Security Model

With the original Java 1.0, any Java code on the local machine could access any resourse such as file system, runtime properties, network etc as the local applications were completely trusted and were thought to be safe. Only the applications which were remote, as in applets were seen with caution and subjected to security. In this system, the applet code was made to run in what is called "Sand Box" which is nothing but a protection domain for the applet code which has no access to a fixed set of critical resourse.

However, with the introduction of Signed applets in Java 1.1, this sand box was made configurable with applets from different code sources (applet written by different signers and downloaded from different location) could do different things in the sand box. User based on the trust on a signer and location could lax the strict sand box; however, the sand box itself was still protecting only a fixed set of critical resource and hence the Protection domain here was said to be with a fixed boundary.

With the introduction of Java 2 Platform Security in Java 1.2, a regular security framework was designed. In this framework, all code belongs to a Protection Domain and the protection domain determines the resources that are accessible to the code. The protection domain here itself is NOT of fixed boundary as the means to define a critical resource is extensible.

java.security.Persmission

This class abstracts the permissible access on a particular resourse. Java provides a hierarchy of sub-classes from this parent class to provide the notion of different types of access on different resources. For example, FilePermission, SocketPermission etc to specify access to file system resources and network resources.

By further sub-classing this class, custom resource access can be defined - and this is crux to a flexible boundary Protection Domain.

A permission object typically has two attributes - (1) target name - name of a particular target object of a particular type of resource; for example a file named C:\Sandesh.doc (2) action list - all the access privileges on the target object - such as read, write etc.

java.security.ProtectionDomain

This class abstracts the permissible activities that a particular trusted code (trust here implies on the author of the code and the location of download of the code) can perform.

As and when a class is loaded by the class loader, the class loader initializes the Class instance with the protection domain to which it belongs.

In Java 1.4, it encapsulates mainly 3 entities - (1) CodeSource (2) Permissions (3) Principals

java.security.CodeSource

This class abstracts the trusted entity based on which protection domains work and as explained consists of (1) signer of code (2) location of code download

Initialization of ProtectionDomain for a Class instance

As we saw earlier, there are three default class loaders that get installed when JVM is started - (1) Bootstrap class loader (2) Extensions class loader (3) System class loader.

These class loaders are responsible for assigning the right Protection Domain during when defining a class. Bootstrap class loader is of native type and assigns all the System classes (classes present in jre/lib/rt.jar) to System Protection Domain which has AllPermission access rights.

The extension and system class loaders are of type java.net.URLClassLoader which inturn inherits from java.security.SecureClassLoader.

While the extensions class loader loads classes from jre/lib/ext directory and system class loader loads from the CLASSPATH environment variable, their intialization process of the protection domain of classes loaded by them is the same as both of them are of the same type.

java.net.URLClassLoader overrides findClass as expected and here, it first gets the raw bytes from the URLs it knows about and then from the same also gets the signer information and from this constructs a CodeSourse object and then calls defineClass, to define the class.

java.security.SecureClassLoader overrides defineClass. SecureClassLoader maintains a cache of ProtectionDomain using a HashMap with key being the code sourse. It first checks if the ProtectionDomain for the given code sourse is already present in the cache and reuses if cached. Otherwise, it calls getPermissions to get an initial set of permissions for the protection domain. This function is overridden by URLClassLoader which inserts an initial set of permission which allows the code source to -

  • If the protocol specified by URL is "file" and the path specified by the URL is a file, then read permission to that file is granted
  • If the protocol specified by URL is "file" and the path specified by the URL is a directory, then read permission to all files and directories recursively
  • If the protocol specified is anything else, permissions to connect to and accept from URL's network host is allowed.

Please note that SecureClassLoader.getPermissions returns an empty set of permissions because it has no idea on what permissions to give [Note: In JDK 1.3, this method actually initialized the initial permissions with permissions read from java.security.Policy.getPermissions(CodeSource) method. But it seems to support dynamic policies, this has changed in 1.4 and has kind of become redundant.]. SecureClassLoader.defineClass then constructs a java.security.ProtectionDomain object using the code source instance, class loader instance and the initial permissions and associates the ProtectionDomain with the Class.

java.security.PermissionCollection and java.security.Permissions

These are two kinds of containers for the permission objects and the difference between the two being that PermissionCollection is a homogenous set of Permission objects where as Permissions are heterogenous.

Binding permissions to ProtectionDomain

Protection Domain provides a mapping between code source and its permissions. In Java 1.2, SecureClassLoader's getPermissions method read java.security.Policy for the configured permissions for a particular code source. Policy object here is used to decouple the policy configuration from the security framework. Policy is itself an abstract class and expects a provider to provide the actual permissions for the code source. This way, users can plugin custom access management systems to the security framework. By default com.sun.security.PolicyFile gave an implementation where the users could configure permissions in an ASCII file.

Static binding of permissions to Protection Domain is not always a good idea. There are some access management systems which can specify if a security context has a particular permission, but cannot specify all the permissions available for a security context. Also, if permissions are to be more dynamic in nature, static binding will not obviously work. In Java 1.4, dynamic policy binding was introduced. Here, the interaction between ProtectionDomain and Policy object has changed. The way it works in 1.4 is - (1) SecureClassLoader no longer asks the Policy object for permissions during creation of ProtectionDomain object, but returns an empty permissions list (2) Protection domain when queried if a particular permission is implied, first checks the policy if a particular permission is implied. This causes the dynamic behavior, as the permissions are never really bound to the ProtectionDomain object, but loosely coupled using the Policy object. (3) Only if the Policy object does not imply the permission, does the default permissions in the Protection domain object consulted.

By default the policy implementation used is com.sun.security.auth.PolicyFile. A different implementation can be configured by changing the security settings in jre/lib/ext/security/java.security file.

com.sun.security.auth.PolicyFile initializes the permissions by reading jre/lib/ext/security/java.policy file. This configuration can be changed by editing policy.url properties specified in jre/lib/ext/security/java.security

Java Security Basics

Java Security Series Part 1

Java takes a holistic approach when dealing with security and handles it at two basic levels as shown below

  1. Language construct level
  2. Runtime
Language Syntax Security Features

Building on the problems faced by languages such C/C++, many of the features of these languages are considered unsafe and left out. These features not only feed into the complexity of the language but also are the source for programmer errors. Some of the features that make Java secure are -

  1. Single Implementation Inheritance
  2. Strong Type checking
  3. No support for pointers
  4. Array bounds checking
  5. Other miscellaneous features such as safety against use of uninitialized variables
Because of this, it makes so many lesser options for the programmers to construct blunders. The compiler makes sure that the bytecode generated form the Java source is safe. However, since the actual byte codes are standardized in JVM specifications, for an informed hacker, it should be fairly easy to concoct byte code sequences. However, this situation is taken care of later during the class loading process when the class loaders send the loaded byte codes into the JVM for Byte Code Verification.

Runtime Security Features
The runtime facilities kick in as soon as the JVM is started and made to execute the Java application. Some of key contributors for a secure environment are -

  1. ClassLoaders
  2. ByteCode Verification Process
  3. Security Framework

Class Loaders

When JVM is instructed to start a Java application one of the first things it does is the creation of "System Classloader". This system class loader is then reponsible for loading up the byte code for the application java class file and executing it. There are typically three class loaders that get installed when JVM starts up. These are -

  1. Bootsrtrap Class loader - Since class loaders are themselves classes, we run into the classic chicken-egg problem. Who loads the class loader classes. This is solved by a native. This class loader is also called Null or Primordial class loader. This class loader is responsible for loading the Java system classes from rt.jar in jre/lib directory.
  2. Extensions Class loader - This is the child of Bootstrap class loader is basically a secure URL class loader responsible for loading classes in jre/lib/ext directory.
  3. System Class loader - This is the class loader which is responsible for acting on the CLASSPATH environment variable. It is also a secure URL class loader type and is the child of Extensions class loader. Though it is called System Classloader, it actually loads up application classes.

Class loader instances at runtime have parent child relationship between them with the boot strap class loader being the root parent. Below this class loader the extensions class loader and system class loader form a linear chain. By creating user class loader and then building up hierarchies, a very structured partitioning system can be formed with each class loader forming a node in the tree and all the classes loaded forming the leaves. Same class loaded by different class loader are actually of two different types and hence the partition. Another benefit of this kind of hierarchy is that it allows delegation when trying to load the classes. So, when a class is required to be loaded, by default the system class loader is requested. System class loader first delegates this loading to its parent class loader which is the extensions class loader which in turn delegates to the bootstrap classloader. If bootstrap class loader cannot load the class (because the class is not a system class), then the extensions class loader attempts and even if this fails then does the system class loader attempt to load. If a user classloader is being used, the same logic works here too. If the user class loader is constructed using the default constructor, then the parent is the system class loader, otherwise, the constructor takes in the parent class loader.

The loadClass API is actually in the java.lang.ClassLoader class, and typically it is not overloaded. Its implementation does the following -

  • See if the class is already loaded and is present in the class cache and if present and needed to resolve the class, resolve the class and return.
  • Otherwise, delegate loading to parent class loader.
  • If none of the parent class loaders can load the class, then call findClass. findClass is supposed to locate the actual class data and define the class and return the Class object corresponding to the class.
  • If needed to resolve the class, resolve the class and return.

Typically user class loaders need to only override findClass method. The overridden findClass method should -

  • "find" the raw bytes pertaining to the bytecode of class
  • "define" the class - process of creating a "Class" object for the class from the raw bytecodes and associating a protection domain (security domain of the class - discussed later)

A class object encapsulates the entire RTTI information. My understanding here is that it encapsulates both the Reflection information (means to find the capability of a class) and Introspection information (means to find the type identity of the class such as type name, inheritance structure etc used in instanceof operator). If the same class is loaded by different class loaders, then obviously the RTTI information will not be the same and hence, these will be two different types. Hence, in Java, a type of an object at runtime is determined both by its class and the class loader.

Now, before this class can be used and executed, it needs to be "resolved" - linked - process of taking the Class and including it into the runtime state of the JVM so that it can be executed. Thsi process involves basically three steps -

  • Byte Code Verification - making sure that the byte code does not break any JLS guarentees.
  • Preparation - allocation of all the static storages such as class variables etc and v-table initialization. The static storages allocated are initialized to default values. However, please note that static initializers are not run, as we are still not in a position to execute anything.
  • Symbol resolutions - checking the referred symbols and loading up these classes and interfaces. The JVM specification is not too strict as to when this can happen. The referred symbols can statically resolved right at this point, or more lazily at runtime. The strategy depends on the implementation

Finally before any code can be executed, the class needs to be initialized. This step involves all the static initializations and initializer list and block execution. Any super classes need to be loaded, verfified, prepared, resolved and initialized at this time. An interesting point here is that any interfaces implemented by the class or any super interfaces of the interface need not be initialized. This is because any interface fields are public static final and hence are compile time constant. In any case, the initialization should happen before -

  • T is a class and an instance of T is created.
  • T is a class and a static method declared by T is invoked.
  • A static field declared by T is assigned.
  • A static field declared by T is used and the field is not a constant variable.

After this point, the class is ready for execution.