/*
* Copyright (c) 1997, 2020, 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.MalformedURLException;
import java.net.URL;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import javax.security.auth.Subject;
import javax.security.auth.x500.X500Principal;
import java.net.SocketPermission;
import java.net.NetPermission;
import java.util.concurrent.ConcurrentHashMap;
import jdk.internal.access.JavaSecurityAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.util.StaticProperty;
import sun.security.util.*;
import sun.net.www.ParseUtil;
import static java.nio.charset.StandardCharsets.UTF_8;
import static jdk.internal.access.JavaSecurityAccess.ProtectionDomainCache;
This class represents a default Policy implementation for the
"JavaPolicy" type.
This object stores the policy for the entire Java runtime,
and is the amalgamation of multiple static policy
configurations that resides in files.
The algorithm for locating the policy file(s) and reading their
information into this Policy
object is:
-
Read in and load the default policy file named
<JAVA_HOME>/lib/security/default.policy. <JAVA_HOME> refers
to the value of the java.home system property, and specifies the directory
where the JRE is installed. This policy file grants permissions to the
modules loaded by the platform class loader. If the default policy file
cannot be loaded, a fatal InternalError is thrown as these permissions
are needed in order for the runtime to operate correctly.
-
Loop through the
java.security.Security
properties,
and policy.url.1, policy.url.2, ...,
policy.url.X". These properties are set
in the Java security properties file, which is located in the file named
<JAVA_HOME>/conf/security/java.security.
Each property value specifies a URL
pointing to a
policy file to be loaded. Read in and load each policy.
If none of these could be loaded, use a builtin static policy
equivalent to the conf/security/java.policy file.
-
The
java.lang.System
property java.security.policy
may also be set to a URL
pointing to another policy file
(which is the case when a user uses the -D switch at runtime).
If this property is defined, and its use is allowed by the
security property file (the Security property,
policy.allowSystemProperty is set to true),
also load that policy.
If the java.security.policy property is defined using
"==" (rather than "="), then load the specified policy file and ignore
all other configured policies. Note, that the default.policy file is
also loaded, as specified in the first step of the algorithm above.
If the specified policy file cannot be loaded, use a builtin static policy
equivalent to the default conf/security/java.policy file.
Each policy file consists of one or more grant entries, each of
which consists of a number of permission entries.
grant signedBy "alias", codeBase "URL",
principal principalClass "principalName",
principal principalClass "principalName",
... {
permission Type "name "action",
signedBy "alias";
permission Type "name "action",
signedBy "alias";
....
};
All non-bold items above must appear as is (although case
doesn't matter and some are optional, as noted below).
principal entries are optional and need not be present.
Italicized items represent variable values.
A grant entry must begin with the word grant
.
The signedBy
,codeBase
and principal
name/value pairs are optional.
If they are not present, then any signer (including unsigned code)
will match, and any codeBase will match.
Note that the principalClass
may be set to the wildcard value, *, which allows it to match
any Principal
class. In addition, the principalName
may also be set to the wildcard value, *, allowing it to match
any Principal
name. When setting the principalName
to the *, do not surround the * with quotes.
A permission entry must begin with the word permission
.
The word Type
in the template above is
a specific permission type, such as java.io.FilePermission
or java.lang.RuntimePermission
.
The "action" is required for
many permission types, such as java.io.FilePermission
(where it specifies what type of file access that is permitted).
It is not required for categories such as
java.lang.RuntimePermission
where it is not necessary - you either have the
permission specified by the "name"
value following the type name or you don't.
The signedBy
name/value pair for a permission entry
is optional. If present, it indicates a signed permission. That is,
the permission class itself must be signed by the given alias in
order for it to be granted. For example,
suppose you have the following grant entry:
grant principal foo.com.Principal "Duke" {
permission Foo "foobar", signedBy "FooSoft";
}
Then this permission of type Foo is granted if the
Foo.class
permission has been signed by the
"FooSoft" alias, or if XXX Foo.class
is a
system class (i.e., is found on the CLASSPATH).
Items that appear in an entry must appear in the specified order
(permission
, Type, "name", and
"action"). An entry is terminated with a semicolon.
Case is unimportant for the identifiers (permission
,
signedBy
, codeBase
, etc.) but is
significant for the Type
or for any string that is passed in as a value.
An example of two entries in a policy configuration file is
// if the code is comes from "foo.com" and is running as "Duke",
// grant it read/write to all files in /tmp.
grant codeBase "foo.com", principal foo.com.Principal "Duke" {
permission java.io.FilePermission "/tmp/*", "read,write";
};
// grant any code running as "Duke" permission to read
// the "java.vendor" Property.
grant principal foo.com.Principal "Duke" {
permission java.util.PropertyPermission "java.vendor";
This Policy implementation supports special handling of any
permission that contains the string, "${{self}}", as part of
its target name. When such a permission is evaluated
(such as during a security check), ${{self}} is replaced
with one or more Principal class/name pairs. The exact
replacement performed depends upon the contents of the
grant clause to which the permission belongs.
If the grant clause does not contain any principal information,
the permission will be ignored (permissions containing
${{self}} in their target names are only valid in the context
of a principal-based grant clause). For example, BarPermission
will always be ignored in the following grant clause:
grant codebase "www.foo.com", signedby "duke" {
permission BarPermission "... ${{self}} ...";
};
If the grant clause contains principal information, ${{self}}
will be replaced with that same principal information.
For example, ${{self}} in BarPermission will be replaced by
javax.security.auth.x500.X500Principal "cn=Duke"
in the following grant clause:
grant principal javax.security.auth.x500.X500Principal "cn=Duke" {
permission BarPermission "... ${{self}} ...";
};
If there is a comma-separated list of principals in the grant
clause, then ${{self}} will be replaced by the same
comma-separated list or principals.
In the case where both the principal class and name are
wildcarded in the grant clause, ${{self}} is replaced
with all the principals associated with the Subject
in the current AccessControlContext
.
For PrivateCredentialPermissions, you can also use "self"
instead of "${{self}}". However the use of "self" is
deprecated in favour of "${{self}}".
See Also: - CodeSource
- Permissions
- ProtectionDomain
/**
* This class represents a default Policy implementation for the
* "JavaPolicy" type.
*
* <p> This object stores the policy for the entire Java runtime,
* and is the amalgamation of multiple static policy
* configurations that resides in files.
* The algorithm for locating the policy file(s) and reading their
* information into this <code>Policy</code> object is:
*
* <ol>
* <li>
* Read in and load the default policy file named
* <JAVA_HOME>/lib/security/default.policy. <JAVA_HOME> refers
* to the value of the java.home system property, and specifies the directory
* where the JRE is installed. This policy file grants permissions to the
* modules loaded by the platform class loader. If the default policy file
* cannot be loaded, a fatal InternalError is thrown as these permissions
* are needed in order for the runtime to operate correctly.
* <li>
* Loop through the <code>java.security.Security</code> properties,
* and <i>policy.url.1</i>, <i>policy.url.2</i>, ...,
* <i>policy.url.X</i>". These properties are set
* in the Java security properties file, which is located in the file named
* <JAVA_HOME>/conf/security/java.security.
* Each property value specifies a <code>URL</code> pointing to a
* policy file to be loaded. Read in and load each policy.
*
* If none of these could be loaded, use a builtin static policy
* equivalent to the conf/security/java.policy file.
*
* <li>
* The <code>java.lang.System</code> property <i>java.security.policy</i>
* may also be set to a <code>URL</code> pointing to another policy file
* (which is the case when a user uses the -D switch at runtime).
* If this property is defined, and its use is allowed by the
* security property file (the Security property,
* <i>policy.allowSystemProperty</i> is set to <i>true</i>),
* also load that policy.
*
* If the <i>java.security.policy</i> property is defined using
* "==" (rather than "="), then load the specified policy file and ignore
* all other configured policies. Note, that the default.policy file is
* also loaded, as specified in the first step of the algorithm above.
* If the specified policy file cannot be loaded, use a builtin static policy
* equivalent to the default conf/security/java.policy file.
* </ol>
*
* Each policy file consists of one or more grant entries, each of
* which consists of a number of permission entries.
*
* <pre>
* grant signedBy "<b>alias</b>", codeBase "<b>URL</b>",
* principal <b>principalClass</b> "<b>principalName</b>",
* principal <b>principalClass</b> "<b>principalName</b>",
* ... {
*
* permission <b>Type</b> "<b>name</b> "<b>action</b>",
* signedBy "<b>alias</b>";
* permission <b>Type</b> "<b>name</b> "<b>action</b>",
* signedBy "<b>alias</b>";
* ....
* };
* </pre>
*
* All non-bold items above must appear as is (although case
* doesn't matter and some are optional, as noted below).
* principal entries are optional and need not be present.
* Italicized items represent variable values.
*
* <p> A grant entry must begin with the word <code>grant</code>.
* The <code>signedBy</code>,<code>codeBase</code> and <code>principal</code>
* name/value pairs are optional.
* If they are not present, then any signer (including unsigned code)
* will match, and any codeBase will match.
* Note that the <i>principalClass</i>
* may be set to the wildcard value, *, which allows it to match
* any <code>Principal</code> class. In addition, the <i>principalName</i>
* may also be set to the wildcard value, *, allowing it to match
* any <code>Principal</code> name. When setting the <i>principalName</i>
* to the *, do not surround the * with quotes.
*
* <p> A permission entry must begin with the word <code>permission</code>.
* The word <code><i>Type</i></code> in the template above is
* a specific permission type, such as <code>java.io.FilePermission</code>
* or <code>java.lang.RuntimePermission</code>.
*
* <p> The "<i>action</i>" is required for
* many permission types, such as <code>java.io.FilePermission</code>
* (where it specifies what type of file access that is permitted).
* It is not required for categories such as
* <code>java.lang.RuntimePermission</code>
* where it is not necessary - you either have the
* permission specified by the <code>"<i>name</i>"</code>
* value following the type name or you don't.
*
* <p> The <code>signedBy</code> name/value pair for a permission entry
* is optional. If present, it indicates a signed permission. That is,
* the permission class itself must be signed by the given alias in
* order for it to be granted. For example,
* suppose you have the following grant entry:
*
* <pre>
* grant principal foo.com.Principal "Duke" {
* permission Foo "foobar", signedBy "FooSoft";
* }
* </pre>
*
* <p> Then this permission of type <i>Foo</i> is granted if the
* <code>Foo.class</code> permission has been signed by the
* "FooSoft" alias, or if XXX <code>Foo.class</code> is a
* system class (i.e., is found on the CLASSPATH).
*
* <p> Items that appear in an entry must appear in the specified order
* (<code>permission</code>, <i>Type</i>, "<i>name</i>", and
* "<i>action</i>"). An entry is terminated with a semicolon.
*
* <p> Case is unimportant for the identifiers (<code>permission</code>,
* <code>signedBy</code>, <code>codeBase</code>, etc.) but is
* significant for the <i>Type</i>
* or for any string that is passed in as a value.
*
* <p> An example of two entries in a policy configuration file is
* <pre>
* // if the code is comes from "foo.com" and is running as "Duke",
* // grant it read/write to all files in /tmp.
*
* grant codeBase "foo.com", principal foo.com.Principal "Duke" {
* permission java.io.FilePermission "/tmp/*", "read,write";
* };
*
* // grant any code running as "Duke" permission to read
* // the "java.vendor" Property.
*
* grant principal foo.com.Principal "Duke" {
* permission java.util.PropertyPermission "java.vendor";
*
*
* </pre>
* This Policy implementation supports special handling of any
* permission that contains the string, "<b>${{self}}</b>", as part of
* its target name. When such a permission is evaluated
* (such as during a security check), <b>${{self}}</b> is replaced
* with one or more Principal class/name pairs. The exact
* replacement performed depends upon the contents of the
* grant clause to which the permission belongs.
* <p>
*
* If the grant clause does not contain any principal information,
* the permission will be ignored (permissions containing
* <b>${{self}}</b> in their target names are only valid in the context
* of a principal-based grant clause). For example, BarPermission
* will always be ignored in the following grant clause:
*
* <pre>
* grant codebase "www.foo.com", signedby "duke" {
* permission BarPermission "... ${{self}} ...";
* };
* </pre>
*
* If the grant clause contains principal information, <b>${{self}}</b>
* will be replaced with that same principal information.
* For example, <b>${{self}}</b> in BarPermission will be replaced by
* <b>javax.security.auth.x500.X500Principal "cn=Duke"</b>
* in the following grant clause:
*
* <pre>
* grant principal javax.security.auth.x500.X500Principal "cn=Duke" {
* permission BarPermission "... ${{self}} ...";
* };
* </pre>
*
* If there is a comma-separated list of principals in the grant
* clause, then <b>${{self}}</b> will be replaced by the same
* comma-separated list or principals.
* In the case where both the principal class and name are
* wildcarded in the grant clause, <b>${{self}}</b> is replaced
* with all the principals associated with the <code>Subject</code>
* in the current <code>AccessControlContext</code>.
*
* <p> For PrivateCredentialPermissions, you can also use "<b>self</b>"
* instead of "<b>${{self}}</b>". However the use of "<b>self</b>" is
* deprecated in favour of "<b>${{self}}</b>".
*
* @see java.security.CodeSource
* @see java.security.Permissions
* @see java.security.ProtectionDomain
*/
public class PolicyFile extends java.security.Policy {
private static final Debug debug = Debug.getInstance("policy");
private static final String SELF = "${{self}}";
private static final String X500PRINCIPAL =
"javax.security.auth.x500.X500Principal";
private static final String POLICY = "java.security.policy";
private static final String POLICY_URL = "policy.url.";
private static final int DEFAULT_CACHE_SIZE = 1;
// contains the policy grant entries, PD cache, and alias mapping
// can be updated if refresh() is called
private volatile PolicyInfo policyInfo;
private boolean expandProperties = true;
private boolean allowSystemProperties = true;
private boolean notUtf8 = false;
private URL url;
// for use with the reflection API
private static final Class<?>[] PARAMS0 = { };
private static final Class<?>[] PARAMS1 = { String.class };
private static final Class<?>[] PARAMS2 = { String.class, String.class };
When a policy file has a syntax error, the exception code may generate
another permission check and this can cause the policy file to be parsed
repeatedly, leading to a StackOverflowError or ClassCircularityError.
To avoid this, this set is populated with policy files that have been
previously parsed and have syntax errors, so that they can be
subsequently ignored.
/**
* When a policy file has a syntax error, the exception code may generate
* another permission check and this can cause the policy file to be parsed
* repeatedly, leading to a StackOverflowError or ClassCircularityError.
* To avoid this, this set is populated with policy files that have been
* previously parsed and have syntax errors, so that they can be
* subsequently ignored.
*/
private static Set<URL> badPolicyURLs =
Collections.newSetFromMap(new ConcurrentHashMap<URL,Boolean>());
Initializes the Policy object and reads the default policy
configuration file(s) into the Policy object.
/**
* Initializes the Policy object and reads the default policy
* configuration file(s) into the Policy object.
*/
public PolicyFile() {
init((URL)null);
}
Initializes the Policy object and reads the default policy
from the specified URL only.
/**
* Initializes the Policy object and reads the default policy
* from the specified URL only.
*/
public PolicyFile(URL url) {
this.url = url;
init(url);
}
Initializes the Policy object and reads the default policy
configuration file(s) into the Policy object.
See the class description for details on the algorithm used to
initialize the Policy object.
/**
* Initializes the Policy object and reads the default policy
* configuration file(s) into the Policy object.
*
* See the class description for details on the algorithm used to
* initialize the Policy object.
*/
private void init(URL url) {
// Properties are set once for each init(); ignore changes
// between diff invocations of initPolicyFile(policy, url, info).
String numCacheStr =
AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public String run() {
expandProperties = "true".equalsIgnoreCase
(Security.getProperty("policy.expandProperties"));
allowSystemProperties = "true".equalsIgnoreCase
(Security.getProperty("policy.allowSystemProperty"));
notUtf8 = "false".equalsIgnoreCase
(System.getProperty("sun.security.policy.utf8"));
return System.getProperty("sun.security.policy.numcaches");
}});
int numCaches;
if (numCacheStr != null) {
try {
numCaches = Integer.parseInt(numCacheStr);
} catch (NumberFormatException e) {
numCaches = DEFAULT_CACHE_SIZE;
}
} else {
numCaches = DEFAULT_CACHE_SIZE;
}
PolicyInfo newInfo = new PolicyInfo(numCaches);
initPolicyFile(newInfo, url);
policyInfo = newInfo;
}
private void initPolicyFile(final PolicyInfo newInfo, final URL url) {
// always load default.policy
AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public Void run() {
initDefaultPolicy(newInfo);
return null;
}
});
if (url != null) {
/**
* If the caller specified a URL via Policy.getInstance,
* we only read from default.policy and that URL.
*/
if (debug != null) {
debug.println("reading " + url);
}
AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public Void run() {
if (init(url, newInfo) == false) {
// use static policy if all else fails
initStaticPolicy(newInfo);
}
return null;
}
});
} else {
/**
* Caller did not specify URL via Policy.getInstance.
* Read from URLs listed in the java.security properties file.
*/
boolean loaded_one = initPolicyFile(POLICY, POLICY_URL, newInfo);
// To maintain strict backward compatibility
// we load the static policy only if POLICY load failed
if (!loaded_one) {
// use static policy if all else fails
initStaticPolicy(newInfo);
}
}
}
private boolean initPolicyFile(final String propname, final String urlname,
final PolicyInfo newInfo) {
boolean loadedPolicy =
AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public Boolean run() {
boolean loaded_policy = false;
if (allowSystemProperties) {
String extra_policy = System.getProperty(propname);
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 = ParseUtil.fileToEncodedURL
(new File(policyFile.getCanonicalPath()));
} else {
policyURL = new URL(extra_policy);
}
if (debug != null) {
debug.println("reading "+policyURL);
}
if (init(policyURL, newInfo)) {
loaded_policy = true;
}
} catch (Exception e) {
// ignore.
if (debug != null) {
debug.println("caught exception: "+e);
}
}
if (overrideAll) {
if (debug != null) {
debug.println("overriding other policies!");
}
return Boolean.valueOf(loaded_policy);
}
}
}
int n = 1;
String policy_uri;
while ((policy_uri = Security.getProperty(urlname+n)) != null) {
try {
URL policy_url = null;
String expanded_uri = PropertyExpander.expand
(policy_uri).replace(File.separatorChar, '/');
if (policy_uri.startsWith("file:${java.home}/") ||
policy_uri.startsWith("file:${user.home}/")) {
// this special case accommodates
// the situation java.home/user.home
// expand to a single slash, resulting in
// a file://foo URI
policy_url = new File
(expanded_uri.substring(5)).toURI().toURL();
} else {
policy_url = new URI(expanded_uri).toURL();
}
if (debug != null) {
debug.println("reading " + policy_url);
}
if (init(policy_url, newInfo)) {
loaded_policy = true;
}
} catch (Exception e) {
if (debug != null) {
debug.println(
"Debug info only. Error reading policy " +e);
e.printStackTrace();
}
// ignore that policy
}
n++;
}
return Boolean.valueOf(loaded_policy);
}
});
return loadedPolicy;
}
private void initDefaultPolicy(PolicyInfo newInfo) {
Path defaultPolicy = Path.of(StaticProperty.javaHome(),
"lib",
"security",
"default.policy");
if (debug != null) {
debug.println("reading " + defaultPolicy);
}
try (BufferedReader br = Files.newBufferedReader(defaultPolicy)) {
PolicyParser pp = new PolicyParser(expandProperties);
pp.read(br);
Enumeration<PolicyParser.GrantEntry> enum_ = pp.grantElements();
while (enum_.hasMoreElements()) {
PolicyParser.GrantEntry ge = enum_.nextElement();
addGrantEntry(ge, null, newInfo);
}
} catch (Exception e) {
throw new InternalError("Failed to load default.policy", e);
}
}
Reads a policy configuration into the Policy object using a
Reader object.
/**
* Reads a policy configuration into the Policy object using a
* Reader object.
*/
private boolean init(URL policy, PolicyInfo newInfo) {
// skip parsing policy file if it has been previously parsed and
// has syntax errors
if (badPolicyURLs.contains(policy)) {
if (debug != null) {
debug.println("skipping bad policy file: " + policy);
}
return false;
}
try (InputStreamReader isr =
getInputStreamReader(PolicyUtil.getInputStream(policy))) {
PolicyParser pp = new PolicyParser(expandProperties);
pp.read(isr);
KeyStore keyStore = null;
try {
keyStore = PolicyUtil.getKeyStore
(policy,
pp.getKeyStoreUrl(),
pp.getKeyStoreType(),
pp.getKeyStoreProvider(),
pp.getStorePassURL(),
debug);
} catch (Exception e) {
// ignore, treat it like we have no keystore
if (debug != null) {
debug.println("Debug info only. Ignoring exception.");
e.printStackTrace();
}
}
Enumeration<PolicyParser.GrantEntry> enum_ = pp.grantElements();
while (enum_.hasMoreElements()) {
PolicyParser.GrantEntry ge = enum_.nextElement();
addGrantEntry(ge, keyStore, newInfo);
}
return true;
} catch (PolicyParser.ParsingException pe) {
// record bad policy file to avoid later reparsing it
badPolicyURLs.add(policy);
Object[] source = {policy, pe.getNonlocalizedMessage()};
System.err.println(LocalizedMessage.getNonlocalized
(POLICY + ".error.parsing.policy.message", source));
if (debug != null) {
pe.printStackTrace();
}
} catch (Exception e) {
if (debug != null) {
debug.println("error parsing "+policy);
debug.println(e.toString());
e.printStackTrace();
}
}
return false;
}
private InputStreamReader getInputStreamReader(InputStream is) {
/*
* Read in policy using UTF-8 by default.
*
* Check non-standard system property to see if the default encoding
* should be used instead.
*/
return (notUtf8)
? new InputStreamReader(is)
: new InputStreamReader(is, UTF_8);
}
private void initStaticPolicy(final PolicyInfo newInfo) {
if (debug != null) {
debug.println("Initializing with static permissions");
}
AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public Void run() {
PolicyEntry pe = new PolicyEntry(new CodeSource(null,
(Certificate[]) null));
pe.add(SecurityConstants.LOCAL_LISTEN_PERMISSION);
pe.add(new PropertyPermission("java.version",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission("java.vendor",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission("java.vendor.url",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission("java.class.version",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission("os.name",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission("os.version",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission("os.arch",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission("file.separator",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission("path.separator",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission("line.separator",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission
("java.specification.version",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission
("java.specification.vendor",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission
("java.specification.name",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission
("java.vm.specification.version",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission
("java.vm.specification.vendor",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission
("java.vm.specification.name",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission("java.vm.version",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission("java.vm.vendor",
SecurityConstants.PROPERTY_READ_ACTION));
pe.add(new PropertyPermission("java.vm.name",
SecurityConstants.PROPERTY_READ_ACTION));
// No need to sync because noone has access to newInfo yet
newInfo.policyEntries.add(pe);
return null;
}
});
}
Given a GrantEntry, create a codeSource.
Returns: null if signedBy alias is not recognized
/**
* Given a GrantEntry, create a codeSource.
*
* @return null if signedBy alias is not recognized
*/
private CodeSource getCodeSource(PolicyParser.GrantEntry ge, KeyStore keyStore,
PolicyInfo newInfo) throws java.net.MalformedURLException
{
Certificate[] certs = null;
if (ge.signedBy != null) {
certs = getCertificates(keyStore, ge.signedBy, newInfo);
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 entry");
}
return null;
}
}
URL location;
if (ge.codeBase != null)
location = new URL(ge.codeBase);
else
location = null;
return (canonicalizeCodebase(new CodeSource(location, certs),false));
}
Add one policy entry to the list.
/**
* Add one policy entry to the list.
*/
private void addGrantEntry(PolicyParser.GrantEntry ge,
KeyStore keyStore, PolicyInfo newInfo) {
if (debug != null) {
debug.println("Adding policy entry: ");
debug.println(" signedBy " + ge.signedBy);
debug.println(" codeBase " + ge.codeBase);
if (ge.principals != null) {
for (PolicyParser.PrincipalEntry pppe : ge.principals) {
debug.println(" " + pppe.toString());
}
}
}
try {
CodeSource codesource = getCodeSource(ge, keyStore, newInfo);
// skip if signedBy alias was unknown...
if (codesource == null) return;
// perform keystore alias principal replacement.
// for example, if alias resolves to X509 certificate,
// replace principal with: <X500Principal class> <SubjectDN>
// -- skip if alias is unknown
if (replacePrincipals(ge.principals, keyStore) == false)
return;
PolicyEntry entry = new PolicyEntry(codesource, ge.principals);
Enumeration<PolicyParser.PermissionEntry> enum_ =
ge.permissionElements();
while (enum_.hasMoreElements()) {
PolicyParser.PermissionEntry pe = enum_.nextElement();
try {
// perform ${{ ... }} expansions within permission name
expandPermissionName(pe, keyStore);
// XXX special case PrivateCredentialPermission-SELF
Permission perm;
if (pe.permission.equals
("javax.security.auth.PrivateCredentialPermission") &&
pe.name.endsWith(" self")) {
pe.name = pe.name.substring(0, pe.name.indexOf("self"))
+ SELF;
}
// check for self
if (pe.name != null && pe.name.indexOf(SELF) != -1) {
// Create a "SelfPermission" , it could be an
// an unresolved permission which will be resolved
// when implies is called
// Add it to entry
Certificate[] certs;
if (pe.signedBy != null) {
certs = getCertificates(keyStore,
pe.signedBy,
newInfo);
} else {
certs = null;
}
perm = new SelfPermission(pe.permission,
pe.name,
pe.action,
certs);
} 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,
newInfo);
} else {
certs = null;
}
// only add if we had no signer or we had
// 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) {
Object[] source = {pe.permission,
ite.getTargetException().toString()};
System.err.println(
LocalizedMessage.getNonlocalized(
POLICY + ".error.adding.Permission.perm.message",
source));
} catch (Exception e) {
Object[] source = {pe.permission,
e.toString()};
System.err.println(
LocalizedMessage.getNonlocalized(
POLICY + ".error.adding.Permission.perm.message",
source));
}
}
// No need to sync because noone has access to newInfo yet
newInfo.policyEntries.add(entry);
} catch (Exception e) {
Object[] source = {e.toString()};
System.err.println(
LocalizedMessage.getNonlocalized(
POLICY + ".error.adding.Entry.message",
source));
}
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 Class.forName
method, and using
the reflection API to invoke the (String name, String actions)
constructor on the
object.
Params: - type – the type of Permission being created.
- name – the name of the Permission being created.
- actions – the actions of the Permission being created.
Throws: - ClassNotFoundException – if the particular Permission
class could not be found.
- IllegalAccessException – if the class or initializer is
not accessible.
- InstantiationException – if getInstance tries to
instantiate an abstract class or an interface, or if the
instantiation fails for some other reason.
- NoSuchMethodException – if the (String, String) constructor
is not found.
- InvocationTargetException – if the underlying Permission
constructor throws an exception.
/**
* 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
{
Class<?> pc = Class.forName(type, false, null);
Permission answer = getKnownPermission(pc, name, actions);
if (answer != null) {
return answer;
}
if (!Permission.class.isAssignableFrom(pc)) {
// not the right subtype
throw new ClassCastException(type + " is not a Permission");
}
if (name == null && actions == null) {
try {
Constructor<?> c = pc.getConstructor(PARAMS0);
return (Permission) c.newInstance(new Object[] {});
} catch (NoSuchMethodException ne) {
try {
Constructor<?> c = pc.getConstructor(PARAMS1);
return (Permission) c.newInstance(
new Object[] { name});
} catch (NoSuchMethodException ne1 ) {
Constructor<?> c = pc.getConstructor(PARAMS2);
return (Permission) c.newInstance(
new Object[] { name, actions });
}
}
} else {
if (name != null && actions == null) {
try {
Constructor<?> c = pc.getConstructor(PARAMS1);
return (Permission) c.newInstance(new Object[] { name});
} catch (NoSuchMethodException ne) {
Constructor<?> c = pc.getConstructor(PARAMS2);
return (Permission) c.newInstance(
new Object[] { name, actions });
}
} else {
Constructor<?> c = pc.getConstructor(PARAMS2);
return (Permission) c.newInstance(
new Object[] { name, actions });
}
}
}
Creates one of the well-known permissions in the java.base module
directly instead of via reflection. Keep list short to not penalize
permissions from other modules.
/**
* Creates one of the well-known permissions in the java.base module
* directly instead of via reflection. Keep list short to not penalize
* permissions from other modules.
*/
private static Permission getKnownPermission(Class<?> claz, String name,
String actions) {
if (claz.equals(FilePermission.class)) {
return new FilePermission(name, actions);
} else if (claz.equals(SocketPermission.class)) {
return new SocketPermission(name, actions);
} else if (claz.equals(RuntimePermission.class)) {
return new RuntimePermission(name, actions);
} else if (claz.equals(PropertyPermission.class)) {
return new PropertyPermission(name, actions);
} else if (claz.equals(NetPermission.class)) {
return new NetPermission(name, actions);
} else if (claz.equals(AllPermission.class)) {
return SecurityConstants.ALL_PERMISSION;
} else if (claz.equals(SecurityPermission.class)) {
return new SecurityPermission(name, actions);
} else {
return null;
}
}
Creates one of the well-known principals in the java.base module
directly instead of via reflection. Keep list short to not penalize
principals from other modules.
/**
* Creates one of the well-known principals in the java.base module
* directly instead of via reflection. Keep list short to not penalize
* principals from other modules.
*/
private static Principal getKnownPrincipal(Class<?> claz, String name) {
if (claz.equals(X500Principal.class)) {
return new X500Principal(name);
} else {
return null;
}
}
Fetch all certs associated with this alias.
/**
* Fetch all certs associated with this alias.
*/
private Certificate[] getCertificates
(KeyStore keyStore, String aliases, PolicyInfo newInfo) {
List<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
synchronized (newInfo.aliasMapping) {
cert = (Certificate)newInfo.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) {
newInfo.aliasMapping.put(alias, cert);
newInfo.aliasMapping.put(cert, alias);
}
}
}
if (cert != null) {
if (vcerts == null)
vcerts = new ArrayList<>();
vcerts.add(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.toArray(certs);
return certs;
} else {
return null;
}
}
Refreshes the policy object by re-reading all the policy files.
/**
* Refreshes the policy object by re-reading all the policy files.
*/
@Override public void refresh() {
init(url);
}
Evaluates the global policy for the permissions granted to
the ProtectionDomain and tests whether the permission is
granted.
Params: - pd – the ProtectionDomain to test
- p – the Permission object to be tested for implication.
See Also: Returns: true if "permission" is a proper subset of a permission
granted to this ProtectionDomain.
/**
* Evaluates the global policy for the permissions granted to
* the ProtectionDomain and tests whether the permission is
* granted.
*
* @param pd the ProtectionDomain to test
* @param p the Permission object to be tested for implication.
*
* @return true if "permission" is a proper subset of a permission
* granted to this ProtectionDomain.
*
* @see java.security.ProtectionDomain
*/
@Override
public boolean implies(ProtectionDomain pd, Permission p) {
ProtectionDomainCache pdMap = policyInfo.getPdMapping();
PermissionCollection pc = pdMap.get(pd);
if (pc != null) {
return pc.implies(p);
}
pc = getPermissions(pd);
if (pc == null) {
return false;
}
// cache mapping of protection domain to its PermissionCollection
pdMap.put(pd, pc);
return pc.implies(p);
}
Examines this Policy
and returns the permissions granted
to the specified ProtectionDomain
. This includes
the permissions currently associated with the domain as well
as the policy permissions granted to the domain's
CodeSource, ClassLoader, and Principals.
Note that this Policy
implementation has
special handling for PrivateCredentialPermissions.
When this method encounters a PrivateCredentialPermission
which specifies "self" as the Principal
class and name,
it does not add that Permission
to the returned
PermissionCollection
. Instead, it builds
a new PrivateCredentialPermission
for each Principal
associated with the provided
Subject
. Each new PrivateCredentialPermission
contains the same Credential class as specified in the
originally granted permission, as well as the Class and name
for the respective Principal
.
Params: - domain – the Permissions granted to this
ProtectionDomain
are returned.
Returns: the Permissions granted to the provided
ProtectionDomain
.
/**
* Examines this <code>Policy</code> and returns the permissions granted
* to the specified <code>ProtectionDomain</code>. This includes
* the permissions currently associated with the domain as well
* as the policy permissions granted to the domain's
* CodeSource, ClassLoader, and Principals.
*
* <p> Note that this <code>Policy</code> implementation has
* special handling for PrivateCredentialPermissions.
* When this method encounters a <code>PrivateCredentialPermission</code>
* which specifies "self" as the <code>Principal</code> class and name,
* it does not add that <code>Permission</code> to the returned
* <code>PermissionCollection</code>. Instead, it builds
* a new <code>PrivateCredentialPermission</code>
* for each <code>Principal</code> associated with the provided
* <code>Subject</code>. Each new <code>PrivateCredentialPermission</code>
* contains the same Credential class as specified in the
* originally granted permission, as well as the Class and name
* for the respective <code>Principal</code>.
*
* @param domain the Permissions granted to this
* <code>ProtectionDomain</code> are returned.
*
* @return the Permissions granted to the provided
* <code>ProtectionDomain</code>.
*/
@Override
public PermissionCollection getPermissions(ProtectionDomain domain) {
Permissions perms = new Permissions();
if (domain == null)
return perms;
// first get policy perms
getPermissions(perms, domain);
// add static perms
// - adding static perms after policy perms is necessary
// to avoid a regression for 4301064
PermissionCollection pc = domain.getPermissions();
if (pc != null) {
synchronized (pc) {
Enumeration<Permission> e = pc.elements();
while (e.hasMoreElements()) {
perms.add(FilePermCompat.newPermPlusAltPath(e.nextElement()));
}
}
}
return perms;
}
Examines this Policy and creates a PermissionCollection object with
the set of permissions for the specified CodeSource.
Params: - 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.
Returns: the set of permissions according to the policy.
/**
* Examines this Policy and creates a PermissionCollection object with
* the set of permissions for the specified CodeSource.
*
* @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.
*/
@Override
public PermissionCollection getPermissions(CodeSource codesource) {
return getPermissions(new Permissions(), codesource);
}
Examines the global policy and returns the provided Permissions
object with additional permissions granted to the specified
ProtectionDomain.
Params: - perms – the Permissions to populate
- pd – the ProtectionDomain associated with the caller.
Returns: the set of Permissions according to the policy.
/**
* Examines the global policy and returns the provided Permissions
* object with additional permissions granted to the specified
* ProtectionDomain.
*
* @param perms the Permissions to populate
* @param pd the ProtectionDomain associated with the caller.
*
* @return the set of Permissions according to the policy.
*/
private PermissionCollection getPermissions(Permissions perms,
ProtectionDomain pd ) {
if (debug != null) {
debug.println("getPermissions:\n\t" + printPD(pd));
}
final CodeSource cs = pd.getCodeSource();
if (cs == null)
return perms;
CodeSource canonCodeSource = AccessController.doPrivileged(
new java.security.PrivilegedAction<>(){
@Override
public CodeSource run() {
return canonicalizeCodebase(cs, true);
}
});
return getPermissions(perms, canonCodeSource, pd.getPrincipals());
}
Examines the global policy and returns the provided Permissions
object with additional permissions granted to the specified
CodeSource.
Params: - perms – the permissions to populate
- cs – 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.
Returns: the set of permissions according to the policy.
/**
* Examines the global policy and returns the provided Permissions
* object with additional permissions granted to the specified
* CodeSource.
*
* @param perms the permissions to populate
* @param cs 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.
*/
private PermissionCollection getPermissions(Permissions perms,
final CodeSource cs) {
if (cs == null)
return perms;
CodeSource canonCodeSource = AccessController.doPrivileged(
new PrivilegedAction<>(){
@Override
public CodeSource run() {
return canonicalizeCodebase(cs, true);
}
});
return getPermissions(perms, canonCodeSource, null);
}
private Permissions getPermissions(Permissions perms,
final CodeSource cs,
Principal[] principals) {
for (PolicyEntry entry : policyInfo.policyEntries) {
addPermissions(perms, cs, principals, entry);
}
return perms;
}
private void addPermissions(Permissions perms,
final CodeSource cs,
Principal[] principals,
final PolicyEntry entry) {
if (debug != null) {
debug.println("evaluate codesources:\n" +
"\tPolicy CodeSource: " + entry.getCodeSource() + "\n" +
"\tActive CodeSource: " + cs);
}
// check to see if the CodeSource implies
Boolean imp = AccessController.doPrivileged
(new PrivilegedAction<>() {
@Override
public Boolean run() {
return entry.getCodeSource().implies(cs);
}
});
if (!imp.booleanValue()) {
if (debug != null) {
debug.println("evaluation (codesource) failed");
}
// CodeSource does not imply - return and try next policy entry
return;
}
// check to see if the Principals imply
List<PolicyParser.PrincipalEntry> entryPs = entry.getPrincipals();
if (debug != null) {
List<PolicyParser.PrincipalEntry> accPs = new ArrayList<>();
if (principals != null) {
for (int i = 0; i < principals.length; i++) {
accPs.add(new PolicyParser.PrincipalEntry
(principals[i].getClass().getName(),
principals[i].getName()));
}
}
debug.println("evaluate principals:\n" +
"\tPolicy Principals: " + entryPs + "\n" +
"\tActive Principals: " + accPs);
}
if (entryPs == null || entryPs.isEmpty()) {
// policy entry has no principals -
// add perms regardless of principals in current ACC
addPerms(perms, principals, entry);
if (debug != null) {
debug.println("evaluation (codesource/principals) passed");
}
return;
} else if (principals == null || principals.length == 0) {
// current thread has no principals but this policy entry
// has principals - perms are not added
if (debug != null) {
debug.println("evaluation (principals) failed");
}
return;
}
// current thread has principals and this policy entry
// has principals. see if policy entry principals match
// principals in current ACC
for (PolicyParser.PrincipalEntry pppe : entryPs) {
// Check for wildcards
if (pppe.isWildcardClass()) {
// a wildcard class matches all principals in current ACC
continue;
}
if (pppe.isWildcardName()) {
// a wildcard name matches any principal with the same class
if (wildcardPrincipalNameImplies(pppe.principalClass,
principals)) {
continue;
}
if (debug != null) {
debug.println("evaluation (principal name wildcard) failed");
}
// policy entry principal not in current ACC -
// immediately return and go to next policy entry
return;
}
Set<Principal> pSet = new HashSet<>(Arrays.asList(principals));
Subject subject = new Subject(true, pSet,
Collections.EMPTY_SET,
Collections.EMPTY_SET);
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class<?> pClass = Class.forName(pppe.principalClass, false, cl);
Principal p = getKnownPrincipal(pClass, pppe.principalName);
if (p == null) {
if (!Principal.class.isAssignableFrom(pClass)) {
// not the right subtype
throw new ClassCastException(pppe.principalClass +
" is not a Principal");
}
Constructor<?> c = pClass.getConstructor(PARAMS1);
p = (Principal)c.newInstance(new Object[] {
pppe.principalName });
}
if (debug != null) {
debug.println("found Principal " + p.getClass().getName());
}
// check if the Principal implies the current
// thread's principals
if (!p.implies(subject)) {
if (debug != null) {
debug.println("evaluation (principal implies) failed");
}
// policy principal does not imply the current Subject -
// immediately return and go to next policy entry
return;
}
} catch (Exception e) {
// fall back to default principal comparison.
// see if policy entry principal is in current ACC
if (debug != null) {
e.printStackTrace();
}
if (!pppe.implies(subject)) {
if (debug != null) {
debug.println("evaluation (default principal implies) failed");
}
// policy entry principal not in current ACC -
// immediately return and go to next policy entry
return;
}
}
// either the principal information matched,
// or the Principal.implies succeeded.
// continue loop and test the next policy principal
}
// all policy entry principals were found in the current ACC -
// grant the policy permissions
if (debug != null) {
debug.println("evaluation (codesource/principals) passed");
}
addPerms(perms, principals, entry);
}
Returns true if the array of principals contains at least one
principal of the specified class.
/**
* Returns true if the array of principals contains at least one
* principal of the specified class.
*/
private static boolean wildcardPrincipalNameImplies(String principalClass,
Principal[] principals)
{
for (Principal p : principals) {
if (principalClass.equals(p.getClass().getName())) {
return true;
}
}
return false;
}
private void addPerms(Permissions perms,
Principal[] accPs,
PolicyEntry entry) {
for (int i = 0; i < entry.permissions.size(); i++) {
Permission p = entry.permissions.get(i);
if (debug != null) {
debug.println(" granting " + p);
}
if (p instanceof SelfPermission) {
// handle "SELF" permissions
expandSelf((SelfPermission)p,
entry.getPrincipals(),
accPs,
perms);
} else {
perms.add(FilePermCompat.newPermPlusAltPath(p));
}
}
}
Params: - sp – the SelfPermission that needs to be expanded.
- entryPs – list of principals for the Policy entry.
- pdp – Principal array from the current ProtectionDomain.
- perms – the PermissionCollection where the individual
Permissions will be added after expansion.
/**
* @param sp the SelfPermission that needs to be expanded.
*
* @param entryPs list of principals for the Policy entry.
*
* @param pdp Principal array from the current ProtectionDomain.
*
* @param perms the PermissionCollection where the individual
* Permissions will be added after expansion.
*/
private void expandSelf(SelfPermission sp,
List<PolicyParser.PrincipalEntry> entryPs,
Principal[] pdp,
Permissions perms) {
if (entryPs == null || entryPs.isEmpty()) {
// No principals in the grant to substitute
if (debug != null) {
debug.println("Ignoring permission "
+ sp.getSelfType()
+ " with target name ("
+ sp.getSelfName() + "). "
+ "No Principal(s) specified "
+ "in the grant clause. "
+ "SELF-based target names are "
+ "only valid in the context "
+ "of a Principal-based grant entry."
);
}
return;
}
int startIndex = 0;
int v;
StringBuilder sb = new StringBuilder();
while ((v = sp.getSelfName().indexOf(SELF, startIndex)) != -1) {
// add non-SELF string
sb.append(sp.getSelfName().substring(startIndex, v));
// expand SELF
Iterator<PolicyParser.PrincipalEntry> pli = entryPs.iterator();
while (pli.hasNext()) {
PolicyParser.PrincipalEntry pppe = pli.next();
String[][] principalInfo = getPrincipalInfo(pppe,pdp);
for (int i = 0; i < principalInfo.length; i++) {
if (i != 0) {
sb.append(", ");
}
sb.append(principalInfo[i][0] + " " +
"\"" + principalInfo[i][1] + "\"");
}
if (pli.hasNext()) {
sb.append(", ");
}
}
startIndex = v + SELF.length();
}
// add remaining string (might be the entire string)
sb.append(sp.getSelfName().substring(startIndex));
if (debug != null) {
debug.println(" expanded:\n\t" + sp.getSelfName()
+ "\n into:\n\t" + sb.toString());
}
try {
// first try to instantiate the permission
perms.add(FilePermCompat.newPermPlusAltPath(getInstance(sp.getSelfType(),
sb.toString(),
sp.getSelfActions())));
} catch (ClassNotFoundException cnfe) {
// ok, the permission is not in the bootclasspath.
// before we add an UnresolvedPermission, check to see
// whether this perm already belongs to the collection.
// if so, use that perm's ClassLoader to create a new
// one.
Class<?> pc = null;
synchronized (perms) {
Enumeration<Permission> e = perms.elements();
while (e.hasMoreElements()) {
Permission pElement = e.nextElement();
if (pElement.getClass().getName().equals(sp.getSelfType())) {
pc = pElement.getClass();
break;
}
}
}
if (pc == null) {
// create an UnresolvedPermission
perms.add(new UnresolvedPermission(sp.getSelfType(),
sb.toString(),
sp.getSelfActions(),
sp.getCerts()));
} else {
try {
// we found an instantiated permission.
// use its class loader to instantiate a new permission.
Constructor<?> c;
// name parameter can not be null
if (sp.getSelfActions() == null) {
try {
c = pc.getConstructor(PARAMS1);
perms.add((Permission)c.newInstance
(new Object[] {sb.toString()}));
} catch (NoSuchMethodException ne) {
c = pc.getConstructor(PARAMS2);
perms.add((Permission)c.newInstance
(new Object[] {sb.toString(),
sp.getSelfActions() }));
}
} else {
c = pc.getConstructor(PARAMS2);
perms.add((Permission)c.newInstance
(new Object[] {sb.toString(),
sp.getSelfActions()}));
}
} catch (Exception nme) {
if (debug != null) {
debug.println("self entry expansion " +
" instantiation failed: "
+ nme.toString());
}
}
}
} catch (Exception e) {
if (debug != null) {
debug.println(e.toString());
}
}
}
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.
/**
* 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
(PolicyParser.PrincipalEntry pe, Principal[] pdp) {
// 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 (!pe.isWildcardClass() && !pe.isWildcardName()) {
// build an info array for the principal
// from the Policy entry
String[][] info = new String[1][2];
info[0][0] = pe.principalClass;
info[0][1] = pe.principalName;
return info;
} else if (!pe.isWildcardClass() && pe.isWildcardName()) {
// build an info array for every principal
// in the current domain which has a principal class
// that is equal to policy entry principal class name
List<Principal> plist = new ArrayList<>();
for (int i = 0; i < pdp.length; i++) {
if (pe.principalClass.equals(pdp[i].getClass().getName()))
plist.add(pdp[i]);
}
String[][] info = new String[plist.size()][2];
int i = 0;
for (Principal p : plist) {
info[i][0] = p.getClass().getName();
info[i][1] = p.getName();
i++;
}
return info;
} else {
// build an info array for every
// one of the current Domain's principals
String[][] info = new String[pdp.length][2];
for (int i = 0; i < pdp.length; i++) {
info[i][0] = pdp[i].getClass().getName();
info[i][1] = pdp[i].getName();
}
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).
*/
protected 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]).getIssuerX500Principal().equals(
((X509Certificate)certs[i+1]).getSubjectX500Principal())) {
i++;
}
i++;
}
if (count == certs.length)
// Done
return certs;
List<Certificate> userCertList = new ArrayList<>();
i = 0;
while (i < certs.length) {
userCertList.add(certs[i]);
while (((i+1) < certs.length)
&& ((X509Certificate)certs[i]).getIssuerX500Principal().equals(
((X509Certificate)certs[i+1]).getSubjectX500Principal())) {
i++;
}
i++;
}
Certificate[] userCerts = new Certificate[userCertList.size()];
userCertList.toArray(userCerts);
return userCerts;
}
private CodeSource canonicalizeCodebase(CodeSource cs,
boolean extractSignerCerts) {
String path = null;
CodeSource canonCs = cs;
URL u = cs.getLocation();
if (u != null) {
if (u.getProtocol().equals("jar")) {
// unwrap url embedded inside jar url
String spec = u.getFile();
int separator = spec.indexOf("!/");
if (separator != -1) {
try {
u = new URL(spec.substring(0, separator));
} catch (MalformedURLException e) {
// Fail silently. In this case, url stays what
// it was above
}
}
}
if (u.getProtocol().equals("file")) {
boolean isLocalFile = false;
String host = u.getHost();
isLocalFile = (host == null || host.isEmpty() ||
host.equals("~") || host.equalsIgnoreCase("localhost"));
if (isLocalFile) {
path = u.getFile().replace('/', File.separatorChar);
path = ParseUtil.decode(path);
}
}
}
if (path != null) {
try {
URL csUrl = null;
path = canonPath(path);
csUrl = ParseUtil.fileToEncodedURL(new File(path));
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) {
canonCs = new CodeSource(cs.getLocation(),
getSignerCertificates(cs));
}
}
} else {
if (extractSignerCerts) {
canonCs = new CodeSource(cs.getLocation(),
getSignerCertificates(cs));
}
}
return canonCs;
}
// Wrapper to return a canonical path that avoids calling getCanonicalPath()
// with paths that are intended to match all entries in the directory
private static String canonPath(String path) throws IOException {
if (path.endsWith("*")) {
path = path.substring(0, path.length()-1) + "-";
path = new File(path).getCanonicalPath();
return path.substring(0, path.length()-1) + "*";
} else {
return new File(path).getCanonicalPath();
}
}
private String printPD(ProtectionDomain pd) {
Principal[] principals = pd.getPrincipals();
String pals = "<no principals>";
if (principals != null && principals.length > 0) {
StringBuilder palBuf = new StringBuilder("(principals ");
for (int i = 0; i < principals.length; i++) {
palBuf.append(principals[i].getClass().getName() +
" \"" + principals[i].getName() +
"\"");
if (i < principals.length-1)
palBuf.append(", ");
else
palBuf.append(")");
}
pals = palBuf.toString();
}
return "PD CodeSource: "
+ pd.getCodeSource()
+"\n\t" + "PD ClassLoader: "
+ pd.getClassLoader()
+"\n\t" + "PD Principals: "
+ pals;
}
return true if no replacement was performed,
or if replacement succeeded.
/**
* return true if no replacement was performed,
* or if replacement succeeded.
*/
private boolean replacePrincipals(
List<PolicyParser.PrincipalEntry> principals, KeyStore keystore) {
if (principals == null || principals.isEmpty() || keystore == null)
return true;
for (PolicyParser.PrincipalEntry pppe : principals) {
if (pppe.isReplaceName()) {
// perform replacement
// (only X509 replacement is possible now)
String name;
if ((name = getDN(pppe.principalName, keystore)) == null) {
return false;
}
if (debug != null) {
debug.println(" Replacing \"" +
pppe.principalName +
"\" with " +
X500PRINCIPAL + "/\"" +
name +
"\"");
}
pppe.principalClass = X500PRINCIPAL;
pppe.principalName = name;
}
}
// return true if no replacement was performed,
// or if replacement succeeded
return true;
}
private void expandPermissionName(PolicyParser.PermissionEntry pe,
KeyStore keystore) throws Exception {
// short cut the common case
if (pe.name == null || pe.name.indexOf("${{", 0) == -1) {
return;
}
int startIndex = 0;
int b, e;
StringBuilder sb = new StringBuilder();
while ((b = pe.name.indexOf("${{", startIndex)) != -1) {
e = pe.name.indexOf("}}", b);
if (e < 1) {
break;
}
sb.append(pe.name.substring(startIndex, b));
// get the value in ${{...}}
String value = pe.name.substring(b+3, e);
// parse up to the first ':'
int colonIndex;
String prefix = value;
String suffix;
if ((colonIndex = value.indexOf(':')) != -1) {
prefix = value.substring(0, colonIndex);
}
// handle different prefix possibilities
if (prefix.equalsIgnoreCase("self")) {
// do nothing - handled later
sb.append(pe.name.substring(b, e+2));
startIndex = e+2;
continue;
} else if (prefix.equalsIgnoreCase("alias")) {
// get the suffix and perform keystore alias replacement
if (colonIndex == -1) {
Object[] source = {pe.name};
throw new Exception(
LocalizedMessage.getNonlocalized(
"alias.name.not.provided.pe.name.",
source));
}
suffix = value.substring(colonIndex+1);
if ((suffix = getDN(suffix, keystore)) == null) {
Object[] source = {value.substring(colonIndex+1)};
throw new Exception(
LocalizedMessage.getNonlocalized(
"unable.to.perform.substitution.on.alias.suffix",
source));
}
sb.append(X500PRINCIPAL + " \"" + suffix + "\"");
startIndex = e+2;
} else {
Object[] source = {prefix};
throw new Exception(
LocalizedMessage.getNonlocalized(
"substitution.value.prefix.unsupported",
source));
}
}
// copy the rest of the value
sb.append(pe.name.substring(startIndex));
// replace the name with expanded value
if (debug != null) {
debug.println(" Permission name expanded from:\n\t" +
pe.name + "\nto\n\t" + sb.toString());
}
pe.name = sb.toString();
}
private String getDN(String alias, KeyStore keystore) {
Certificate cert = null;
try {
cert = keystore.getCertificate(alias);
} catch (Exception e) {
if (debug != null) {
debug.println(" Error retrieving certificate for '" +
alias +
"': " +
e.toString());
}
return null;
}
if (cert == null || !(cert instanceof X509Certificate)) {
if (debug != null) {
debug.println(" -- No certificate for '" +
alias +
"' - ignoring entry");
}
return null;
} else {
X509Certificate x509Cert = (X509Certificate)cert;
// 4702543: X500 names with an EmailAddress
// were encoded incorrectly. create new
// X500Principal name with correct encoding
X500Principal p = new X500Principal
(x509Cert.getSubjectX500Principal().toString());
return p.getName();
}
}
Each entry in the policy configuration file is represented by a
PolicyEntry object.
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.
The Permission contains the (Type, Name, Action) triplet.
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.
For example, the entry
permission java.io.File "/tmp", "read,write",
signedBy "Duke";
is represented internally
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);
Author: Marianne Mueller, Roland Schemers See Also:
/**
* 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 {
private final CodeSource codesource;
final List<Permission> permissions;
private final List<PolicyParser.PrincipalEntry> principals;
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
Params: - 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.
/**
* 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, List<PolicyParser.PrincipalEntry> principals)
{
this.codesource = cs;
this.permissions = new ArrayList<Permission>();
this.principals = principals; // can be null
}
PolicyEntry(CodeSource cs)
{
this(cs, null);
}
List<PolicyParser.PrincipalEntry> getPrincipals() {
return principals; // can be null
}
add a Permission object to this entry.
No need to sync add op because perms are added to entry only
while entry is being initialized
/**
* add a Permission object to this entry.
* No need to sync add op because perms are added to entry only
* while entry is being initialized
*/
void add(Permission p) {
permissions.add(p);
}
Return the CodeSource for this policy entry
/**
* Return the CodeSource for this policy entry
*/
CodeSource getCodeSource() {
return codesource;
}
@Override public String toString(){
StringBuilder sb = new StringBuilder();
sb.append(ResourcesMgr.getString("LPARAM"));
sb.append(getCodeSource());
sb.append("\n");
for (int j = 0; j < permissions.size(); j++) {
Permission p = permissions.get(j);
sb.append(ResourcesMgr.getString("SPACE"));
sb.append(ResourcesMgr.getString("SPACE"));
sb.append(p);
sb.append(ResourcesMgr.getString("NEWLINE"));
}
sb.append(ResourcesMgr.getString("RPARAM"));
sb.append(ResourcesMgr.getString("NEWLINE"));
return sb.toString();
}
}
private static class SelfPermission extends Permission {
@java.io.Serial
private static final long serialVersionUID = -8315562579967246806L;
The class name of the Permission class that will be
created when this self permission is expanded .
@serial
/**
* The class name of the Permission class that will be
* created when this self permission is expanded .
*
* @serial
*/
private String type;
The permission name.
@serial
/**
* The permission name.
*
* @serial
*/
private String name;
The actions of the permission.
@serial
/**
* The actions of the permission.
*
* @serial
*/
private String actions;
The certs of the permission.
@serial
/**
* The certs of the permission.
*
* @serial
*/
private Certificate[] certs;
Creates a new SelfPermission containing the permission
information needed later to expand the self
Params: - type – the class name of the Permission class that will be
created when this permission is expanded and if necessary resolved.
- name – the name of the permission.
- actions – the actions of the permission.
- certs – the certificates the permission's class was signed with.
This is a list of certificate chains, where each chain is composed of
a signer certificate and optionally its supporting certificate chain.
Each chain is ordered bottom-to-top (i.e., with the signer
certificate first and the (root) certificate authority last).
/**
* Creates a new SelfPermission containing the permission
* information needed later to expand the self
* @param type the class name of the Permission class that will be
* created when this permission is expanded and if necessary resolved.
* @param name the name of the permission.
* @param actions the actions of the permission.
* @param certs the certificates the permission's class was signed with.
* This is a list of certificate chains, where each chain is composed of
* a signer certificate and optionally its supporting certificate chain.
* Each chain is ordered bottom-to-top (i.e., with the signer
* certificate first and the (root) certificate authority last).
*/
public SelfPermission(String type, String name, String actions,
Certificate[] certs)
{
super(type);
if (type == null) {
throw new NullPointerException
(LocalizedMessage.getNonlocalized("type.can.t.be.null"));
}
this.type = type;
this.name = name;
this.actions = actions;
if (certs != null) {
// Extract the signer certs from the list of certificates.
for (int i=0; i<certs.length; i++) {
if (!(certs[i] instanceof X509Certificate)) {
// there is no concept of signer certs, so we store the
// entire cert array
this.certs = certs.clone();
break;
}
}
if (this.certs == null) {
// Go through the list of certs and see if all the certs are
// signer certs.
int i = 0;
int count = 0;
while (i < certs.length) {
count++;
while (((i+1) < certs.length) &&
((X509Certificate)certs[i]).getIssuerX500Principal().equals(
((X509Certificate)certs[i+1]).getSubjectX500Principal())) {
i++;
}
i++;
}
if (count == certs.length) {
// All the certs are signer certs, so we store the
// entire array
this.certs = certs.clone();
}
if (this.certs == null) {
// extract the signer certs
List<Certificate> signerCerts = new ArrayList<>();
i = 0;
while (i < certs.length) {
signerCerts.add(certs[i]);
while (((i+1) < certs.length) &&
((X509Certificate)certs[i]).getIssuerX500Principal().equals(
((X509Certificate)certs[i+1]).getSubjectX500Principal())) {
i++;
}
i++;
}
this.certs = new Certificate[signerCerts.size()];
signerCerts.toArray(this.certs);
}
}
}
}
This method always returns false for SelfPermission permissions.
That is, an SelfPermission never considered to
imply another permission.
Params: - p – the permission to check against.
Returns: false.
/**
* This method always returns false for SelfPermission permissions.
* That is, an SelfPermission never considered to
* imply another permission.
*
* @param p the permission to check against.
*
* @return false.
*/
@Override public boolean implies(Permission p) {
return false;
}
Checks two SelfPermission objects for equality.
Checks that obj is an SelfPermission, and has
the same type (class) name, permission name, actions, and
certificates as this object.
Params: - obj – the object we are testing for equality with this object.
Returns: true if obj is an SelfPermission, and has the same
type (class) name, permission name, actions, and
certificates as this object.
/**
* Checks two SelfPermission objects for equality.
*
* Checks that <i>obj</i> is an SelfPermission, and has
* the same type (class) name, permission name, actions, and
* certificates as this object.
*
* @param obj the object we are testing for equality with this object.
*
* @return true if obj is an SelfPermission, and has the same
* type (class) name, permission name, actions, and
* certificates as this object.
*/
@Override public boolean equals(Object obj) {
if (obj == this)
return true;
if (! (obj instanceof SelfPermission))
return false;
SelfPermission that = (SelfPermission) obj;
if (!(this.type.equals(that.type) &&
this.name.equals(that.name) &&
this.actions.equals(that.actions)))
return false;
if (this.certs.length != that.certs.length)
return false;
int i,j;
boolean match;
for (i = 0; i < this.certs.length; i++) {
match = false;
for (j = 0; j < that.certs.length; j++) {
if (this.certs[i].equals(that.certs[j])) {
match = true;
break;
}
}
if (!match) return false;
}
for (i = 0; i < that.certs.length; i++) {
match = false;
for (j = 0; j < this.certs.length; j++) {
if (that.certs[i].equals(this.certs[j])) {
match = true;
break;
}
}
if (!match) return false;
}
return true;
}
Returns the hash code value for this object.
Returns: a hash code value for this object.
/**
* Returns the hash code value for this object.
*
* @return a hash code value for this object.
*/
@Override public int hashCode() {
int hash = type.hashCode();
if (name != null)
hash ^= name.hashCode();
if (actions != null)
hash ^= actions.hashCode();
return hash;
}
Returns the canonical string representation of the actions,
which currently is the empty string "", since there are no actions
for an SelfPermission. That is, the actions for the
permission that will be created when this SelfPermission
is resolved may be non-null, but an SelfPermission
itself is never considered to have any actions.
Returns: the empty string "".
/**
* Returns the canonical string representation of the actions,
* which currently is the empty string "", since there are no actions
* for an SelfPermission. That is, the actions for the
* permission that will be created when this SelfPermission
* is resolved may be non-null, but an SelfPermission
* itself is never considered to have any actions.
*
* @return the empty string "".
*/
@Override public String getActions() {
return "";
}
public String getSelfType() {
return type;
}
public String getSelfName() {
return name;
}
public String getSelfActions() {
return actions;
}
public Certificate[] getCerts() {
return certs;
}
Returns a string describing this SelfPermission. The convention
is to specify the class name, the permission name, and the actions,
in the following format: '(unresolved "ClassName" "name" "actions")'.
Returns: information about this SelfPermission.
/**
* Returns a string describing this SelfPermission. The convention
* is to specify the class name, the permission name, and the actions,
* in the following format: '(unresolved "ClassName" "name" "actions")'.
*
* @return information about this SelfPermission.
*/
@Override public String toString() {
return "(SelfPermission " + type + " " + name + " " + actions + ")";
}
}
holds policy information that we need to synch on
/**
* holds policy information that we need to synch on
*/
private static class PolicyInfo {
private static final boolean verbose = false;
// Stores grant entries in the policy
final List<PolicyEntry> policyEntries;
// Maps aliases to certs
final Map<Object, Object> aliasMapping;
// Maps ProtectionDomain to PermissionCollection
private final ProtectionDomainCache[] pdMapping;
private java.util.Random random;
PolicyInfo(int numCaches) {
policyEntries = new ArrayList<>();
aliasMapping = Collections.synchronizedMap(new HashMap<>(11));
pdMapping = new ProtectionDomainCache[numCaches];
JavaSecurityAccess jspda
= SharedSecrets.getJavaSecurityAccess();
for (int i = 0; i < numCaches; i++) {
pdMapping[i] = jspda.getProtectionDomainCache();
}
if (numCaches > 1) {
random = new java.util.Random();
}
}
ProtectionDomainCache getPdMapping() {
if (pdMapping.length == 1) {
return pdMapping[0];
} else {
int i = java.lang.Math.abs(random.nextInt() % pdMapping.length);
return pdMapping[i];
}
}
}
}