mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-27 19:33:39 +00:00
1197 lines
44 KiB
Java
1197 lines
44 KiB
Java
/*
|
|
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package sun.security.provider;
|
|
|
|
import java.io.*;
|
|
import java.lang.reflect.*;
|
|
import java.net.URL;
|
|
import java.util.*;
|
|
|
|
import java.security.AccessController;
|
|
import java.security.CodeSource;
|
|
import java.security.KeyStore;
|
|
import java.security.KeyStoreException;
|
|
import java.security.Permission;
|
|
import java.security.Permissions;
|
|
import java.security.PermissionCollection;
|
|
import java.security.Principal;
|
|
import java.security.PrivilegedAction;
|
|
import java.security.UnresolvedPermission;
|
|
import java.security.Security;
|
|
import java.security.cert.Certificate;
|
|
import java.security.cert.X509Certificate;
|
|
|
|
import javax.security.auth.Subject;
|
|
import javax.security.auth.PrivateCredentialPermission;
|
|
|
|
import sun.security.provider.PolicyParser.GrantEntry;
|
|
import sun.security.provider.PolicyParser.PermissionEntry;
|
|
import sun.security.provider.PolicyParser.PrincipalEntry;
|
|
import sun.security.util.Debug;
|
|
import sun.security.util.PolicyUtil;
|
|
import sun.security.util.PropertyExpander;
|
|
|
|
/**
|
|
* See {@code com.sun.security.auth.PolicyFile} for the class description.
|
|
* This class is necessary in order to support a default
|
|
* {@code javax.security.auth.Policy} implementation on the compact1 and
|
|
* compact2 profiles.
|
|
*
|
|
* @deprecated As of JDK 1.4, replaced by
|
|
* {@code sun.security.provider.PolicyFile}.
|
|
* This class is entirely deprecated.
|
|
*/
|
|
@Deprecated
|
|
@SuppressWarnings("removal")
|
|
public class AuthPolicyFile extends javax.security.auth.Policy {
|
|
|
|
static final ResourceBundle rb =
|
|
AccessController.doPrivileged(new PrivilegedAction<ResourceBundle>() {
|
|
@Override public ResourceBundle run() {
|
|
return (ResourceBundle.getBundle
|
|
("sun.security.util.AuthResources"));
|
|
}
|
|
});
|
|
|
|
private static final Debug debug = Debug.getInstance("policy",
|
|
"\t[Auth Policy]");
|
|
|
|
private static final String AUTH_POLICY = "java.security.auth.policy";
|
|
private static final String SECURITY_MANAGER = "java.security.manager";
|
|
private static final String AUTH_POLICY_URL = "auth.policy.url.";
|
|
|
|
private Vector<PolicyEntry> policyEntries;
|
|
private Hashtable<Object, Object> aliasMapping;
|
|
|
|
private boolean initialized = false;
|
|
|
|
private boolean expandProperties = true;
|
|
private boolean ignoreIdentityScope = true;
|
|
|
|
// for use with the reflection API
|
|
private static final Class<?>[] PARAMS = { String.class, String.class};
|
|
|
|
/**
|
|
* Initializes the Policy object and reads the default policy
|
|
* configuration file(s) into the Policy object.
|
|
*/
|
|
public AuthPolicyFile() {
|
|
// initialize Policy if either the AUTH_POLICY or
|
|
// SECURITY_MANAGER properties are set
|
|
String prop = System.getProperty(AUTH_POLICY);
|
|
|
|
if (prop == null) {
|
|
prop = System.getProperty(SECURITY_MANAGER);
|
|
}
|
|
if (prop != null) {
|
|
init();
|
|
}
|
|
}
|
|
|
|
private synchronized void init() {
|
|
if (initialized) {
|
|
return;
|
|
}
|
|
|
|
policyEntries = new Vector<PolicyEntry>();
|
|
aliasMapping = new Hashtable<Object, Object>(11);
|
|
|
|
initPolicyFile();
|
|
initialized = true;
|
|
}
|
|
|
|
@Override
|
|
public synchronized void refresh() {
|
|
|
|
java.lang.SecurityManager sm = System.getSecurityManager();
|
|
if (sm != null) {
|
|
sm.checkPermission(new javax.security.auth.AuthPermission
|
|
("refreshPolicy"));
|
|
}
|
|
|
|
// XXX
|
|
//
|
|
// 1) if code instantiates PolicyFile directly, then it will need
|
|
// all the permissions required for the PolicyFile initialization
|
|
// 2) if code calls Policy.getPolicy, then it simply needs
|
|
// AuthPermission(getPolicy), and the javax.security.auth.Policy
|
|
// implementation instantiates PolicyFile in a doPrivileged block
|
|
// 3) if after instantiating a Policy (either via #1 or #2),
|
|
// code calls refresh, it simply needs
|
|
// AuthPermission(refreshPolicy). then PolicyFile wraps
|
|
// the refresh in a doPrivileged block.
|
|
initialized = false;
|
|
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
|
@Override public Void run() {
|
|
init();
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
private KeyStore initKeyStore(URL policyUrl, String keyStoreName,
|
|
String keyStoreType) {
|
|
if (keyStoreName != null) {
|
|
try {
|
|
/*
|
|
* location of keystore is specified as absolute URL in policy
|
|
* file, or is relative to URL of policy file
|
|
*/
|
|
URL keyStoreUrl = null;
|
|
try {
|
|
keyStoreUrl = new URL(keyStoreName);
|
|
// absolute URL
|
|
} catch (java.net.MalformedURLException e) {
|
|
// relative URL
|
|
keyStoreUrl = new URL(policyUrl, keyStoreName);
|
|
}
|
|
|
|
if (debug != null) {
|
|
debug.println("reading keystore"+keyStoreUrl);
|
|
}
|
|
|
|
InputStream inStream = new BufferedInputStream(
|
|
PolicyUtil.getInputStream(keyStoreUrl));
|
|
|
|
KeyStore ks;
|
|
if (keyStoreType != null)
|
|
ks = KeyStore.getInstance(keyStoreType);
|
|
else
|
|
ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
|
ks.load(inStream, null);
|
|
inStream.close();
|
|
return ks;
|
|
} catch (Exception e) {
|
|
// ignore, treat it like we have no keystore
|
|
if (debug != null) {
|
|
e.printStackTrace();
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void initPolicyFile() {
|
|
|
|
String prop = Security.getProperty("policy.expandProperties");
|
|
if (prop != null) {
|
|
expandProperties = prop.equalsIgnoreCase("true");
|
|
}
|
|
|
|
String iscp = Security.getProperty("policy.ignoreIdentityScope");
|
|
if (iscp != null) {
|
|
ignoreIdentityScope = iscp.equalsIgnoreCase("true");
|
|
}
|
|
|
|
String allowSys = Security.getProperty("policy.allowSystemProperty");
|
|
if (allowSys != null && allowSys.equalsIgnoreCase("true")) {
|
|
String extra_policy = System.getProperty(AUTH_POLICY);
|
|
if (extra_policy != null) {
|
|
boolean overrideAll = false;
|
|
if (extra_policy.startsWith("=")) {
|
|
overrideAll = true;
|
|
extra_policy = extra_policy.substring(1);
|
|
}
|
|
try {
|
|
extra_policy = PropertyExpander.expand(extra_policy);
|
|
URL policyURL;
|
|
File policyFile = new File(extra_policy);
|
|
if (policyFile.exists()) {
|
|
policyURL =
|
|
new URL("file:" + policyFile.getCanonicalPath());
|
|
} else {
|
|
policyURL = new URL(extra_policy);
|
|
}
|
|
if (debug != null) {
|
|
debug.println("reading " + policyURL);
|
|
}
|
|
init(policyURL);
|
|
} catch (Exception e) {
|
|
// ignore.
|
|
if (debug != null) {
|
|
debug.println("caught exception: " + e);
|
|
}
|
|
|
|
}
|
|
if (overrideAll) {
|
|
if (debug != null) {
|
|
debug.println("overriding other policies!");
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int n = 1;
|
|
boolean loaded_one = false;
|
|
String policy_url;
|
|
|
|
while ((policy_url = Security.getProperty(AUTH_POLICY_URL+n)) != null) {
|
|
try {
|
|
policy_url = PropertyExpander.expand(policy_url).replace
|
|
(File.separatorChar, '/');
|
|
if (debug != null) {
|
|
debug.println("reading " + policy_url);
|
|
}
|
|
init(new URL(policy_url));
|
|
loaded_one = true;
|
|
} catch (Exception e) {
|
|
if (debug != null) {
|
|
debug.println("error reading policy " + e);
|
|
e.printStackTrace();
|
|
}
|
|
// ignore that policy
|
|
}
|
|
n++;
|
|
}
|
|
|
|
if (loaded_one == false) {
|
|
// do not load a static policy
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks public key. If it is marked as trusted in
|
|
* the identity database, add it to the policy
|
|
* with the AllPermission.
|
|
*/
|
|
private boolean checkForTrustedIdentity(final Certificate cert) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Reads a policy configuration into the Policy object using a
|
|
* Reader object.
|
|
*
|
|
* @param policyFile the policy Reader object.
|
|
*/
|
|
private void init(URL policy) {
|
|
PolicyParser pp = new PolicyParser(expandProperties);
|
|
try (InputStreamReader isr
|
|
= new InputStreamReader(PolicyUtil.getInputStream(policy))) {
|
|
pp.read(isr);
|
|
KeyStore keyStore = initKeyStore(policy, pp.getKeyStoreUrl(),
|
|
pp.getKeyStoreType());
|
|
Enumeration<GrantEntry> enum_ = pp.grantElements();
|
|
while (enum_.hasMoreElements()) {
|
|
GrantEntry ge = enum_.nextElement();
|
|
addGrantEntry(ge, keyStore);
|
|
}
|
|
} catch (PolicyParser.ParsingException pe) {
|
|
System.err.println(AUTH_POLICY +
|
|
rb.getString(".error.parsing.") + policy);
|
|
System.err.println(AUTH_POLICY + rb.getString("COLON") +
|
|
pe.getMessage());
|
|
if (debug != null) {
|
|
pe.printStackTrace();
|
|
}
|
|
} catch (Exception e) {
|
|
if (debug != null) {
|
|
debug.println("error parsing " + policy);
|
|
debug.println(e.toString());
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given a PermissionEntry, create a codeSource.
|
|
*
|
|
* @return null if signedBy alias is not recognized
|
|
*/
|
|
CodeSource getCodeSource(GrantEntry ge, KeyStore keyStore)
|
|
throws java.net.MalformedURLException
|
|
{
|
|
Certificate[] certs = null;
|
|
if (ge.signedBy != null) {
|
|
certs = getCertificates(keyStore, ge.signedBy);
|
|
if (certs == null) {
|
|
// we don't have a key for this alias,
|
|
// just return
|
|
if (debug != null) {
|
|
debug.println(" no certs for alias " +
|
|
ge.signedBy + ", ignoring.");
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
URL location;
|
|
if (ge.codeBase != null) {
|
|
location = new URL(ge.codeBase);
|
|
} else {
|
|
location = null;
|
|
}
|
|
|
|
if (ge.principals == null || ge.principals.size() == 0) {
|
|
return (canonicalizeCodebase
|
|
(new CodeSource(location, certs),
|
|
false));
|
|
} else {
|
|
return (canonicalizeCodebase
|
|
(new SubjectCodeSource(null, ge.principals, location, certs),
|
|
false));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add one policy entry to the vector.
|
|
*/
|
|
private void addGrantEntry(GrantEntry ge, KeyStore keyStore) {
|
|
|
|
if (debug != null) {
|
|
debug.println("Adding policy entry: ");
|
|
debug.println(" signedBy " + ge.signedBy);
|
|
debug.println(" codeBase " + ge.codeBase);
|
|
if (ge.principals != null) {
|
|
for (PrincipalEntry pppe : ge.principals) {
|
|
debug.println(" " + pppe.getPrincipalClass() +
|
|
" " + pppe.getPrincipalName());
|
|
}
|
|
}
|
|
debug.println();
|
|
}
|
|
|
|
try {
|
|
CodeSource codesource = getCodeSource(ge, keyStore);
|
|
// skip if signedBy alias was unknown...
|
|
if (codesource == null) return;
|
|
|
|
PolicyEntry entry = new PolicyEntry(codesource);
|
|
Enumeration<PermissionEntry> enum_ = ge.permissionElements();
|
|
while (enum_.hasMoreElements()) {
|
|
PermissionEntry pe = enum_.nextElement();
|
|
try {
|
|
// XXX special case PrivateCredentialPermission-SELF
|
|
Permission perm;
|
|
if (pe.permission.equals
|
|
("javax.security.auth.PrivateCredentialPermission") &&
|
|
pe.name.endsWith(" self")) {
|
|
perm = getInstance(pe.permission,
|
|
pe.name + " \"self\"",
|
|
pe.action);
|
|
} else {
|
|
perm = getInstance(pe.permission,
|
|
pe.name,
|
|
pe.action);
|
|
}
|
|
entry.add(perm);
|
|
if (debug != null) {
|
|
debug.println(" "+perm);
|
|
}
|
|
} catch (ClassNotFoundException cnfe) {
|
|
Certificate[] certs;
|
|
if (pe.signedBy != null) {
|
|
certs = getCertificates(keyStore, pe.signedBy);
|
|
} else {
|
|
certs = null;
|
|
}
|
|
|
|
// only add if we had no signer or we had a
|
|
// a signer and found the keys for it.
|
|
if (certs != null || pe.signedBy == null) {
|
|
Permission perm = new UnresolvedPermission(
|
|
pe.permission,
|
|
pe.name,
|
|
pe.action,
|
|
certs);
|
|
entry.add(perm);
|
|
if (debug != null) {
|
|
debug.println(" "+perm);
|
|
}
|
|
}
|
|
} catch (java.lang.reflect.InvocationTargetException ite) {
|
|
System.err.println
|
|
(AUTH_POLICY +
|
|
rb.getString(".error.adding.Permission.") +
|
|
pe.permission +
|
|
rb.getString("SPACE") +
|
|
ite.getTargetException());
|
|
} catch (Exception e) {
|
|
System.err.println
|
|
(AUTH_POLICY +
|
|
rb.getString(".error.adding.Permission.") +
|
|
pe.permission +
|
|
rb.getString("SPACE") +
|
|
e);
|
|
}
|
|
}
|
|
policyEntries.addElement(entry);
|
|
} catch (Exception e) {
|
|
System.err.println
|
|
(AUTH_POLICY +
|
|
rb.getString(".error.adding.Entry.") +
|
|
ge +
|
|
rb.getString("SPACE") +
|
|
e);
|
|
}
|
|
|
|
if (debug != null) {
|
|
debug.println();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a new Permission object of the given Type. The Permission is
|
|
* created by getting the
|
|
* Class object using the <code>Class.forName</code> method, and using
|
|
* the reflection API to invoke the (String name, String actions)
|
|
* constructor on the
|
|
* object.
|
|
*
|
|
* @param type the type of Permission being created.
|
|
* @param name the name of the Permission being created.
|
|
* @param actions the actions of the Permission being created.
|
|
*
|
|
* @exception ClassNotFoundException if the particular Permission
|
|
* class could not be found.
|
|
*
|
|
* @exception IllegalAccessException if the class or initializer is
|
|
* not accessible.
|
|
*
|
|
* @exception InstantiationException if getInstance tries to
|
|
* instantiate an abstract class or an interface, or if the
|
|
* instantiation fails for some other reason.
|
|
*
|
|
* @exception NoSuchMethodException if the (String, String) constructor
|
|
* is not found.
|
|
*
|
|
* @exception InvocationTargetException if the underlying Permission
|
|
* constructor throws an exception.
|
|
*
|
|
*/
|
|
private static final Permission getInstance(String type,
|
|
String name,
|
|
String actions)
|
|
throws ClassNotFoundException,
|
|
InstantiationException,
|
|
IllegalAccessException,
|
|
NoSuchMethodException,
|
|
InvocationTargetException
|
|
{
|
|
//XXX we might want to keep a hash of created factories...
|
|
Class<?> pc = Class.forName(type);
|
|
Constructor<?> c = pc.getConstructor(PARAMS);
|
|
return (Permission) c.newInstance(new Object[] { name, actions });
|
|
}
|
|
|
|
/**
|
|
* Fetch all certs associated with this alias.
|
|
*/
|
|
Certificate[] getCertificates(KeyStore keyStore, String aliases) {
|
|
|
|
Vector<Certificate> vcerts = null;
|
|
|
|
StringTokenizer st = new StringTokenizer(aliases, ",");
|
|
int n = 0;
|
|
|
|
while (st.hasMoreTokens()) {
|
|
String alias = st.nextToken().trim();
|
|
n++;
|
|
Certificate cert = null;
|
|
// See if this alias's cert has already been cached
|
|
cert = (Certificate) aliasMapping.get(alias);
|
|
if (cert == null && keyStore != null) {
|
|
|
|
try {
|
|
cert = keyStore.getCertificate(alias);
|
|
} catch (KeyStoreException kse) {
|
|
// never happens, because keystore has already been loaded
|
|
// when we call this
|
|
}
|
|
if (cert != null) {
|
|
aliasMapping.put(alias, cert);
|
|
aliasMapping.put(cert, alias);
|
|
}
|
|
}
|
|
|
|
if (cert != null) {
|
|
if (vcerts == null) {
|
|
vcerts = new Vector<Certificate>();
|
|
}
|
|
vcerts.addElement(cert);
|
|
}
|
|
}
|
|
|
|
// make sure n == vcerts.size, since we are doing a logical *and*
|
|
if (vcerts != null && n == vcerts.size()) {
|
|
Certificate[] certs = new Certificate[vcerts.size()];
|
|
vcerts.copyInto(certs);
|
|
return certs;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enumerate all the entries in the global policy object.
|
|
* This method is used by policy admin tools. The tools
|
|
* should use the Enumeration methods on the returned object
|
|
* to fetch the elements sequentially.
|
|
*/
|
|
private final synchronized Enumeration<PolicyEntry> elements() {
|
|
return policyEntries.elements();
|
|
}
|
|
|
|
@Override
|
|
public PermissionCollection getPermissions(final Subject subject,
|
|
final CodeSource codesource) {
|
|
|
|
// 1) if code instantiates PolicyFile directly, then it will need
|
|
// all the permissions required for the PolicyFile initialization
|
|
// 2) if code calls Policy.getPolicy, then it simply needs
|
|
// AuthPermission(getPolicy), and the javax.security.auth.Policy
|
|
// implementation instantiates PolicyFile in a doPrivileged block
|
|
// 3) if after instantiating a Policy (either via #1 or #2),
|
|
// code calls getPermissions, PolicyFile wraps the call
|
|
// in a doPrivileged block.
|
|
return AccessController.doPrivileged
|
|
(new PrivilegedAction<PermissionCollection>() {
|
|
@Override public PermissionCollection run() {
|
|
SubjectCodeSource scs = new SubjectCodeSource(
|
|
subject, null,
|
|
codesource == null ? null : codesource.getLocation(),
|
|
codesource == null ? null : codesource.getCertificates());
|
|
if (initialized) {
|
|
return getPermissions(new Permissions(), scs);
|
|
} else {
|
|
return new PolicyPermissions(AuthPolicyFile.this, scs);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Examines the global policy for the specified CodeSource, and
|
|
* creates a PermissionCollection object with
|
|
* the set of permissions for that principal's protection domain.
|
|
*
|
|
* @param CodeSource the codesource associated with the caller.
|
|
* This encapsulates the original location of the code (where the code
|
|
* came from) and the public key(s) of its signer.
|
|
*
|
|
* @return the set of permissions according to the policy.
|
|
*/
|
|
PermissionCollection getPermissions(CodeSource codesource) {
|
|
|
|
if (initialized) {
|
|
return getPermissions(new Permissions(), codesource);
|
|
} else {
|
|
return new PolicyPermissions(this, codesource);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Examines the global policy for the specified CodeSource, and
|
|
* creates a PermissionCollection object with
|
|
* the set of permissions for that principal's protection domain.
|
|
*
|
|
* @param permissions the permissions to populate
|
|
* @param codesource the codesource associated with the caller.
|
|
* This encapsulates the original location of the code (where the code
|
|
* came from) and the public key(s) of its signer.
|
|
*
|
|
* @return the set of permissions according to the policy.
|
|
*/
|
|
Permissions getPermissions(final Permissions perms,
|
|
final CodeSource cs)
|
|
{
|
|
if (!initialized) {
|
|
init();
|
|
}
|
|
|
|
final CodeSource[] codesource = {null};
|
|
|
|
codesource[0] = canonicalizeCodebase(cs, true);
|
|
|
|
if (debug != null) {
|
|
debug.println("evaluate(" + codesource[0] + ")\n");
|
|
}
|
|
|
|
// needs to be in a begin/endPrivileged block because
|
|
// codesource.implies calls URL.equals which does an
|
|
// InetAddress lookup
|
|
|
|
for (int i = 0; i < policyEntries.size(); i++) {
|
|
|
|
PolicyEntry entry = policyEntries.elementAt(i);
|
|
|
|
if (debug != null) {
|
|
debug.println("PolicyFile CodeSource implies: " +
|
|
entry.codesource.toString() + "\n\n" +
|
|
"\t" + codesource[0].toString() + "\n\n");
|
|
}
|
|
|
|
if (entry.codesource.implies(codesource[0])) {
|
|
for (int j = 0; j < entry.permissions.size(); j++) {
|
|
Permission p = entry.permissions.elementAt(j);
|
|
if (debug != null) {
|
|
debug.println(" granting " + p);
|
|
}
|
|
if (!addSelfPermissions(p, entry.codesource,
|
|
codesource[0], perms)) {
|
|
// we could check for duplicates
|
|
// before adding new permissions,
|
|
// but the SubjectDomainCombiner
|
|
// already checks for duplicates later
|
|
perms.add(p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// now see if any of the keys are trusted ids.
|
|
|
|
if (!ignoreIdentityScope) {
|
|
Certificate[] certs = codesource[0].getCertificates();
|
|
if (certs != null) {
|
|
for (int k=0; k < certs.length; k++) {
|
|
if (aliasMapping.get(certs[k]) == null &&
|
|
checkForTrustedIdentity(certs[k])) {
|
|
// checkForTrustedIdentity added it
|
|
// to the policy for us. next time
|
|
// around we'll find it. This time
|
|
// around we need to add it.
|
|
perms.add(new java.security.AllPermission());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return perms;
|
|
}
|
|
|
|
/**
|
|
* Returns true if 'Self' permissions were added to the provided
|
|
* 'perms', and false otherwise.
|
|
*
|
|
* <p>
|
|
*
|
|
* @param p check to see if this Permission is a "SELF"
|
|
* PrivateCredentialPermission. <p>
|
|
*
|
|
* @param entryCs the codesource for the Policy entry.
|
|
*
|
|
* @param accCs the codesource for from the current AccessControlContext.
|
|
*
|
|
* @param perms the PermissionCollection where the individual
|
|
* PrivateCredentialPermissions will be added.
|
|
*/
|
|
private boolean addSelfPermissions(final Permission p,
|
|
CodeSource entryCs,
|
|
CodeSource accCs,
|
|
Permissions perms) {
|
|
|
|
if (!(p instanceof PrivateCredentialPermission)) {
|
|
return false;
|
|
}
|
|
|
|
if (!(entryCs instanceof SubjectCodeSource)) {
|
|
return false;
|
|
}
|
|
|
|
PrivateCredentialPermission pcp = (PrivateCredentialPermission)p;
|
|
SubjectCodeSource scs = (SubjectCodeSource)entryCs;
|
|
|
|
// see if it is a SELF permission
|
|
String[][] pPrincipals = pcp.getPrincipals();
|
|
if (pPrincipals.length <= 0 ||
|
|
!pPrincipals[0][0].equalsIgnoreCase("self") ||
|
|
!pPrincipals[0][1].equalsIgnoreCase("self")) {
|
|
|
|
// regular PrivateCredentialPermission
|
|
return false;
|
|
} else {
|
|
|
|
// granted a SELF permission - create a
|
|
// PrivateCredentialPermission for each
|
|
// of the Policy entry's CodeSource Principals
|
|
|
|
if (scs.getPrincipals() == null) {
|
|
// XXX SubjectCodeSource has no Subject???
|
|
return true;
|
|
}
|
|
|
|
for (PrincipalEntry principal : scs.getPrincipals()) {
|
|
|
|
// if the Policy entry's Principal does not contain a
|
|
// WILDCARD for the Principal name, then a
|
|
// new PrivateCredentialPermission is created
|
|
// for the Principal listed in the Policy entry.
|
|
// if the Policy entry's Principal contains a WILDCARD
|
|
// for the Principal name, then a new
|
|
// PrivateCredentialPermission is created
|
|
// for each Principal associated with the Subject
|
|
// in the current ACC.
|
|
|
|
String[][] principalInfo = getPrincipalInfo(principal, accCs);
|
|
|
|
for (int i = 0; i < principalInfo.length; i++) {
|
|
|
|
// here's the new PrivateCredentialPermission
|
|
|
|
PrivateCredentialPermission newPcp =
|
|
new PrivateCredentialPermission
|
|
(pcp.getCredentialClass() +
|
|
" " +
|
|
principalInfo[i][0] +
|
|
" " +
|
|
"\"" + principalInfo[i][1] + "\"",
|
|
"read");
|
|
|
|
if (debug != null) {
|
|
debug.println("adding SELF permission: " +
|
|
newPcp.toString());
|
|
}
|
|
|
|
perms.add(newPcp);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* return the principal class/name pair in the 2D array.
|
|
* array[x][y]: x corresponds to the array length.
|
|
* if (y == 0), it's the principal class.
|
|
* if (y == 1), it's the principal name.
|
|
*/
|
|
private String[][] getPrincipalInfo(PrincipalEntry principal,
|
|
final CodeSource accCs) {
|
|
|
|
// there are 3 possibilities:
|
|
// 1) the entry's Principal class and name are not wildcarded
|
|
// 2) the entry's Principal name is wildcarded only
|
|
// 3) the entry's Principal class and name are wildcarded
|
|
|
|
if (!principal.getPrincipalClass().equals
|
|
(PrincipalEntry.WILDCARD_CLASS) &&
|
|
!principal.getPrincipalName().equals
|
|
(PrincipalEntry.WILDCARD_NAME)) {
|
|
|
|
// build a PrivateCredentialPermission for the principal
|
|
// from the Policy entry
|
|
String[][] info = new String[1][2];
|
|
info[0][0] = principal.getPrincipalClass();
|
|
info[0][1] = principal.getPrincipalName();
|
|
return info;
|
|
|
|
} else if (!principal.getPrincipalClass().equals
|
|
(PrincipalEntry.WILDCARD_CLASS) &&
|
|
principal.getPrincipalName().equals
|
|
(PrincipalEntry.WILDCARD_NAME)) {
|
|
|
|
// build a PrivateCredentialPermission for all
|
|
// the Subject's principals that are instances of principalClass
|
|
|
|
// the accCs is guaranteed to be a SubjectCodeSource
|
|
// because the earlier CodeSource.implies succeeded
|
|
SubjectCodeSource scs = (SubjectCodeSource)accCs;
|
|
|
|
Set<? extends Principal> principalSet = null;
|
|
try {
|
|
// principal.principalClass should extend Principal
|
|
// If it doesn't, we should stop here with a ClassCastException.
|
|
@SuppressWarnings("unchecked")
|
|
Class<? extends Principal> pClass = (Class<? extends Principal>)
|
|
Class.forName(principal.getPrincipalClass(), false,
|
|
ClassLoader.getSystemClassLoader());
|
|
principalSet = scs.getSubject().getPrincipals(pClass);
|
|
} catch (Exception e) {
|
|
if (debug != null) {
|
|
debug.println("problem finding Principal Class " +
|
|
"when expanding SELF permission: " +
|
|
e.toString());
|
|
}
|
|
}
|
|
|
|
if (principalSet == null) {
|
|
// error
|
|
return new String[0][0];
|
|
}
|
|
|
|
String[][] info = new String[principalSet.size()][2];
|
|
|
|
int i = 0;
|
|
for (Principal p : principalSet) {
|
|
info[i][0] = p.getClass().getName();
|
|
info[i][1] = p.getName();
|
|
i++;
|
|
}
|
|
return info;
|
|
|
|
} else {
|
|
|
|
// build a PrivateCredentialPermission for every
|
|
// one of the current Subject's principals
|
|
|
|
// the accCs is guaranteed to be a SubjectCodeSource
|
|
// because the earlier CodeSource.implies succeeded
|
|
SubjectCodeSource scs = (SubjectCodeSource)accCs;
|
|
Set<Principal> principalSet = scs.getSubject().getPrincipals();
|
|
|
|
String[][] info = new String[principalSet.size()][2];
|
|
|
|
int i = 0;
|
|
for (Principal p : principalSet) {
|
|
info[i][0] = p.getClass().getName();
|
|
info[i][1] = p.getName();
|
|
i++;
|
|
}
|
|
return info;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns the signer certificates from the list of certificates associated
|
|
* with the given code source.
|
|
*
|
|
* The signer certificates are those certificates that were used to verify
|
|
* signed code originating from the codesource location.
|
|
*
|
|
* This method assumes that in the given code source, each signer
|
|
* certificate is followed by its supporting certificate chain
|
|
* (which may be empty), and that the signer certificate and its
|
|
* supporting certificate chain are ordered bottom-to-top (i.e., with the
|
|
* signer certificate first and the (root) certificate authority last).
|
|
*/
|
|
Certificate[] getSignerCertificates(CodeSource cs) {
|
|
Certificate[] certs = null;
|
|
if ((certs = cs.getCertificates()) == null) {
|
|
return null;
|
|
}
|
|
for (int i = 0; i < certs.length; i++) {
|
|
if (!(certs[i] instanceof X509Certificate))
|
|
return cs.getCertificates();
|
|
}
|
|
|
|
// Do we have to do anything?
|
|
int i = 0;
|
|
int count = 0;
|
|
while (i < certs.length) {
|
|
count++;
|
|
while (((i+1) < certs.length)
|
|
&& ((X509Certificate)certs[i]).getIssuerDN().equals(
|
|
((X509Certificate)certs[i+1]).getSubjectDN())) {
|
|
i++;
|
|
}
|
|
i++;
|
|
}
|
|
if (count == certs.length) {
|
|
// Done
|
|
return certs;
|
|
}
|
|
|
|
ArrayList<Certificate> userCertList = new ArrayList<>();
|
|
i = 0;
|
|
while (i < certs.length) {
|
|
userCertList.add(certs[i]);
|
|
while (((i+1) < certs.length)
|
|
&& ((X509Certificate)certs[i]).getIssuerDN().equals(
|
|
((X509Certificate)certs[i+1]).getSubjectDN())) {
|
|
i++;
|
|
}
|
|
i++;
|
|
}
|
|
Certificate[] userCerts = new Certificate[userCertList.size()];
|
|
userCertList.toArray(userCerts);
|
|
return userCerts;
|
|
}
|
|
|
|
private CodeSource canonicalizeCodebase(CodeSource cs,
|
|
boolean extractSignerCerts) {
|
|
CodeSource canonCs = cs;
|
|
if (cs.getLocation() != null &&
|
|
cs.getLocation().getProtocol().equalsIgnoreCase("file")) {
|
|
try {
|
|
String path = cs.getLocation().getFile().replace
|
|
('/',
|
|
File.separatorChar);
|
|
URL csUrl = null;
|
|
if (path.endsWith("*")) {
|
|
// remove trailing '*' because it causes canonicalization
|
|
// to fail on win32
|
|
path = path.substring(0, path.length()-1);
|
|
boolean appendFileSep = false;
|
|
if (path.endsWith(File.separator)) {
|
|
appendFileSep = true;
|
|
}
|
|
if (path.equals("")) {
|
|
path = System.getProperty("user.dir");
|
|
}
|
|
File f = new File(path);
|
|
path = f.getCanonicalPath();
|
|
StringBuilder sb = new StringBuilder(path);
|
|
// reappend '*' to canonicalized filename (note that
|
|
// canonicalization may have removed trailing file
|
|
// separator, so we have to check for that, too)
|
|
if (!path.endsWith(File.separator) &&
|
|
(appendFileSep || f.isDirectory())) {
|
|
sb.append(File.separatorChar);
|
|
}
|
|
sb.append('*');
|
|
path = sb.toString();
|
|
} else {
|
|
path = new File(path).getCanonicalPath();
|
|
}
|
|
csUrl = new File(path).toURL();
|
|
|
|
if (cs instanceof SubjectCodeSource) {
|
|
SubjectCodeSource scs = (SubjectCodeSource)cs;
|
|
if (extractSignerCerts) {
|
|
canonCs = new SubjectCodeSource(scs.getSubject(),
|
|
scs.getPrincipals(),
|
|
csUrl,
|
|
getSignerCertificates(scs));
|
|
} else {
|
|
canonCs = new SubjectCodeSource(scs.getSubject(),
|
|
scs.getPrincipals(),
|
|
csUrl,
|
|
scs.getCertificates());
|
|
}
|
|
} else {
|
|
if (extractSignerCerts) {
|
|
canonCs = new CodeSource(csUrl,
|
|
getSignerCertificates(cs));
|
|
} else {
|
|
canonCs = new CodeSource(csUrl,
|
|
cs.getCertificates());
|
|
}
|
|
}
|
|
} catch (IOException ioe) {
|
|
// leave codesource as it is, unless we have to extract its
|
|
// signer certificates
|
|
if (extractSignerCerts) {
|
|
if (!(cs instanceof SubjectCodeSource)) {
|
|
canonCs = new CodeSource(cs.getLocation(),
|
|
getSignerCertificates(cs));
|
|
} else {
|
|
SubjectCodeSource scs = (SubjectCodeSource)cs;
|
|
canonCs = new SubjectCodeSource(scs.getSubject(),
|
|
scs.getPrincipals(),
|
|
scs.getLocation(),
|
|
getSignerCertificates(scs));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (extractSignerCerts) {
|
|
if (!(cs instanceof SubjectCodeSource)) {
|
|
canonCs = new CodeSource(cs.getLocation(),
|
|
getSignerCertificates(cs));
|
|
} else {
|
|
SubjectCodeSource scs = (SubjectCodeSource)cs;
|
|
canonCs = new SubjectCodeSource(scs.getSubject(),
|
|
scs.getPrincipals(),
|
|
scs.getLocation(),
|
|
getSignerCertificates(scs));
|
|
}
|
|
}
|
|
}
|
|
return canonCs;
|
|
}
|
|
|
|
/**
|
|
* Each entry in the policy configuration file is represented by a
|
|
* PolicyEntry object. <p>
|
|
*
|
|
* A PolicyEntry is a (CodeSource,Permission) pair. The
|
|
* CodeSource contains the (URL, PublicKey) that together identify
|
|
* where the Java bytecodes come from and who (if anyone) signed
|
|
* them. The URL could refer to localhost. The URL could also be
|
|
* null, meaning that this policy entry is given to all comers, as
|
|
* long as they match the signer field. The signer could be null,
|
|
* meaning the code is not signed. <p>
|
|
*
|
|
* The Permission contains the (Type, Name, Action) triplet. <p>
|
|
*
|
|
* For now, the Policy object retrieves the public key from the
|
|
* X.509 certificate on disk that corresponds to the signedBy
|
|
* alias specified in the Policy config file. For reasons of
|
|
* efficiency, the Policy object keeps a hashtable of certs already
|
|
* read in. This could be replaced by a secure internal key
|
|
* store.
|
|
*
|
|
* <p>
|
|
* For example, the entry
|
|
* <pre>
|
|
* permission java.io.File "/tmp", "read,write",
|
|
* signedBy "Duke";
|
|
* </pre>
|
|
* is represented internally
|
|
* <pre>
|
|
*
|
|
* FilePermission f = new FilePermission("/tmp", "read,write");
|
|
* PublicKey p = publickeys.get("Duke");
|
|
* URL u = InetAddress.getLocalHost();
|
|
* CodeBase c = new CodeBase( p, u );
|
|
* pe = new PolicyEntry(f, c);
|
|
* </pre>
|
|
*
|
|
* @author Marianne Mueller
|
|
* @author Roland Schemers
|
|
* @see java.security.CodeSource
|
|
* @see java.security.Policy
|
|
* @see java.security.Permissions
|
|
* @see java.security.ProtectionDomain
|
|
*/
|
|
private static class PolicyEntry {
|
|
|
|
CodeSource codesource;
|
|
Vector<Permission> permissions;
|
|
|
|
/**
|
|
* Given a Permission and a CodeSource, create a policy entry.
|
|
*
|
|
* XXX Decide if/how to add validity fields and "purpose" fields to
|
|
* XXX policy entries
|
|
*
|
|
* @param cs the CodeSource, which encapsulates the URL and the public
|
|
* key attributes from the policy config file. Validity checks
|
|
* are performed on the public key before PolicyEntry is called.
|
|
*
|
|
*/
|
|
PolicyEntry(CodeSource cs) {
|
|
this.codesource = cs;
|
|
this.permissions = new Vector<Permission>();
|
|
}
|
|
|
|
/**
|
|
* add a Permission object to this entry.
|
|
*/
|
|
void add(Permission p) {
|
|
permissions.addElement(p);
|
|
}
|
|
|
|
/**
|
|
* Return the CodeSource for this policy entry
|
|
*/
|
|
CodeSource getCodeSource() {
|
|
return this.codesource;
|
|
}
|
|
|
|
@Override
|
|
public String toString(){
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append(rb.getString("LPARAM"));
|
|
sb.append(getCodeSource());
|
|
sb.append("\n");
|
|
for (int j = 0; j < permissions.size(); j++) {
|
|
Permission p = permissions.elementAt(j);
|
|
sb.append(rb.getString("SPACE"));
|
|
sb.append(rb.getString("SPACE"));
|
|
sb.append(p);
|
|
sb.append(rb.getString("NEWLINE"));
|
|
}
|
|
sb.append(rb.getString("RPARAM"));
|
|
sb.append(rb.getString("NEWLINE"));
|
|
return sb.toString();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("deprecation")
|
|
class PolicyPermissions extends PermissionCollection {
|
|
|
|
private static final long serialVersionUID = -1954188373270545523L;
|
|
|
|
private CodeSource codesource;
|
|
private Permissions perms;
|
|
private AuthPolicyFile policy;
|
|
private boolean notInit; // have we pulled in the policy permissions yet?
|
|
private Vector<Permission> additionalPerms;
|
|
|
|
PolicyPermissions(AuthPolicyFile policy,
|
|
CodeSource codesource)
|
|
{
|
|
this.codesource = codesource;
|
|
this.policy = policy;
|
|
this.perms = null;
|
|
this.notInit = true;
|
|
this.additionalPerms = null;
|
|
}
|
|
|
|
@Override
|
|
public void add(Permission permission) {
|
|
if (isReadOnly())
|
|
throw new SecurityException
|
|
(AuthPolicyFile.rb.getString
|
|
("attempt.to.add.a.Permission.to.a.readonly.PermissionCollection"));
|
|
|
|
if (perms == null) {
|
|
if (additionalPerms == null) {
|
|
additionalPerms = new Vector<Permission>();
|
|
}
|
|
additionalPerms.add(permission);
|
|
} else {
|
|
perms.add(permission);
|
|
}
|
|
}
|
|
|
|
private synchronized void init() {
|
|
if (notInit) {
|
|
if (perms == null) {
|
|
perms = new Permissions();
|
|
}
|
|
if (additionalPerms != null) {
|
|
Enumeration<Permission> e = additionalPerms.elements();
|
|
while (e.hasMoreElements()) {
|
|
perms.add(e.nextElement());
|
|
}
|
|
additionalPerms = null;
|
|
}
|
|
policy.getPermissions(perms, codesource);
|
|
notInit = false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean implies(Permission permission) {
|
|
if (notInit) {
|
|
init();
|
|
}
|
|
return perms.implies(permission);
|
|
}
|
|
|
|
@Override
|
|
public Enumeration<Permission> elements() {
|
|
if (notInit) {
|
|
init();
|
|
}
|
|
return perms.elements();
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
if (notInit) {
|
|
init();
|
|
}
|
|
return perms.toString();
|
|
}
|
|
}
|