org.freshcookies.security.policy
Class PolicyReader

java.lang.Object
  extended by org.freshcookies.security.policy.PolicyReader

public class PolicyReader
extends java.lang.Object

Parser that reads a Java 2 security policy file into memory.

If a SecurityManager is running, the security policy must grant PolicyReader's CodeSource the following permissions:

...where path-to-policy-file is the policy file to parse, path-to-keystore is the path of the keystore named in the policy file (if any), and custom-permission-packages and custom-principal-packages denote the names of custom Permission and Principal classes to be loaded by the current classloader when the file is parsed. The methods that require these permissions enclose their operations inside of doPrivileged blocks, so calling classes' ProtectionDomains do not need to be granted these privileges.


Constructor Summary
PolicyReader(java.io.File file)
           Constructs a new PolicyReader for parsing a supplied policy File using the Java platform standard charset.
PolicyReader(java.io.File file, java.lang.String charset)
           Constructs a new PolicyReader for parsing a supplied policy File.
 
Method Summary
protected  void addMessage(java.lang.Exception e)
          Adds an Exception to the list of parsing errors.
protected  java.net.URL extractCodeBaseUrl(java.lang.String token)
          Extracts the URL from a codeBase token string.
protected  java.security.PermissionCollection extractPermissionCollection(java.lang.String permissions)
          Parses permissions from a grant block and returns a PermissionCollection.
protected  java.security.Principal extractPrincipal(java.lang.String token)
          Extracts the Principal from a principal token string.
protected  java.security.cert.Certificate[] extractSigningCertificates(java.lang.String token)
          Extracts and resolves signing Certificates from a signedBy token string.
static java.lang.String findAlias(java.security.KeyStore ks, java.security.cert.Certificate cert)
          Examines a Keystore and returns the alias that matches a given signing Certificate.
static PolicyReader[] findPolicies()
           Static method that identifies the active security policies for the JVM and returns an array of PolicyReader objects (one for each active policy).
 java.io.File getFile()
          Returns the File object used to instantiate this PolicyReader.
 java.security.KeyStore getKeyStore()
           Returns the keystore associated with the policy file, if one is specified in the policy.
 java.util.List getMessages()
          The parsing errors encountered by PolicyReader when the read() method was last invoked.
 java.security.ProtectionDomain[] getProtectionDomains()
          Returns the ProtectionDomains parsed by the read() method.
static java.security.cert.Certificate getSigner(java.lang.Class clazz)
          Static method that returns the certificate used to sign a particular class.
static boolean isSigned(java.lang.Class clazz)
          Returns true if a class is digitally signed.
 boolean isValid()
          Returns true> if the security policy file was parsed correctly and validated without errors.
 boolean isVerified(java.lang.Class clazz)
          Returns code if a supplied class' CodeSource is digitally signed, and the certificate used to sign it can be found in this PolicyReader's keystore.
protected  void loadPolicy(java.io.File file)
          Protected method that loads the policy file into memory, scrubs contents of line breaks and extra whitespace, and returns it as a string.
protected  java.security.ProtectionDomain parseProtectionDomain(java.lang.String granteeString, java.lang.String permissionString)
           Returns a valid ProtectionDomain by parsing a grant block that denotes the codebase, pricipals and signers to whom permissions should be granted.
 void read()
           Parses the security policy file, and loads its contents into memory.
protected static boolean secureExists(java.io.File file)
          Returns true if a supplied File exists in the file system, and the SecurityManager (if running) has granted read access to it.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

PolicyReader

public PolicyReader(java.io.File file)
             throws java.io.FileNotFoundException

Constructs a new PolicyReader for parsing a supplied policy File using the Java platform standard charset.

Parameters:
file - the policy file to be parsed
Throws:
java.io.FileNotFoundException - if the policy file does not exist in the filesystem
java.lang.SecurityException - if the running SecurityManager denied any of the required Permissions

PolicyReader

public PolicyReader(java.io.File file,
                    java.lang.String charset)
             throws java.io.FileNotFoundException

Constructs a new PolicyReader for parsing a supplied policy File. If the file does not exist, this constructor returns an IllegalArgumentException.

If a SecurityManager is running, the security policy must grant PolicyReader's CodeSource the following permissions:

...where custom-permission-packages and custom-principal-packages are the names of custom Permission and Principal classes loaded from the current classloader.

Parameters:
file - the policy file to be parsed
Throws:
java.io.FileNotFoundException - if the policy file does not exist in the filesystem
java.lang.SecurityException - if the running SecurityManager denied any of the required Permissions
Method Detail

findAlias

public static java.lang.String findAlias(java.security.KeyStore ks,
                                         java.security.cert.Certificate cert)
Examines a Keystore and returns the alias that matches a given signing Certificate. If no match is found, this method returns null.

Parameters:
ks - the keystore to search
cert - the certificate to match
Returns:
the alias corresponding to the certificate

getProtectionDomains

public java.security.ProtectionDomain[] getProtectionDomains()
Returns the ProtectionDomains parsed by the read() method. If the read() method has not been called yet, this method will return a zero-length array.

