blob: 7214e3f036211ec777003ac08b3d40a9d42d72e0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Alexey V. Varlamov
* @version $Revision$
*/
package org.apache.harmony.security.fortress;
import java.io.File;
import java.net.URL;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Policy;
import java.security.ProtectionDomain;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.WeakHashMap;
import org.apache.harmony.security.PolicyEntry;
/**
* Default Policy implementation based on policy configuration files. This
* implementation recognizes text files, consisting of clauses with the
* following syntax:
*
* <pre>
* keystore &quot;some_keystore_url&quot; [, &quot;keystore_type&quot;];
* </pre>
<pre>
* grant [SignedBy &quot;signer_names&quot;] [, CodeBase &quot;URL&quot;]
* [, Principal [principal_class_name] &quot;principal_name&quot;]
* [, Principal [principal_class_name] &quot;principal_name&quot;] ... {
* permission permission_class_name [ &quot;target_name&quot; ] [, &quot;action&quot;]
* [, SignedBy &quot;signer_names&quot;];
* permission ...
* };
*
* </pre>
*
* The <i>keystore </i> 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
* <i>grant </i> entries of the file. The policy file can contain any number of
* <i>keystore </i> 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. <br>
* The <i>grant </i> 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
* <i>CodeBase </i> and <i>SignedBy </i> fields. The <i>CodeBase </i> value must
* be in URL format, while <i>SignedBy </i> 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.
* <br>
* 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 <i>Principal </i> fields in the <i>grant </i> clause.
* Each Principal is specified as class/name pair; name and class can be either
* concrete value or wildcard <i>* </i>. 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. <br>
* The order between the <i>CodeBase </i>, <i>SignedBy </i>, and <i>Principal
* </i> fields does not matter. The policy file can contain any number of grant
* clauses. <br>
* Each <i>grant </i> clause must contain one or more <i>permission </i> entry.
* The permission entry consist of a fully qualified class name along with
* optional <i>name </i>, <i>actions </i> and <i>signedby </i> 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). <br>
* <br>
* The policy content may be parameterized via property expansion. Namely,
* expressions like <i>${key} </i> are replaced by values of corresponding
* system properties. Also, the special <i>slash </i> key (i.e. ${/}) is
* supported, it is a shortcut to &quot;file.separator&quot; 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 &quot;policy.expandProperties&quot; property
* to <i>true </i>. <br>
* If property expansion fails (due to a missing key), a corresponding entry is
* ignored. For fields of <i>keystore </i> and <i>grant </i> clauses, the whole
* clause is ignored, and for <i>permission </i> entry, only that entry is
* ignored. <br>
* <br>
* The policy also supports generalized expansion in permissions names, of
* expressions like <i>${{protocol:data}} </i>. Currently the following
* protocols supported:
* <dl>
* <dt>self
* <dd>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 <i>class &quot;name&quot; </i>. If parental
* Grant entry has no Principals, the permission is ignored.
* <dt>alias: <i>name </i>
* <dd>Denotes substitution of a KeyStore alias. Namely, if a KeyStore has an
* X.509 certificate associated with the specified name, then replaced by
* <i>javax.security.auth.x500.X500Principal &quot; <i>DN </i>&quot; </i>
* string, where <i>DN </i> is a certificate's subject distinguished name.
* </dl>
* <br>
* <br>
* 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)
*/
public class DefaultPolicy extends Policy {
/**
* System property for dynamically added policy location.
*/
public static final String JAVA_SECURITY_POLICY = "java.security.policy";
/**
* Prefix for numbered Policy locations specified in security.properties.
*/
public static final String POLICY_URL_PREFIX = "policy.url.";
// A set of PolicyEntries constituting this Policy.
private final Set<PolicyEntry> grants = new HashSet<PolicyEntry>();
// Calculated Permissions cache, organized as
// Map{Object->Collection&lt;Permission&gt;}.
// The Object is a ProtectionDomain, a CodeSource or
// any other permissions-granted entity.
private final Map<Object, Collection<Permission>> cache = new WeakHashMap<Object, Collection<Permission>>();
// A specific parser for a particular policy file format.
private final DefaultPolicyParser parser;
// A flag indicating brand new instance which needs to be loaded
// on the first appeal to it's data.
private boolean initialized;
/**
* Default constructor, equivalent to
* <code>DefaultPolicy(new DefaultPolicyParser())</code>.
*/
public DefaultPolicy() {
this(new DefaultPolicyParser());
}
/**
* Extension constructor for plugging-in a custom parser. Defers policy data
* initialization before the first <code>getPermissions()</code> call
* (though policy may be refreshed explicitly, as well).
*/
public DefaultPolicy(DefaultPolicyParser dpr) {
parser = dpr;
initialized = false;
refresh();
}
/**
* 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 <code>null</code> if the domain is <code>null</code>.
*/
public PermissionCollection getPermissions(ProtectionDomain pd) {
if (!initialized) {
synchronized (this) {
if (!initialized) {
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);
}
/**
* Returns collection of permissions allowed for the codesource
* according to the policy.
* The evaluation assumes that current principals are undefined.
*/
public PermissionCollection getPermissions(CodeSource cs) {
if (!initialized) {
synchronized (this) {
if (!initialized) {
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);
}
/**
* 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. <br>
* This method is declared synchronized to avoid concurrent reloading.
*
* @see PolicyUtils#getPolicyURLs(Properties, String, String)
*/
public synchronized void refresh() {
Set<PolicyEntry> fresh = new HashSet<PolicyEntry>();
Properties system = new Properties(AccessController
.doPrivileged(new PolicyUtils.SystemKit()));
system.setProperty("/", File.separator);
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();
}
initialized = true;
}
}