--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/provider/PolicyParser.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,1372 @@
+/*
+ * Copyright (c) 1997, 2017, 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.security.GeneralSecurityException;
+import java.security.Principal;
+import java.util.*;
+import javax.security.auth.x500.X500Principal;
+
+import sun.security.util.Debug;
+import sun.security.util.PropertyExpander;
+import sun.security.util.LocalizedMessage;
+
+/**
+ * The policy for a Java runtime (specifying
+ * which permissions are available for code from various principals)
+ * is represented as a separate
+ * persistent configuration. The configuration may be stored as a
+ * flat ASCII file, as a serialized binary file of
+ * the Policy class, or as a database.
+ *
+ * <p>The Java runtime creates one global Policy object, which is used to
+ * represent the static policy configuration file. It is consulted by
+ * a ProtectionDomain when the protection domain initializes its set of
+ * permissions.
+ *
+ * <p>The Policy <code>init</code> method parses the policy
+ * configuration file, and then
+ * populates the Policy object. The Policy object is agnostic in that
+ * it is not involved in making policy decisions. It is merely the
+ * Java runtime representation of the persistent policy configuration
+ * file.
+ *
+ * <p>When a protection domain needs to initialize its set of
+ * permissions, it executes code such as the following
+ * to ask the global Policy object to populate a
+ * Permissions object with the appropriate permissions:
+ * <pre>
+ * policy = Policy.getPolicy();
+ * Permissions perms = policy.getPermissions(protectiondomain)
+ * </pre>
+ *
+ * <p>The protection domain contains a CodeSource
+ * object, which encapsulates its codebase (URL) and public key attributes.
+ * It also contains the principals associated with the domain.
+ * The Policy object evaluates the global policy in light of who the
+ * principal is and what the code source is and returns an appropriate
+ * Permissions object.
+ *
+ * @author Roland Schemers
+ * @author Ram Marti
+ *
+ * @since 1.2
+ */
+
+public class PolicyParser {
+
+ private Vector<GrantEntry> grantEntries;
+ private Map<String, DomainEntry> domainEntries;
+
+ // Convenience variables for parsing
+ private static final Debug debug = Debug.getInstance("parser",
+ "\t[Policy Parser]");
+ private StreamTokenizer st;
+ private int lookahead;
+ private boolean expandProp = false;
+ private String keyStoreUrlString = null; // unexpanded
+ private String keyStoreType = null;
+ private String keyStoreProvider = null;
+ private String storePassURL = null;
+
+ private String expand(String value)
+ throws PropertyExpander.ExpandException
+ {
+ return expand(value, false);
+ }
+
+ private String expand(String value, boolean encodeURL)
+ throws PropertyExpander.ExpandException
+ {
+ if (!expandProp) {
+ return value;
+ } else {
+ return PropertyExpander.expand(value, encodeURL);
+ }
+ }
+
+ /**
+ * Creates a PolicyParser object.
+ */
+
+ public PolicyParser() {
+ grantEntries = new Vector<GrantEntry>();
+ }
+
+
+ public PolicyParser(boolean expandProp) {
+ this();
+ this.expandProp = expandProp;
+ }
+
+ /**
+ * Reads a policy configuration into the Policy object using a
+ * Reader object. <p>
+ *
+ * @param policy the policy Reader object.
+ *
+ * @exception ParsingException if the policy configuration contains
+ * a syntax error.
+ *
+ * @exception IOException if an error occurs while reading the policy
+ * configuration.
+ */
+
+ public void read(Reader policy)
+ throws ParsingException, IOException
+ {
+ if (!(policy instanceof BufferedReader)) {
+ policy = new BufferedReader(policy);
+ }
+
+ /**
+ * Configure the stream tokenizer:
+ * Recognize strings between "..."
+ * Don't convert words to lowercase
+ * Recognize both C-style and C++-style comments
+ * Treat end-of-line as white space, not as a token
+ */
+ st = new StreamTokenizer(policy);
+
+ st.resetSyntax();
+ st.wordChars('a', 'z');
+ st.wordChars('A', 'Z');
+ st.wordChars('.', '.');
+ st.wordChars('0', '9');
+ st.wordChars('_', '_');
+ st.wordChars('$', '$');
+ st.wordChars(128 + 32, 255);
+ st.whitespaceChars(0, ' ');
+ st.commentChar('/');
+ st.quoteChar('\'');
+ st.quoteChar('"');
+ st.lowerCaseMode(false);
+ st.ordinaryChar('/');
+ st.slashSlashComments(true);
+ st.slashStarComments(true);
+
+ /**
+ * The main parsing loop. The loop is executed once
+ * for each entry in the config file. The entries
+ * are delimited by semicolons. Once we've read in
+ * the information for an entry, go ahead and try to
+ * add it to the policy vector.
+ *
+ */
+
+ lookahead = st.nextToken();
+ GrantEntry ge = null;
+ while (lookahead != StreamTokenizer.TT_EOF) {
+ if (peek("grant")) {
+ ge = parseGrantEntry();
+ // could be null if we couldn't expand a property
+ if (ge != null)
+ add(ge);
+ } else if (peek("keystore") && keyStoreUrlString==null) {
+ // only one keystore entry per policy file, others will be
+ // ignored
+ parseKeyStoreEntry();
+ } else if (peek("keystorePasswordURL") && storePassURL==null) {
+ // only one keystore passwordURL per policy file, others will be
+ // ignored
+ parseStorePassURL();
+ } else if (ge == null && keyStoreUrlString == null &&
+ storePassURL == null && peek("domain")) {
+ if (domainEntries == null) {
+ domainEntries = new TreeMap<>();
+ }
+ DomainEntry de = parseDomainEntry();
+ if (de != null) {
+ String domainName = de.getName();
+ if (!domainEntries.containsKey(domainName)) {
+ domainEntries.put(domainName, de);
+ } else {
+ LocalizedMessage localizedMsg =
+ new LocalizedMessage("duplicate.keystore.domain.name");
+ Object[] source = {domainName};
+ String msg = "duplicate keystore domain name: " +
+ domainName;
+ throw new ParsingException(msg, localizedMsg, source);
+ }
+ }
+ } else {
+ // error?
+ }
+ match(";");
+ }
+
+ if (keyStoreUrlString == null && storePassURL != null) {
+ throw new ParsingException(LocalizedMessage.getMessage
+ ("keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore"));
+ }
+ }
+
+ public void add(GrantEntry ge)
+ {
+ grantEntries.addElement(ge);
+ }
+
+ public void replace(GrantEntry origGe, GrantEntry newGe)
+ {
+ grantEntries.setElementAt(newGe, grantEntries.indexOf(origGe));
+ }
+
+ public boolean remove(GrantEntry ge)
+ {
+ return grantEntries.removeElement(ge);
+ }
+
+ /**
+ * Returns the (possibly expanded) keystore location, or null if the
+ * expansion fails.
+ */
+ public String getKeyStoreUrl() {
+ try {
+ if (keyStoreUrlString!=null && keyStoreUrlString.length()!=0) {
+ return expand(keyStoreUrlString, true).replace
+ (File.separatorChar, '/');
+ }
+ } catch (PropertyExpander.ExpandException peee) {
+ if (debug != null) {
+ debug.println(peee.toString());
+ }
+ return null;
+ }
+ return null;
+ }
+
+ public void setKeyStoreUrl(String url) {
+ keyStoreUrlString = url;
+ }
+
+ public String getKeyStoreType() {
+ return keyStoreType;
+ }
+
+ public void setKeyStoreType(String type) {
+ keyStoreType = type;
+ }
+
+ public String getKeyStoreProvider() {
+ return keyStoreProvider;
+ }
+
+ public void setKeyStoreProvider(String provider) {
+ keyStoreProvider = provider;
+ }
+
+ public String getStorePassURL() {
+ try {
+ if (storePassURL!=null && storePassURL.length()!=0) {
+ return expand(storePassURL, true).replace
+ (File.separatorChar, '/');
+ }
+ } catch (PropertyExpander.ExpandException peee) {
+ if (debug != null) {
+ debug.println(peee.toString());
+ }
+ return null;
+ }
+ return null;
+ }
+
+ public void setStorePassURL(String storePassURL) {
+ this.storePassURL = storePassURL;
+ }
+
+ /**
+ * 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.
+ */
+ public Enumeration<GrantEntry> grantElements(){
+ return grantEntries.elements();
+ }
+
+ public Collection<DomainEntry> getDomainEntries() {
+ return domainEntries.values();
+ }
+
+ /**
+ * write out the policy
+ */
+
+ public void write(Writer policy)
+ {
+ PrintWriter out = new PrintWriter(new BufferedWriter(policy));
+
+ Enumeration<GrantEntry> enum_ = grantElements();
+
+ out.println("/* AUTOMATICALLY GENERATED ON "+
+ (new java.util.Date()) + "*/");
+ out.println("/* DO NOT EDIT */");
+ out.println();
+
+ // write the (unexpanded) keystore entry as the first entry of the
+ // policy file
+ if (keyStoreUrlString != null) {
+ writeKeyStoreEntry(out);
+ }
+ if (storePassURL != null) {
+ writeStorePassURL(out);
+ }
+
+ // write "grant" entries
+ while (enum_.hasMoreElements()) {
+ GrantEntry ge = enum_.nextElement();
+ ge.write(out);
+ out.println();
+ }
+ out.flush();
+ }
+
+ /**
+ * parses a keystore entry
+ */
+ private void parseKeyStoreEntry() throws ParsingException, IOException {
+ match("keystore");
+ keyStoreUrlString = match("quoted string");
+
+ // parse keystore type
+ if (!peek(",")) {
+ return; // default type
+ }
+ match(",");
+
+ if (peek("\"")) {
+ keyStoreType = match("quoted string");
+ } else {
+ throw new ParsingException(st.lineno(),
+ LocalizedMessage.getMessage("expected.keystore.type"));
+ }
+
+ // parse keystore provider
+ if (!peek(",")) {
+ return; // provider optional
+ }
+ match(",");
+
+ if (peek("\"")) {
+ keyStoreProvider = match("quoted string");
+ } else {
+ throw new ParsingException(st.lineno(),
+ LocalizedMessage.getMessage("expected.keystore.provider"));
+ }
+ }
+
+ private void parseStorePassURL() throws ParsingException, IOException {
+ match("keyStorePasswordURL");
+ storePassURL = match("quoted string");
+ }
+
+ /**
+ * writes the (unexpanded) keystore entry
+ */
+ private void writeKeyStoreEntry(PrintWriter out) {
+ out.print("keystore \"");
+ out.print(keyStoreUrlString);
+ out.print('"');
+ if (keyStoreType != null && keyStoreType.length() > 0)
+ out.print(", \"" + keyStoreType + "\"");
+ if (keyStoreProvider != null && keyStoreProvider.length() > 0)
+ out.print(", \"" + keyStoreProvider + "\"");
+ out.println(";");
+ out.println();
+ }
+
+ private void writeStorePassURL(PrintWriter out) {
+ out.print("keystorePasswordURL \"");
+ out.print(storePassURL);
+ out.print('"');
+ out.println(";");
+ out.println();
+ }
+
+ /**
+ * parse a Grant entry
+ */
+ private GrantEntry parseGrantEntry()
+ throws ParsingException, IOException
+ {
+ GrantEntry e = new GrantEntry();
+ LinkedList<PrincipalEntry> principals = null;
+ boolean ignoreEntry = false;
+
+ match("grant");
+
+ while(!peek("{")) {
+
+ if (peekAndMatch("Codebase")) {
+ if (e.codeBase != null)
+ throw new ParsingException(
+ st.lineno(),
+ LocalizedMessage.getMessage
+ ("multiple.Codebase.expressions"));
+ e.codeBase = match("quoted string");
+ peekAndMatch(",");
+ } else if (peekAndMatch("SignedBy")) {
+ if (e.signedBy != null)
+ throw new ParsingException(
+ st.lineno(),
+ LocalizedMessage.getMessage
+ ("multiple.SignedBy.expressions"));
+ e.signedBy = match("quoted string");
+
+ // verify syntax of the aliases
+ StringTokenizer aliases = new StringTokenizer(e.signedBy,
+ ",", true);
+ int actr = 0;
+ int cctr = 0;
+ while (aliases.hasMoreTokens()) {
+ String alias = aliases.nextToken().trim();
+ if (alias.equals(","))
+ cctr++;
+ else if (alias.length() > 0)
+ actr++;
+ }
+ if (actr <= cctr)
+ throw new ParsingException(
+ st.lineno(),
+ LocalizedMessage.getMessage
+ ("SignedBy.has.empty.alias"));
+
+ peekAndMatch(",");
+ } else if (peekAndMatch("Principal")) {
+ if (principals == null) {
+ principals = new LinkedList<>();
+ }
+
+ String principalClass;
+ String principalName;
+
+ if (peek("\"")) {
+ // both the principalClass and principalName
+ // will be replaced later
+ principalClass = PrincipalEntry.REPLACE_NAME;
+ principalName = match("principal type");
+ } else {
+ // check for principalClass wildcard
+ if (peek("*")) {
+ match("*");
+ principalClass = PrincipalEntry.WILDCARD_CLASS;
+ } else {
+ principalClass = match("principal type");
+ }
+
+ // check for principalName wildcard
+ if (peek("*")) {
+ match("*");
+ principalName = PrincipalEntry.WILDCARD_NAME;
+ } else {
+ principalName = match("quoted string");
+ }
+
+ // disallow WILDCARD_CLASS && actual name
+ if (principalClass.equals(PrincipalEntry.WILDCARD_CLASS) &&
+ !principalName.equals(PrincipalEntry.WILDCARD_NAME)) {
+ if (debug != null) {
+ debug.println("disallowing principal that " +
+ "has WILDCARD class but no WILDCARD name");
+ }
+ throw new ParsingException
+ (st.lineno(),
+ LocalizedMessage.getMessage
+ ("can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name"));
+ }
+ }
+
+ try {
+ principalName = expand(principalName);
+
+ if (principalClass.equals
+ ("javax.security.auth.x500.X500Principal") &&
+ !principalName.equals(PrincipalEntry.WILDCARD_NAME)) {
+
+ // 4702543: X500 names with an EmailAddress
+ // were encoded incorrectly. construct a new
+ // X500Principal with correct encoding.
+
+ X500Principal p = new X500Principal
+ ((new X500Principal(principalName)).toString());
+ principalName = p.getName();
+ }
+
+ principals.add
+ (new PrincipalEntry(principalClass, principalName));
+ } catch (PropertyExpander.ExpandException peee) {
+ // ignore the entire policy entry
+ // but continue parsing all the info
+ // so we can get to the next entry
+ if (debug != null) {
+ debug.println("principal name expansion failed: " +
+ principalName);
+ }
+ ignoreEntry = true;
+ }
+ peekAndMatch(",");
+
+ } else {
+ throw new ParsingException(st.lineno(),
+ LocalizedMessage.getMessage
+ ("expected.codeBase.or.SignedBy.or.Principal"));
+ }
+ }
+
+ if (principals != null) e.principals = principals;
+ match("{");
+
+ while(!peek("}")) {
+ if (peek("Permission")) {
+ try {
+ PermissionEntry pe = parsePermissionEntry();
+ e.add(pe);
+ } catch (PropertyExpander.ExpandException peee) {
+ // ignore. The add never happened
+ if (debug != null) {
+ debug.println(peee.toString());
+ }
+ skipEntry(); // BugId 4219343
+ }
+ match(";");
+ } else {
+ throw new
+ ParsingException(st.lineno(),
+ LocalizedMessage.getMessage
+ ("expected.permission.entry"));
+ }
+ }
+ match("}");
+
+ try {
+ if (e.signedBy != null) e.signedBy = expand(e.signedBy);
+ if (e.codeBase != null) {
+ e.codeBase = expand(e.codeBase, true).replace
+ (File.separatorChar, '/');
+ }
+ } catch (PropertyExpander.ExpandException peee) {
+ if (debug != null) {
+ debug.println(peee.toString());
+ }
+ return null;
+ }
+
+ return (ignoreEntry == true) ? null : e;
+ }
+
+ /**
+ * parse a Permission entry
+ */
+ private PermissionEntry parsePermissionEntry()
+ throws ParsingException, IOException, PropertyExpander.ExpandException
+ {
+ PermissionEntry e = new PermissionEntry();
+
+ // Permission
+ match("Permission");
+ e.permission = match("permission type");
+
+ if (peek("\"")) {
+ // Permission name
+ e.name = expand(match("quoted string"));
+ }
+
+ if (!peek(",")) {
+ return e;
+ }
+ match(",");
+
+ if (peek("\"")) {
+ e.action = expand(match("quoted string"));
+ if (!peek(",")) {
+ return e;
+ }
+ match(",");
+ }
+
+ if (peekAndMatch("SignedBy")) {
+ e.signedBy = expand(match("quoted string"));
+ }
+ return e;
+ }
+
+ /**
+ * parse a domain entry
+ */
+ private DomainEntry parseDomainEntry()
+ throws ParsingException, IOException
+ {
+ boolean ignoreEntry = false;
+ DomainEntry domainEntry;
+ String name = null;
+ Map<String, String> properties = new HashMap<>();
+
+ match("domain");
+ name = match("domain name");
+
+ while(!peek("{")) {
+ // get the domain properties
+ properties = parseProperties("{");
+ }
+ match("{");
+ domainEntry = new DomainEntry(name, properties);
+
+ while(!peek("}")) {
+
+ match("keystore");
+ name = match("keystore name");
+ // get the keystore properties
+ if (!peek("}")) {
+ properties = parseProperties(";");
+ }
+ match(";");
+ domainEntry.add(new KeyStoreEntry(name, properties));
+ }
+ match("}");
+
+ return (ignoreEntry == true) ? null : domainEntry;
+ }
+
+ /*
+ * Return a collection of domain properties or keystore properties.
+ */
+ private Map<String, String> parseProperties(String terminator)
+ throws ParsingException, IOException {
+
+ Map<String, String> properties = new HashMap<>();
+ String key;
+ String value;
+ while (!peek(terminator)) {
+ key = match("property name");
+ match("=");
+
+ try {
+ value = expand(match("quoted string"));
+ } catch (PropertyExpander.ExpandException peee) {
+ throw new IOException(peee.getLocalizedMessage());
+ }
+ properties.put(key.toLowerCase(Locale.ENGLISH), value);
+ }
+
+ return properties;
+ }
+
+ private boolean peekAndMatch(String expect)
+ throws ParsingException, IOException
+ {
+ if (peek(expect)) {
+ match(expect);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private boolean peek(String expect) {
+ boolean found = false;
+
+ switch (lookahead) {
+
+ case StreamTokenizer.TT_WORD:
+ if (expect.equalsIgnoreCase(st.sval))
+ found = true;
+ break;
+ case ',':
+ if (expect.equalsIgnoreCase(","))
+ found = true;
+ break;
+ case '{':
+ if (expect.equalsIgnoreCase("{"))
+ found = true;
+ break;
+ case '}':
+ if (expect.equalsIgnoreCase("}"))
+ found = true;
+ break;
+ case '"':
+ if (expect.equalsIgnoreCase("\""))
+ found = true;
+ break;
+ case '*':
+ if (expect.equalsIgnoreCase("*"))
+ found = true;
+ break;
+ case ';':
+ if (expect.equalsIgnoreCase(";"))
+ found = true;
+ break;
+ default:
+
+ }
+ return found;
+ }
+
+ private String match(String expect)
+ throws ParsingException, IOException
+ {
+ String value = null;
+
+ switch (lookahead) {
+ case StreamTokenizer.TT_NUMBER:
+ throw new ParsingException(st.lineno(), expect,
+ LocalizedMessage.getMessage("number.") +
+ String.valueOf(st.nval));
+ case StreamTokenizer.TT_EOF:
+ LocalizedMessage localizedMsg = new LocalizedMessage
+ ("expected.expect.read.end.of.file.");
+ Object[] source = {expect};
+ String msg = "expected [" + expect + "], read [end of file]";
+ throw new ParsingException(msg, localizedMsg, source);
+ case StreamTokenizer.TT_WORD:
+ if (expect.equalsIgnoreCase(st.sval)) {
+ lookahead = st.nextToken();
+ } else if (expect.equalsIgnoreCase("permission type")) {
+ value = st.sval;
+ lookahead = st.nextToken();
+ } else if (expect.equalsIgnoreCase("principal type")) {
+ value = st.sval;
+ lookahead = st.nextToken();
+ } else if (expect.equalsIgnoreCase("domain name") ||
+ expect.equalsIgnoreCase("keystore name") ||
+ expect.equalsIgnoreCase("property name")) {
+ value = st.sval;
+ lookahead = st.nextToken();
+ } else {
+ throw new ParsingException(st.lineno(), expect,
+ st.sval);
+ }
+ break;
+ case '"':
+ if (expect.equalsIgnoreCase("quoted string")) {
+ value = st.sval;
+ lookahead = st.nextToken();
+ } else if (expect.equalsIgnoreCase("permission type")) {
+ value = st.sval;
+ lookahead = st.nextToken();
+ } else if (expect.equalsIgnoreCase("principal type")) {
+ value = st.sval;
+ lookahead = st.nextToken();
+ } else {
+ throw new ParsingException(st.lineno(), expect, st.sval);
+ }
+ break;
+ case ',':
+ if (expect.equalsIgnoreCase(","))
+ lookahead = st.nextToken();
+ else
+ throw new ParsingException(st.lineno(), expect, ",");
+ break;
+ case '{':
+ if (expect.equalsIgnoreCase("{"))
+ lookahead = st.nextToken();
+ else
+ throw new ParsingException(st.lineno(), expect, "{");
+ break;
+ case '}':
+ if (expect.equalsIgnoreCase("}"))
+ lookahead = st.nextToken();
+ else
+ throw new ParsingException(st.lineno(), expect, "}");
+ break;
+ case ';':
+ if (expect.equalsIgnoreCase(";"))
+ lookahead = st.nextToken();
+ else
+ throw new ParsingException(st.lineno(), expect, ";");
+ break;
+ case '*':
+ if (expect.equalsIgnoreCase("*"))
+ lookahead = st.nextToken();
+ else
+ throw new ParsingException(st.lineno(), expect, "*");
+ break;
+ case '=':
+ if (expect.equalsIgnoreCase("="))
+ lookahead = st.nextToken();
+ else
+ throw new ParsingException(st.lineno(), expect, "=");
+ break;
+ default:
+ throw new ParsingException(st.lineno(), expect,
+ new String(new char[] {(char)lookahead}));
+ }
+ return value;
+ }
+
+ /**
+ * skip all tokens for this entry leaving the delimiter ";"
+ * in the stream.
+ */
+ private void skipEntry() throws ParsingException, IOException {
+ while(lookahead != ';') {
+ switch (lookahead) {
+ case StreamTokenizer.TT_NUMBER:
+ throw new ParsingException(st.lineno(), ";",
+ LocalizedMessage.getMessage("number.") +
+ String.valueOf(st.nval));
+ case StreamTokenizer.TT_EOF:
+ throw new ParsingException(LocalizedMessage.getMessage
+ ("expected.read.end.of.file."));
+ default:
+ lookahead = st.nextToken();
+ }
+ }
+ }
+
+ /**
+ * Each grant entry in the policy configuration file is
+ * represented by a GrantEntry object.
+ *
+ * <p>
+ * For example, the entry
+ * <pre>
+ * grant signedBy "Duke" {
+ * permission java.io.FilePermission "/tmp", "read,write";
+ * };
+ *
+ * </pre>
+ * is represented internally
+ * <pre>
+ *
+ * pe = new PermissionEntry("java.io.FilePermission",
+ * "/tmp", "read,write");
+ *
+ * ge = new GrantEntry("Duke", null);
+ *
+ * ge.add(pe);
+ *
+ * </pre>
+ *
+ * @author Roland Schemers
+ *
+ * version 1.19, 05/21/98
+ */
+
+ public static class GrantEntry {
+
+ public String signedBy;
+ public String codeBase;
+ public LinkedList<PrincipalEntry> principals;
+ public Vector<PermissionEntry> permissionEntries;
+
+ public GrantEntry() {
+ principals = new LinkedList<PrincipalEntry>();
+ permissionEntries = new Vector<PermissionEntry>();
+ }
+
+ public GrantEntry(String signedBy, String codeBase) {
+ this.codeBase = codeBase;
+ this.signedBy = signedBy;
+ principals = new LinkedList<PrincipalEntry>();
+ permissionEntries = new Vector<PermissionEntry>();
+ }
+
+ public void add(PermissionEntry pe)
+ {
+ permissionEntries.addElement(pe);
+ }
+
+ public boolean remove(PrincipalEntry pe)
+ {
+ return principals.remove(pe);
+ }
+
+ public boolean remove(PermissionEntry pe)
+ {
+ return permissionEntries.removeElement(pe);
+ }
+
+ public boolean contains(PrincipalEntry pe)
+ {
+ return principals.contains(pe);
+ }
+
+ public boolean contains(PermissionEntry pe)
+ {
+ return permissionEntries.contains(pe);
+ }
+
+ /**
+ * Enumerate all the permission entries in this GrantEntry.
+ */
+ public Enumeration<PermissionEntry> permissionElements(){
+ return permissionEntries.elements();
+ }
+
+
+ public void write(PrintWriter out) {
+ out.print("grant");
+ if (signedBy != null) {
+ out.print(" signedBy \"");
+ out.print(signedBy);
+ out.print('"');
+ if (codeBase != null)
+ out.print(", ");
+ }
+ if (codeBase != null) {
+ out.print(" codeBase \"");
+ out.print(codeBase);
+ out.print('"');
+ if (principals != null && principals.size() > 0)
+ out.print(",\n");
+ }
+ if (principals != null && principals.size() > 0) {
+ Iterator<PrincipalEntry> pli = principals.iterator();
+ while (pli.hasNext()) {
+ out.print(" ");
+ PrincipalEntry pe = pli.next();
+ pe.write(out);
+ if (pli.hasNext())
+ out.print(",\n");
+ }
+ }
+ out.println(" {");
+ Enumeration<PermissionEntry> enum_ = permissionEntries.elements();
+ while (enum_.hasMoreElements()) {
+ PermissionEntry pe = enum_.nextElement();
+ out.write(" ");
+ pe.write(out);
+ }
+ out.println("};");
+ }
+
+ public Object clone() {
+ GrantEntry ge = new GrantEntry();
+ ge.codeBase = this.codeBase;
+ ge.signedBy = this.signedBy;
+ ge.principals = new LinkedList<PrincipalEntry>(this.principals);
+ ge.permissionEntries =
+ new Vector<PermissionEntry>(this.permissionEntries);
+ return ge;
+ }
+ }
+
+ /**
+ * Principal info (class and name) in a grant entry
+ */
+ public static class PrincipalEntry implements Principal {
+
+ public static final String WILDCARD_CLASS = "WILDCARD_PRINCIPAL_CLASS";
+ public static final String WILDCARD_NAME = "WILDCARD_PRINCIPAL_NAME";
+ public static final String REPLACE_NAME = "PolicyParser.REPLACE_NAME";
+
+ String principalClass;
+ String principalName;
+
+ /**
+ * A PrincipalEntry consists of the Principal class and Principal name.
+ *
+ * @param principalClass the Principal class
+ * @param principalName the Principal name
+ * @throws NullPointerException if principalClass or principalName
+ * are null
+ */
+ public PrincipalEntry(String principalClass, String principalName) {
+ if (principalClass == null || principalName == null)
+ throw new NullPointerException(LocalizedMessage.getMessage
+ ("null.principalClass.or.principalName"));
+ this.principalClass = principalClass;
+ this.principalName = principalName;
+ }
+
+ boolean isWildcardName() {
+ return principalName.equals(WILDCARD_NAME);
+ }
+
+ boolean isWildcardClass() {
+ return principalClass.equals(WILDCARD_CLASS);
+ }
+
+ boolean isReplaceName() {
+ return principalClass.equals(REPLACE_NAME);
+ }
+
+ public String getPrincipalClass() {
+ return principalClass;
+ }
+
+ public String getPrincipalName() {
+ return principalName;
+ }
+
+ public String getDisplayClass() {
+ if (isWildcardClass()) {
+ return "*";
+ } else if (isReplaceName()) {
+ return "";
+ }
+ else return principalClass;
+ }
+
+ public String getDisplayName() {
+ return getDisplayName(false);
+ }
+
+ public String getDisplayName(boolean addQuote) {
+ if (isWildcardName()) {
+ return "*";
+ }
+ else {
+ if (addQuote) return "\"" + principalName + "\"";
+ else return principalName;
+ }
+ }
+
+ @Override
+ public String getName() {
+ return principalName;
+ }
+
+ @Override
+ public String toString() {
+ if (!isReplaceName()) {
+ return getDisplayClass() + "/" + getDisplayName();
+ } else {
+ return getDisplayName();
+ }
+ }
+
+ /**
+ * Test for equality between the specified object and this object.
+ * Two PrincipalEntries are equal if their class and name values
+ * are equal.
+ *
+ * @param obj the object to test for equality with this object
+ * @return true if the objects are equal, false otherwise
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+
+ if (!(obj instanceof PrincipalEntry))
+ return false;
+
+ PrincipalEntry that = (PrincipalEntry)obj;
+ return (principalClass.equals(that.principalClass) &&
+ principalName.equals(that.principalName));
+ }
+
+ /**
+ * Return a hashcode for this PrincipalEntry.
+ *
+ * @return a hashcode for this PrincipalEntry
+ */
+ @Override
+ public int hashCode() {
+ return principalClass.hashCode();
+ }
+
+ public void write(PrintWriter out) {
+ out.print("principal " + getDisplayClass() + " " +
+ getDisplayName(true));
+ }
+ }
+
+ /**
+ * Each permission entry in the policy configuration file is
+ * represented by a
+ * PermissionEntry object.
+ *
+ * <p>
+ * For example, the entry
+ * <pre>
+ * permission java.io.FilePermission "/tmp", "read,write";
+ * </pre>
+ * is represented internally
+ * <pre>
+ *
+ * pe = new PermissionEntry("java.io.FilePermission",
+ * "/tmp", "read,write");
+ * </pre>
+ *
+ * @author Roland Schemers
+ *
+ * version 1.19, 05/21/98
+ */
+
+ public static class PermissionEntry {
+
+ public String permission;
+ public String name;
+ public String action;
+ public String signedBy;
+
+ public PermissionEntry() {
+ }
+
+ public PermissionEntry(String permission,
+ String name,
+ String action) {
+ this.permission = permission;
+ this.name = name;
+ this.action = action;
+ }
+
+ /**
+ * Calculates a hash code value for the object. Objects
+ * which are equal will also have the same hashcode.
+ */
+ @Override
+ public int hashCode() {
+ int retval = permission.hashCode();
+ if (name != null) retval ^= name.hashCode();
+ if (action != null) retval ^= action.hashCode();
+ return retval;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+
+ if (! (obj instanceof PermissionEntry))
+ return false;
+
+ PermissionEntry that = (PermissionEntry) obj;
+
+ if (this.permission == null) {
+ if (that.permission != null) return false;
+ } else {
+ if (!this.permission.equals(that.permission)) return false;
+ }
+
+ if (this.name == null) {
+ if (that.name != null) return false;
+ } else {
+ if (!this.name.equals(that.name)) return false;
+ }
+
+ if (this.action == null) {
+ if (that.action != null) return false;
+ } else {
+ if (!this.action.equals(that.action)) return false;
+ }
+
+ if (this.signedBy == null) {
+ if (that.signedBy != null) return false;
+ } else {
+ if (!this.signedBy.equals(that.signedBy)) return false;
+ }
+
+ // everything matched -- the 2 objects are equal
+ return true;
+ }
+
+ public void write(PrintWriter out) {
+ out.print("permission ");
+ out.print(permission);
+ if (name != null) {
+ out.print(" \"");
+
+ // ATTENTION: regex with double escaping,
+ // the normal forms look like:
+ // $name =~ s/\\/\\\\/g; and
+ // $name =~ s/\"/\\\"/g;
+ // and then in a java string, it's escaped again
+
+ out.print(name.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\\"", "\\\\\\\""));
+ out.print('"');
+ }
+ if (action != null) {
+ out.print(", \"");
+ out.print(action);
+ out.print('"');
+ }
+ if (signedBy != null) {
+ out.print(", signedBy \"");
+ out.print(signedBy);
+ out.print('"');
+ }
+ out.println(";");
+ }
+ }
+
+ /**
+ * Each domain entry in the keystore domain configuration file is
+ * represented by a DomainEntry object.
+ */
+ static class DomainEntry {
+ private final String name;
+ private final Map<String, String> properties;
+ private final Map<String, KeyStoreEntry> entries;
+
+ DomainEntry(String name, Map<String, String> properties) {
+ this.name = name;
+ this.properties = properties;
+ entries = new HashMap<>();
+ }
+
+ String getName() {
+ return name;
+ }
+
+ Map<String, String> getProperties() {
+ return properties;
+ }
+
+ Collection<KeyStoreEntry> getEntries() {
+ return entries.values();
+ }
+
+ void add(KeyStoreEntry entry) throws ParsingException {
+ String keystoreName = entry.getName();
+ if (!entries.containsKey(keystoreName)) {
+ entries.put(keystoreName, entry);
+ } else {
+ LocalizedMessage localizedMsg = new LocalizedMessage
+ ("duplicate.keystore.name");
+ Object[] source = {keystoreName};
+ String msg = "duplicate keystore name: " + keystoreName;
+ throw new ParsingException(msg, localizedMsg, source);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s =
+ new StringBuilder("\ndomain ").append(name);
+
+ if (properties != null) {
+ for (Map.Entry<String, String> property :
+ properties.entrySet()) {
+ s.append("\n ").append(property.getKey()).append('=')
+ .append(property.getValue());
+ }
+ }
+ s.append(" {\n");
+
+ if (entries != null) {
+ for (KeyStoreEntry entry : entries.values()) {
+ s.append(entry).append("\n");
+ }
+ }
+ s.append("}");
+
+ return s.toString();
+ }
+ }
+
+ /**
+ * Each keystore entry in the keystore domain configuration file is
+ * represented by a KeyStoreEntry object.
+ */
+
+ static class KeyStoreEntry {
+ private final String name;
+ private final Map<String, String> properties;
+
+ KeyStoreEntry(String name, Map<String, String> properties) {
+ this.name = name;
+ this.properties = properties;
+ }
+
+ String getName() {
+ return name;
+ }
+
+ Map<String, String> getProperties() {
+ return properties;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder("\n keystore ").append(name);
+ if (properties != null) {
+ for (Map.Entry<String, String> property :
+ properties.entrySet()) {
+ s.append("\n ").append(property.getKey()).append('=')
+ .append(property.getValue());
+ }
+ }
+ s.append(";");
+
+ return s.toString();
+ }
+ }
+
+ public static class ParsingException extends GeneralSecurityException {
+
+ private static final long serialVersionUID = -4330692689482574072L;
+
+ private String i18nMessage;
+ private LocalizedMessage localizedMsg;
+ private Object[] source;
+
+ /**
+ * Constructs a ParsingException with the specified
+ * detail message. A detail message is a String that describes
+ * this particular exception, which may, for example, specify which
+ * algorithm is not available.
+ *
+ * @param msg the detail message.
+ */
+ public ParsingException(String msg) {
+ super(msg);
+ i18nMessage = msg;
+ }
+
+ public ParsingException(String msg, LocalizedMessage localizedMsg,
+ Object[] source) {
+ super(msg);
+ this.localizedMsg = localizedMsg;
+ this.source = source;
+ }
+
+ public ParsingException(int line, String msg) {
+ super("line " + line + ": " + msg);
+ // don't call form.format unless getLocalizedMessage is called
+ // to avoid unnecessary permission checks
+ localizedMsg = new LocalizedMessage("line.number.msg");
+ source = new Object[] {line, msg};
+ }
+
+ public ParsingException(int line, String expect, String actual) {
+ super("line " + line + ": expected [" + expect +
+ "], found [" + actual + "]");
+ // don't call form.format unless getLocalizedMessage is called
+ // to avoid unnecessary permission checks
+ localizedMsg = new LocalizedMessage
+ ("line.number.expected.expect.found.actual.");
+ source = new Object[] {line, expect, actual};
+ }
+
+ @Override
+ public String getLocalizedMessage() {
+ return i18nMessage != null ? i18nMessage : localizedMsg.format(source);
+ }
+ }
+
+ public static void main(String[] arg) throws Exception {
+ try (FileReader fr = new FileReader(arg[0]);
+ FileWriter fw = new FileWriter(arg[1])) {
+ PolicyParser pp = new PolicyParser(true);
+ pp.read(fr);
+ pp.write(fw);
+ }
+ }
+}