FileDocCategorySizeDatePackage
DefaultPolicy.javaAPI DocAndroid 1.5 API13477Wed May 06 22:41:06 BST 2009org.apache.harmony.security.fortress

DefaultPolicy

public class DefaultPolicy extends Policy
Default Policy implementation based on policy configuration files. This implementation recognizes text files, consisting of clauses with the following syntax:
keystore "some_keystore_url" [, "keystore_type"];
grant [SignedBy "signer_names"] [, CodeBase "URL"]
[, Principal [principal_class_name] "principal_name"]
[, Principal [principal_class_name] "principal_name"] ... {
permission permission_class_name [ "target_name" ] [, "action"]
[, SignedBy "signer_names"];
permission ...
};

The keystore clause specifies reference to a keystore, which is a database of private keys and their associated digital certificates. The keystore is used to look up the certificates of signers specified in the grant entries of the file. The policy file can contain any number of keystore entries which can appear at any ordinal position. However, only the first successfully loaded keystore is used, others are ignored. The keystore must be specified if some grant clause refers to a certificate's alias.
The grant clause associates a CodeSource (consisting of an URL and a set of certificates) of some executable code with a set of Permissions which should be granted to the code. So, the CodeSource is defined by values of CodeBase and SignedBy fields. The CodeBase value must be in URL format, while SignedBy value is a (comma-separated list of) alias(es) to keystore certificates. These fields can be omitted to denote any codebase and any signers (including case of unsigned code), respectively.
Also, the code may be required to be executed on behalf of some Principals (in other words, code's ProtectionDomain must have the array of Principals associated) in order to possess the Permissions. This fact is indicated by specifying one or more Principal fields in the grant clause. Each Principal is specified as class/name pair; name and class can be either concrete value or wildcard * . As a special case, the class value may be omitted and then the name is treated as an alias to X.509 Certificate, and the Principal is assumed to be javax.security.auth.x500.X500Principal with a name of subject's distinguished name from the certificate.
The order between the CodeBase , SignedBy , and Principal fields does not matter. The policy file can contain any number of grant clauses.
Each grant clause must contain one or more permission entry. The permission entry consist of a fully qualified class name along with optional name , actions and signedby values. Name and actions are arguments to the corresponding constructor of the permission class. SignedBy value represents the keystore alias(es) to certificate(s) used to sign the permission class. That is, this permission entry is effective (i.e., access control permission will be granted based on this entry) only if the bytecode implementation of permission class is verified to be correctly signed by the said alias(es).

The policy content may be parameterized via property expansion. Namely, expressions like ${key} are replaced by values of corresponding system properties. Also, the special slash key (i.e. ${/}) is supported, it is a shortcut to "file.separator" key. Property expansion is performed anywhere a double quoted string is allowed in the policy file. However, this feature is controlled by security properties and should be turned on by setting "policy.expandProperties" property to true .
If property expansion fails (due to a missing key), a corresponding entry is ignored. For fields of keystore and grant clauses, the whole clause is ignored, and for permission entry, only that entry is ignored.

The policy also supports generalized expansion in permissions names, of expressions like ${{protocol:data}} . Currently the following protocols supported:
self
Denotes substitution to a principal information of the parental Grant entry. Replaced by a space-separated list of resolved Principals (including wildcarded), each formatted as class "name" . If parental Grant entry has no Principals, the permission is ignored.
alias: name
Denotes substitution of a KeyStore alias. Namely, if a KeyStore has an X.509 certificate associated with the specified name, then replaced by javax.security.auth.x500.X500Principal " DN " string, where DN is a certificate's subject distinguished name.


This implementation is thread-safe. The policy caches sets of calculated permissions for the requested objects (ProtectionDomains and CodeSources) via WeakHashMap; the cache is cleaned either explicitly during refresh() invocation, or naturally by garbage-collecting the corresponding objects.
see
org.apache.harmony.security.fortress.PolicyUtils#getPolicyURLs( Properties, String, String)

Fields Summary
public static final String
JAVA_SECURITY_POLICY
System property for dynamically added policy location.
public static final String
POLICY_URL_PREFIX
Prefix for numbered Policy locations specified in security.properties.
private final Set
grants
private final Map
cache
private final DefaultPolicyParser
parser
private boolean
initiailized
Constructors Summary
public DefaultPolicy()
Default constructor, equivalent to DefaultPolicy(new DefaultPolicyParser()).


               
      
        this(new DefaultPolicyParser());
    
public DefaultPolicy(DefaultPolicyParser dpr)
Extension constructor for plugging-in a custom parser. Defers policy data initialization before the first getPermissions() call (though policy may be refreshed explicitly, as well).

        parser = dpr;
        initiailized = false;
        refresh();
    
Methods Summary
public java.security.PermissionCollectiongetPermissions(java.security.ProtectionDomain pd)
Returns collection of permissions allowed for the domain according to the policy. The evaluated characteristics of the domain are it's codesource and principals; they are assumed to be null if the domain is null.

        if (!initiailized) {
            synchronized (this) {
                if (!initiailized) {
                    refresh();
                }
            }
        }
        Collection<Permission> pc = cache.get(pd);
        if (pc == null) {
            //have to synchronize to exclude cache pollution after refresh
            synchronized (cache) {

                // double check in case value has been put to cache
                // while we've been awaiting monitor
                pc = cache.get(pd);
                if (pc == null) {
                    pc = new HashSet<Permission>();
                    Iterator<PolicyEntry> it = grants.iterator();
                    while (it.hasNext()) {
                        PolicyEntry ge = (PolicyEntry)it.next();
                        if (ge.impliesPrincipals(pd == null ? null : pd.getPrincipals())
                            && ge.impliesCodeSource(pd == null ? null : pd.getCodeSource())) {
                            pc.addAll(ge.getPermissions());
                        }
                    }
                    cache.put(pd, pc);
                }
            }
        }
        return PolicyUtils.toPermissionCollection(pc);

    
public java.security.PermissionCollectiongetPermissions(java.security.CodeSource cs)
Returns collection of permissions allowed for the codesource according to the policy. The evaluation assumes that current principals are undefined.

        if (!initiailized) {
            synchronized (this) {
                if (!initiailized) {
                    refresh();
                }
            }
        }
        Collection<Permission> pc = cache.get(cs);
        if (pc == null) {
            //have to synchronize to exclude cache pollution after refresh
            synchronized (cache) {

                // double check in case value has been put to cache
                // while we've been awaiting monitor
                pc = cache.get(cs);
                if (pc == null) {
                    pc = new HashSet<Permission>();
                    Iterator<PolicyEntry> it = grants.iterator();
                    while (it.hasNext()) {
                        PolicyEntry ge = (PolicyEntry)it.next();
                        if (ge.impliesPrincipals(null)
                            && ge.impliesCodeSource(cs)) {
                            pc.addAll(ge.getPermissions());
                        }
                    }
                    cache.put(cs, pc);
                }
            }
        }
        return PolicyUtils.toPermissionCollection(pc);
    
public synchronized voidrefresh()
Gets fresh list of locations and tries to load all of them in sequence; failed loads are ignored. After processing all locations, old policy settings are discarded and new ones come into force.
This method is declared synchronized to avoid concurrent reloading.

see
PolicyUtils#getPolicyURLs(Properties, String, String)

        Set<PolicyEntry> fresh = new HashSet<PolicyEntry>();
        Properties system = new Properties(AccessController
                .doPrivileged(new PolicyUtils.SystemKit()));
        system.setProperty("/", File.separator); //$NON-NLS-1$
        URL[] policyLocations = PolicyUtils.getPolicyURLs(system,
                                                          JAVA_SECURITY_POLICY,
                                                          POLICY_URL_PREFIX);
        for (int i = 0; i < policyLocations.length; i++) {
            try {
                //TODO debug log
                //System.err.println("Parsing policy file: " + policyLocations[i]);
                fresh.addAll(parser.parse(policyLocations[i], system));
            } catch (Exception e) {
                // TODO log warning
                //System.err.println("Ignoring policy file: " 
                //                 + policyLocations[i] + ". Reason:\n"+ e);
            }
        }
        // XXX: what if new policy is empty - provide some default??

        // we could safely replace references instead of
        // synchronizing access:
        // <pre>
        // grants = fresh;
        // cache = new WeakHashMap();
        // </pre>
        // but there is possibility that concurrent thread will put
        // old data to cache right after we finish refresh(),
        // thus synchronization is added in getPermissions() methods...
        synchronized (cache) {
            grants.clear();
            grants.addAll(fresh);

            cache.clear();
        }
        initiailized = true;