Returns:
the ProtectionDomains parsed from the policy file

findPolicies

public static PolicyReader[] findPolicies()

Static method that identifies the active security policies for the JVM and returns an array of PolicyReader objects (one for each active policy).

This method accesses the file system to determine whether certain default security policies exist, and it reads the java.home and user.home properties to determine the location of the system and user security policies. Therefore, to run this method under a SecurityManager, the security policy must grant PolicyReader's CodeSource the following permissions:

Returns:
PolicyReader objects for each active policy file
Throws:
java.lang.SecurityException - if the running SecurityManager denied any of the required Permissions

getSigner

public static java.security.cert.Certificate getSigner(java.lang.Class clazz)
Static method that returns the certificate used to sign a particular class. We determine the signing certificate by obtaining the class' ProtectionDomain first, then looking up the CodeSource and extracting certificates. If the code source was not signed, this method returns null. This method obtains the ProtectionDomain of the class. Therefore, to run this method under a SecurityManager, the security policy must grant PolicyReader's CodeSource the following permission:

Parameters:
clazz - the class whose signer should be returned
Returns:
the signing certificate, or null if no signer
Throws:
java.lang.SecurityException - if the running SecurityManager denied this ProtectionDomain the permission java.lang.RuntimePermission "getProtectionDomain"

isSigned

public static boolean isSigned(java.lang.Class clazz)
Returns true if a class is digitally signed. This looks up the signer by delegating to getSigner(Class), and therefore requires the same privileges if run under a SecurityManager.

Parameters:
clazz - the class to check
Returns:
the result.
Throws:
java.lang.SecurityException - if the running SecurityManager denied this ProtectionDomain the permission java.lang.RuntimePermission "getProtectionDomain"
See Also:
getSigner(Class)

getFile

public java.io.File getFile()
Returns the File object used to instantiate this PolicyReader.

Returns:
the file
See Also:
PolicyReader(File)

getKeyStore

public java.security.KeyStore getKeyStore()
                                   throws java.io.IOException

Returns the keystore associated with the policy file, if one is specified in the policy. The strategy used to file the keystore follows the J2SE convention: if the file named in the keystore line of the policy file is not an absolute path name, the location for the file is assumed to be the same directory as the keystore. If the keystore cannot be located, this method throws an IOException.

If the policy file does not specify a keystore, this method returns null, and calling methods should check for this result!

This method attempts to read the keystore into memo from the file specified in the secuity policy. Therefore, to run this method under a SecurityManager, the security policy must grant PolicyReader's CodeSource the following permission:

Returns:
the keystore associated with the policy file, or null if not found
Throws:
java.io.IOException - if the keystore file cannot be read from the filesystem because it does not exist
java.lang.SecurityException - if the running SecurityManager denied the Permission java.io.FilePermission "path-to-keystore", "read"

read

public void read()
          throws java.io.IOException

Parses the security policy file, and loads its contents into memory. Any errors encountered while reading the policy will be added to an internally cached set of error messages, which may be obtained by getMessages().

Because this method attempts to read the policy file contents, it requires read permissions to the file containing the policy if a SecurityManager is running. It will also require read access to the keystore named in the policy, and will need to read named classes' ProtectionDomains to verify signatures if required by the policy. Therefore, to run this method under a SecurityManager, the security policy must grant PolicyReader's CodeSource the following permission:

Throws:
java.io.IOException - if the policy file cannot be read
java.lang.SecurityException - when the SecurityManager, if running, denies the ProtectionDomain of this class any of the required permissions

getMessages

public java.util.List getMessages()
The parsing errors encountered by PolicyReader when the read() method was last invoked. The list of errors is reset every time read() is called.

Returns:
the list of Exception objects

addMessage

protected void addMessage(java.lang.Exception e)
Adds an Exception to the list of parsing errors.

Parameters:
e - the exception added

loadPolicy

protected void loadPolicy(java.io.File file)
                   throws java.io.IOException
Protected method that loads the policy file into memory, scrubs contents of line breaks and extra whitespace, and returns it as a string. If running under a SecurityManager, this method must have read access to the file.

Parameters:
file - the policy file to load
Throws:
java.io.IOException - if the file does not exist in the file system
java.lang.SecurityException - if the running SecurityManager denied the Permission java.io.FilePermission "path-to-file", "read"

parseProtectionDomain

protected java.security.ProtectionDomain parseProtectionDomain(java.lang.String granteeString,
                                                               java.lang.String permissionString)

Returns a valid ProtectionDomain by parsing a grant block that denotes the codebase, pricipals and signers to whom permissions should be granted. The grant block is comprised of two parts, which are passed to this method: the grantee string and the permissions string.

The grantee is the codebase, principals and signers portion of the grant block, minus the word "grant" itself. The following are all valid grantee strings:

 codeBase "file:${user.home}/workspace/freshcookies-security/target-test/"
 signedBy "testsigner", principal org.freshcookies.security.policy.GenericPrincipal "Foo", principal "Bar"
 signedBy "testsigner"

