| /* |
| * 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 "some_keystore_url" [, "keystore_type"]; |
| * </pre> |
| <pre> |
| * 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 ... |
| * }; |
| * |
| * </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 "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 <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 "name" </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 " <i>DN </i>" </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<Permission>}. |
| // 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; |
| } |
| } |