001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Alexey V. Varlamov
020: * @version $Revision$
021: */package org.apache.harmony.security.fortress;
022:
023: import java.io.File;
024: import java.net.URL;
025: import java.security.AccessController;
026: import java.security.CodeSource;
027: import java.security.Permission;
028: import java.security.PermissionCollection;
029: import java.security.Policy;
030: import java.security.ProtectionDomain;
031: import java.util.Collection;
032: import java.util.HashSet;
033: import java.util.Iterator;
034: import java.util.Map;
035: import java.util.Properties;
036: import java.util.Set;
037: import java.util.WeakHashMap;
038:
039: import org.apache.harmony.security.PolicyEntry;
040:
041: /**
042: * Default Policy implementation based on policy configuration files. This
043: * implementation recognizes text files, consisting of clauses with the
044: * following syntax:
045: *
046: * <pre>
047: * keystore "some_keystore_url" [, "keystore_type"];
048: * </pre>
049: <pre>
050: * grant [SignedBy "signer_names"] [, CodeBase "URL"]
051: * [, Principal [principal_class_name] "principal_name"]
052: * [, Principal [principal_class_name] "principal_name"] ... {
053: * permission permission_class_name [ "target_name" ] [, "action"]
054: * [, SignedBy "signer_names"];
055: * permission ...
056: * };
057: *
058: * </pre>
059: *
060: * The <i>keystore </i> clause specifies reference to a keystore, which is a
061: * database of private keys and their associated digital certificates. The
062: * keystore is used to look up the certificates of signers specified in the
063: * <i>grant </i> entries of the file. The policy file can contain any number of
064: * <i>keystore </i> entries which can appear at any ordinal position. However,
065: * only the first successfully loaded keystore is used, others are ignored. The
066: * keystore must be specified if some grant clause refers to a certificate's
067: * alias. <br>
068: * The <i>grant </i> clause associates a CodeSource (consisting of an URL and a
069: * set of certificates) of some executable code with a set of Permissions which
070: * should be granted to the code. So, the CodeSource is defined by values of
071: * <i>CodeBase </i> and <i>SignedBy </i> fields. The <i>CodeBase </i> value must
072: * be in URL format, while <i>SignedBy </i> value is a (comma-separated list of)
073: * alias(es) to keystore certificates. These fields can be omitted to denote any
074: * codebase and any signers (including case of unsigned code), respectively.
075: * <br>
076: * Also, the code may be required to be executed on behalf of some Principals
077: * (in other words, code's ProtectionDomain must have the array of Principals
078: * associated) in order to possess the Permissions. This fact is indicated by
079: * specifying one or more <i>Principal </i> fields in the <i>grant </i> clause.
080: * Each Principal is specified as class/name pair; name and class can be either
081: * concrete value or wildcard <i>* </i>. As a special case, the class value may
082: * be omitted and then the name is treated as an alias to X.509 Certificate, and
083: * the Principal is assumed to be javax.security.auth.x500.X500Principal with a
084: * name of subject's distinguished name from the certificate. <br>
085: * The order between the <i>CodeBase </i>, <i>SignedBy </i>, and <i>Principal
086: * </i> fields does not matter. The policy file can contain any number of grant
087: * clauses. <br>
088: * Each <i>grant </i> clause must contain one or more <i>permission </i> entry.
089: * The permission entry consist of a fully qualified class name along with
090: * optional <i>name </i>, <i>actions </i> and <i>signedby </i> values. Name and
091: * actions are arguments to the corresponding constructor of the permission
092: * class. SignedBy value represents the keystore alias(es) to certificate(s)
093: * used to sign the permission class. That is, this permission entry is
094: * effective (i.e., access control permission will be granted based on this
095: * entry) only if the bytecode implementation of permission class is verified to
096: * be correctly signed by the said alias(es). <br>
097: * <br>
098: * The policy content may be parameterized via property expansion. Namely,
099: * expressions like <i>${key} </i> are replaced by values of corresponding
100: * system properties. Also, the special <i>slash </i> key (i.e. ${/}) is
101: * supported, it is a shortcut to "file.separator" key. Property
102: * expansion is performed anywhere a double quoted string is allowed in the
103: * policy file. However, this feature is controlled by security properties and
104: * should be turned on by setting "policy.expandProperties" property
105: * to <i>true </i>. <br>
106: * If property expansion fails (due to a missing key), a corresponding entry is
107: * ignored. For fields of <i>keystore </i> and <i>grant </i> clauses, the whole
108: * clause is ignored, and for <i>permission </i> entry, only that entry is
109: * ignored. <br>
110: * <br>
111: * The policy also supports generalized expansion in permissions names, of
112: * expressions like <i>${{protocol:data}} </i>. Currently the following
113: * protocols supported:
114: * <dl>
115: * <dt>self
116: * <dd>Denotes substitution to a principal information of the parental Grant
117: * entry. Replaced by a space-separated list of resolved Principals (including
118: * wildcarded), each formatted as <i>class "name" </i>. If parental
119: * Grant entry has no Principals, the permission is ignored.
120: * <dt>alias: <i>name </i>
121: * <dd>Denotes substitution of a KeyStore alias. Namely, if a KeyStore has an
122: * X.509 certificate associated with the specified name, then replaced by
123: * <i>javax.security.auth.x500.X500Principal " <i>DN </i>" </i>
124: * string, where <i>DN </i> is a certificate's subject distinguished name.
125: * </dl>
126: * <br>
127: * <br>
128: * This implementation is thread-safe. The policy caches sets of calculated
129: * permissions for the requested objects (ProtectionDomains and CodeSources) via
130: * WeakHashMap; the cache is cleaned either explicitly during refresh()
131: * invocation, or naturally by garbage-collecting the corresponding objects.
132: *
133: * @see org.apache.harmony.security.PolicyUtils#getPolicyURLs(Properties, String,
134: * String)
135: */
136:
137: public class DefaultPolicy extends Policy {
138:
139: /**
140: * System property for dynamically added policy location.
141: */
142: public static final String JAVA_SECURITY_POLICY = "java.security.policy"; //$NON-NLS-1$
143:
144: /**
145: * Prefix for numbered Policy locations specified in security.properties.
146: */
147: public static final String POLICY_URL_PREFIX = "policy.url."; //$NON-NLS-1$
148:
149: // A set of PolicyEntries constituting this Policy.
150: private final Set<PolicyEntry> grants = new HashSet<PolicyEntry>();
151:
152: // Calculated Permissions cache, organized as
153: // Map{Object->Collection<Permission>}.
154: // The Object is a ProtectionDomain, a CodeSource or
155: // any other permissions-granted entity.
156: private final Map<Object, Collection<Permission>> cache = new WeakHashMap<Object, Collection<Permission>>();
157:
158: // A specific parser for a particular policy file format.
159: private final DefaultPolicyParser parser;
160:
161: // A flag indicating brand new instance which needs to be loaded
162: // on the first appeal to it's data.
163: private boolean initialized;
164:
165: /**
166: * Default constructor, equivalent to
167: * <code>DefaultPolicy(new DefaultPolicyParser())</code>.
168: */
169: public DefaultPolicy() {
170: this (new DefaultPolicyParser());
171: }
172:
173: /**
174: * Extension constructor for plugging-in a custom parser. Defers policy data
175: * initialization before the first <code>getPermissions()</code> call
176: * (though policy may be refreshed explicitly, as well).
177: */
178: public DefaultPolicy(DefaultPolicyParser dpr) {
179: parser = dpr;
180: initialized = false;
181: refresh();
182: }
183:
184: /**
185: * Returns collection of permissions allowed for the domain
186: * according to the policy. The evaluated characteristics of the
187: * domain are it's codesource and principals; they are assumed
188: * to be <code>null</code> if the domain is <code>null</code>.
189: */
190: public PermissionCollection getPermissions(ProtectionDomain pd) {
191: if (!initialized) {
192: synchronized (this ) {
193: if (!initialized) {
194: refresh();
195: }
196: }
197: }
198: Collection<Permission> pc = cache.get(pd);
199: if (pc == null) {
200: //have to synchronize to exclude cache pollution after refresh
201: synchronized (cache) {
202:
203: // double check in case value has been put to cache
204: // while we've been awaiting monitor
205: pc = cache.get(pd);
206: if (pc == null) {
207: pc = new HashSet<Permission>();
208: Iterator<PolicyEntry> it = grants.iterator();
209: while (it.hasNext()) {
210: PolicyEntry ge = (PolicyEntry) it.next();
211: if (ge.impliesPrincipals(pd == null ? null : pd
212: .getPrincipals())
213: && ge
214: .impliesCodeSource(pd == null ? null
215: : pd.getCodeSource())) {
216: pc.addAll(ge.getPermissions());
217: }
218: }
219: cache.put(pd, pc);
220: }
221: }
222: }
223: return PolicyUtils.toPermissionCollection(pc);
224:
225: }
226:
227: /**
228: * Returns collection of permissions allowed for the codesource
229: * according to the policy.
230: * The evaluation assumes that current principals are undefined.
231: */
232: public PermissionCollection getPermissions(CodeSource cs) {
233: if (!initialized) {
234: synchronized (this ) {
235: if (!initialized) {
236: refresh();
237: }
238: }
239: }
240: Collection<Permission> pc = cache.get(cs);
241: if (pc == null) {
242: //have to synchronize to exclude cache pollution after refresh
243: synchronized (cache) {
244:
245: // double check in case value has been put to cache
246: // while we've been awaiting monitor
247: pc = cache.get(cs);
248: if (pc == null) {
249: pc = new HashSet<Permission>();
250: Iterator<PolicyEntry> it = grants.iterator();
251: while (it.hasNext()) {
252: PolicyEntry ge = (PolicyEntry) it.next();
253: if (ge.impliesPrincipals(null)
254: && ge.impliesCodeSource(cs)) {
255: pc.addAll(ge.getPermissions());
256: }
257: }
258: cache.put(cs, pc);
259: }
260: }
261: }
262: return PolicyUtils.toPermissionCollection(pc);
263: }
264:
265: /**
266: * Gets fresh list of locations and tries to load all of them in sequence;
267: * failed loads are ignored. After processing all locations, old policy
268: * settings are discarded and new ones come into force. <br>
269: * This method is declared synchronized to avoid concurrent reloading.
270: *
271: * @see PolicyUtils#getPolicyURLs(Properties, String, String)
272: */
273: public synchronized void refresh() {
274: Set<PolicyEntry> fresh = new HashSet<PolicyEntry>();
275: Properties system = new Properties(AccessController
276: .doPrivileged(new PolicyUtils.SystemKit()));
277: system.setProperty("/", File.separator); //$NON-NLS-1$
278: URL[] policyLocations = PolicyUtils.getPolicyURLs(system,
279: JAVA_SECURITY_POLICY, POLICY_URL_PREFIX);
280: for (int i = 0; i < policyLocations.length; i++) {
281: try {
282: //TODO debug log
283: //System.err.println("Parsing policy file: " + policyLocations[i]);
284: fresh.addAll(parser.parse(policyLocations[i], system));
285: } catch (Exception e) {
286: // TODO log warning
287: //System.err.println("Ignoring policy file: "
288: // + policyLocations[i] + ". Reason:\n"+ e);
289: }
290: }
291: // XXX: what if new policy is empty - provide some default??
292:
293: // we could safely replace references instead of
294: // synchronizing access:
295: // <pre>
296: // grants = fresh;
297: // cache = new WeakHashMap();
298: // </pre>
299: // but there is possibility that concurrent thread will put
300: // old data to cache right after we finish refresh(),
301: // thus synchronization is added in getPermissions() methods...
302: synchronized (cache) {
303: grants.clear();
304: grants.addAll(fresh);
305:
306: cache.clear();
307: }
308: initialized = true;
309: }
310: }
|