The grantee string is processed as follows:

The permission string is the portion of the grant statement between braces ({}). Permissions are delimited with a semicolons. The permissions string is processed as follows:

The ProtectionDomain that is returned by this method will be constructed with CodeSource, PermissionCollection, and Principal array parameters that correspond to those parsed from the grantee and permission strings. The ClassLoader passed to ProtectionDomain.ProtectionDomain(CodeSource, PermissionCollection, ClassLoader, Principal[]) will be the current ClassLoader; that is, the one that

This method needs certain privileges to run correctly under a SecurityManager. It requires the NetPermission specifyStreamHander to construct CodeSource URLs. It will also require permission to obtain the ProtectionDomain for loaded classes. For example, an appropriate grant statement might look like this:

grant codeBase "file:/path-to-this-jar/freshcookies-security-version.jar" {
   permission java.lang.RuntimePermission "getProtectionDomain";
   permission java.net.NetPermission "specifyStreamHandler";
};

Parameters:
granteeString - the grantee string, which is the portion of the grant block between the grant token and the opening brace ({) that begins the individual permission lines
permissionString - the permission string, which is the portion of the grant block between opening and closing braces({}). This string will be split into individual permissions and parsed
Returns:
the initialized ProtectionDomain
Throws:
java.lang.SecurityException - when the SecurityManager, if running, denies the protection domain of this class any of the following permissions:
  • java.lang.RuntimePermission "accessClassInPackage.java.,javax."
  • java.lang.RuntimePermission "accessClassInPackage.custom-principal-packages"
  • java.lang.RuntimePermission "createClassLoader"
  • java.lang.RuntimePermission "getProtectionDomain"
  • java.net.NetPermission "specifyStreamHandler"

secureExists

protected static boolean secureExists(java.io.File file)
                               throws java.lang.SecurityException
Returns true if a supplied File exists in the file system, and the SecurityManager (if running) has granted read access to it.

Parameters:
file - the file to check
Returns:
the result
Throws:
java.lang.SecurityException - if the running SecurityManager denied the Permission java.io.FilePermission "path-to-file", "read"

extractCodeBaseUrl

protected java.net.URL extractCodeBaseUrl(java.lang.String token)
Extracts the URL from a codeBase token string. Example: codeBase "file:${user.home}/workspace/freshcookies-security/target/classes/-".

Parameters:
token - the string containing the URL
Returns:
the constructed URL

extractPrincipal

protected java.security.Principal extractPrincipal(java.lang.String token)
Extracts the Principal from a principal token string. Example: principal org.freshcookies.security.policy.GenericPrincipal "Andrew Jaquith".

Parameters:
token - the string containing the Principal
Returns:
the constructed Principal

extractSigningCertificates

protected java.security.cert.Certificate[] extractSigningCertificates(java.lang.String token)
Extracts and resolves signing Certificates from a signedBy token string. Example: signedBy "testsigner".

Parameters:
token - the string containing the certificate alias
Returns:
the resolved certificate chain

extractPermissionCollection

protected java.security.PermissionCollection extractPermissionCollection(java.lang.String permissions)

Parses permissions from a grant block and returns a PermissionCollection. The permissions are extracted from the string portion of the grant statement between braces ({}). Permissions are delimited with a semicolons. This method will attempt to load the specified permissions into memory using the current ClassLoader. If a particular permission statement contains a signedBy token, this method will also attempt to verify that the Permission was signed by the keystore alias specified in the statement.

Parameters:
permissions - the permissions string, which will be parsed into individual permissions and parsed. For example, the following string will be parsed as two separate permissions:
 permission java.util.PropertyPermission "java.version", "read";
 permission java.util.PropertyPermission "java.vendor", "read";
Returns:
the instantiated collection of Permissions for the Grantee
Throws:
java.lang.SecurityException - when the SecurityManager, if running, denies the protection domain of this class any of the following permissions:
  • java.lang.RuntimePermission "accessClassInPackage.java.,javax."
  • java.lang.RuntimePermission "accessClassInPackage.custom-permission-packages"
  • java.lang.RuntimePermission "createClassLoader"
  • java.lang.RuntimePermission "getProtectionDomain"

isValid

public boolean isValid()
Returns true> if the security policy file was parsed correctly and validated without errors. If the read() method has not yet been called, this method returns false.

Returns:
true if the policy file parsed correctly, false otherwise

isVerified

public boolean isVerified(java.lang.Class clazz)
                   throws java.io.IOException
Returns code if a supplied class' CodeSource is digitally signed, and the certificate used to sign it can be found in this PolicyReader's keystore. If the CodeSource is not signed, or if a matching certificate cannot be found, this method returns false.

Parameters:
clazz - the class to verify
Returns:
the result of the verification operation
Throws:
java.io.IOException - if the keystore cannot be read
java.lang.SecurityException - if the running SecurityManager denied this ProtectionDomain the permission java.lang.RuntimePermission "getProtectionDomain"
See Also:
getKeyStore()