Path: blob/master/src/java.base/share/classes/sun/security/provider/PolicyParser.java
41159 views
/*1* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.provider;2627import java.io.*;28import java.security.GeneralSecurityException;29import java.security.Principal;30import java.util.*;31import javax.security.auth.x500.X500Principal;3233import sun.security.util.Debug;34import sun.security.util.PropertyExpander;35import sun.security.util.LocalizedMessage;3637/**38* The policy for a Java runtime (specifying39* which permissions are available for code from various principals)40* is represented as a separate41* persistent configuration. The configuration may be stored as a42* flat ASCII file, as a serialized binary file of43* the Policy class, or as a database.44*45* <p>The Java runtime creates one global Policy object, which is used to46* represent the static policy configuration file. It is consulted by47* a ProtectionDomain when the protection domain initializes its set of48* permissions.49*50* <p>The Policy <code>init</code> method parses the policy51* configuration file, and then52* populates the Policy object. The Policy object is agnostic in that53* it is not involved in making policy decisions. It is merely the54* Java runtime representation of the persistent policy configuration55* file.56*57* <p>When a protection domain needs to initialize its set of58* permissions, it executes code such as the following59* to ask the global Policy object to populate a60* Permissions object with the appropriate permissions:61* <pre>62* policy = Policy.getPolicy();63* Permissions perms = policy.getPermissions(protectiondomain)64* </pre>65*66* <p>The protection domain contains a CodeSource67* object, which encapsulates its codebase (URL) and public key attributes.68* It also contains the principals associated with the domain.69* The Policy object evaluates the global policy in light of who the70* principal is and what the code source is and returns an appropriate71* Permissions object.72*73* @author Roland Schemers74* @author Ram Marti75*76* @since 1.277*/7879public class PolicyParser {8081private Vector<GrantEntry> grantEntries;82private Map<String, DomainEntry> domainEntries;8384// Convenience variables for parsing85private static final Debug debug = Debug.getInstance("parser",86"\t[Policy Parser]");87private StreamTokenizer st;88private int lookahead;89private boolean expandProp = false;90private String keyStoreUrlString = null; // unexpanded91private String keyStoreType = null;92private String keyStoreProvider = null;93private String storePassURL = null;9495private String expand(String value)96throws PropertyExpander.ExpandException97{98return expand(value, false);99}100101private String expand(String value, boolean encodeURL)102throws PropertyExpander.ExpandException103{104if (!expandProp) {105return value;106} else {107return PropertyExpander.expand(value, encodeURL);108}109}110111/**112* Creates a PolicyParser object.113*/114115public PolicyParser() {116grantEntries = new Vector<GrantEntry>();117}118119120public PolicyParser(boolean expandProp) {121this();122this.expandProp = expandProp;123}124125/**126* Reads a policy configuration into the Policy object using a127* Reader object. <p>128*129* @param policy the policy Reader object.130*131* @exception ParsingException if the policy configuration contains132* a syntax error.133*134* @exception IOException if an error occurs while reading the policy135* configuration.136*/137138public void read(Reader policy)139throws ParsingException, IOException140{141if (!(policy instanceof BufferedReader)) {142policy = new BufferedReader(policy);143}144145/**146* Configure the stream tokenizer:147* Recognize strings between "..."148* Don't convert words to lowercase149* Recognize both C-style and C++-style comments150* Treat end-of-line as white space, not as a token151*/152st = new StreamTokenizer(policy);153154st.resetSyntax();155st.wordChars('a', 'z');156st.wordChars('A', 'Z');157st.wordChars('.', '.');158st.wordChars('0', '9');159st.wordChars('_', '_');160st.wordChars('$', '$');161st.wordChars(128 + 32, 255);162st.whitespaceChars(0, ' ');163st.commentChar('/');164st.quoteChar('\'');165st.quoteChar('"');166st.lowerCaseMode(false);167st.ordinaryChar('/');168st.slashSlashComments(true);169st.slashStarComments(true);170171/**172* The main parsing loop. The loop is executed once173* for each entry in the config file. The entries174* are delimited by semicolons. Once we've read in175* the information for an entry, go ahead and try to176* add it to the policy vector.177*178*/179180lookahead = st.nextToken();181GrantEntry ge = null;182while (lookahead != StreamTokenizer.TT_EOF) {183if (peek("grant")) {184ge = parseGrantEntry();185// could be null if we couldn't expand a property186if (ge != null)187add(ge);188} else if (peek("keystore") && keyStoreUrlString==null) {189// only one keystore entry per policy file, others will be190// ignored191parseKeyStoreEntry();192} else if (peek("keystorePasswordURL") && storePassURL==null) {193// only one keystore passwordURL per policy file, others will be194// ignored195parseStorePassURL();196} else if (ge == null && keyStoreUrlString == null &&197storePassURL == null && peek("domain")) {198if (domainEntries == null) {199domainEntries = new TreeMap<>();200}201DomainEntry de = parseDomainEntry();202if (de != null) {203String domainName = de.getName();204if (!domainEntries.containsKey(domainName)) {205domainEntries.put(domainName, de);206} else {207LocalizedMessage localizedMsg = new LocalizedMessage(208"duplicate.keystore.domain.name");209Object[] source = {domainName};210String msg = "duplicate keystore domain name: " +211domainName;212throw new ParsingException(msg, localizedMsg, source);213}214}215} else {216// error?217}218match(";");219}220221if (keyStoreUrlString == null && storePassURL != null) {222throw new ParsingException(LocalizedMessage.getNonlocalized223("keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore"));224}225}226227public void add(GrantEntry ge)228{229grantEntries.addElement(ge);230}231232public void replace(GrantEntry origGe, GrantEntry newGe)233{234grantEntries.setElementAt(newGe, grantEntries.indexOf(origGe));235}236237public boolean remove(GrantEntry ge)238{239return grantEntries.removeElement(ge);240}241242/**243* Returns the (possibly expanded) keystore location, or null if the244* expansion fails.245*/246public String getKeyStoreUrl() {247try {248if (keyStoreUrlString!=null && keyStoreUrlString.length()!=0) {249return expand(keyStoreUrlString, true).replace250(File.separatorChar, '/');251}252} catch (PropertyExpander.ExpandException peee) {253if (debug != null) {254debug.println(peee.toString());255}256return null;257}258return null;259}260261public void setKeyStoreUrl(String url) {262keyStoreUrlString = url;263}264265public String getKeyStoreType() {266return keyStoreType;267}268269public void setKeyStoreType(String type) {270keyStoreType = type;271}272273public String getKeyStoreProvider() {274return keyStoreProvider;275}276277public void setKeyStoreProvider(String provider) {278keyStoreProvider = provider;279}280281public String getStorePassURL() {282try {283if (storePassURL!=null && storePassURL.length()!=0) {284return expand(storePassURL, true).replace285(File.separatorChar, '/');286}287} catch (PropertyExpander.ExpandException peee) {288if (debug != null) {289debug.println(peee.toString());290}291return null;292}293return null;294}295296public void setStorePassURL(String storePassURL) {297this.storePassURL = storePassURL;298}299300/**301* Enumerate all the entries in the global policy object.302* This method is used by policy admin tools. The tools303* should use the Enumeration methods on the returned object304* to fetch the elements sequentially.305*/306public Enumeration<GrantEntry> grantElements(){307return grantEntries.elements();308}309310public Collection<DomainEntry> getDomainEntries() {311return domainEntries.values();312}313314/**315* write out the policy316*/317318public void write(Writer policy)319{320PrintWriter out = new PrintWriter(new BufferedWriter(policy));321322Enumeration<GrantEntry> enum_ = grantElements();323324out.println("/* AUTOMATICALLY GENERATED ON "+325(new java.util.Date()) + "*/");326out.println("/* DO NOT EDIT */");327out.println();328329// write the (unexpanded) keystore entry as the first entry of the330// policy file331if (keyStoreUrlString != null) {332writeKeyStoreEntry(out);333}334if (storePassURL != null) {335writeStorePassURL(out);336}337338// write "grant" entries339while (enum_.hasMoreElements()) {340GrantEntry ge = enum_.nextElement();341ge.write(out);342out.println();343}344out.flush();345}346347/**348* parses a keystore entry349*/350private void parseKeyStoreEntry() throws ParsingException, IOException {351match("keystore");352keyStoreUrlString = match("quoted string");353354// parse keystore type355if (!peek(",")) {356return; // default type357}358match(",");359360if (peek("\"")) {361keyStoreType = match("quoted string");362} else {363throw new ParsingException(st.lineno(),364LocalizedMessage.getNonlocalized("expected.keystore.type"));365}366367// parse keystore provider368if (!peek(",")) {369return; // provider optional370}371match(",");372373if (peek("\"")) {374keyStoreProvider = match("quoted string");375} else {376throw new ParsingException(st.lineno(),377LocalizedMessage.getNonlocalized("expected.keystore.provider"));378}379}380381private void parseStorePassURL() throws ParsingException, IOException {382match("keyStorePasswordURL");383storePassURL = match("quoted string");384}385386/**387* writes the (unexpanded) keystore entry388*/389private void writeKeyStoreEntry(PrintWriter out) {390out.print("keystore \"");391out.print(keyStoreUrlString);392out.print('"');393if (keyStoreType != null && !keyStoreType.isEmpty())394out.print(", \"" + keyStoreType + "\"");395if (keyStoreProvider != null && !keyStoreProvider.isEmpty())396out.print(", \"" + keyStoreProvider + "\"");397out.println(";");398out.println();399}400401private void writeStorePassURL(PrintWriter out) {402out.print("keystorePasswordURL \"");403out.print(storePassURL);404out.print('"');405out.println(";");406out.println();407}408409/**410* parse a Grant entry411*/412private GrantEntry parseGrantEntry()413throws ParsingException, IOException414{415GrantEntry e = new GrantEntry();416LinkedList<PrincipalEntry> principals = null;417boolean ignoreEntry = false;418419match("grant");420421while(!peek("{")) {422423if (peekAndMatch("Codebase")) {424if (e.codeBase != null)425throw new ParsingException(426st.lineno(),427LocalizedMessage.getNonlocalized428("multiple.Codebase.expressions"));429e.codeBase = match("quoted string");430peekAndMatch(",");431} else if (peekAndMatch("SignedBy")) {432if (e.signedBy != null)433throw new ParsingException(434st.lineno(),435LocalizedMessage.getNonlocalized436("multiple.SignedBy.expressions"));437e.signedBy = match("quoted string");438439// verify syntax of the aliases440StringTokenizer aliases = new StringTokenizer(e.signedBy,441",", true);442int actr = 0;443int cctr = 0;444while (aliases.hasMoreTokens()) {445String alias = aliases.nextToken().trim();446if (alias.equals(","))447cctr++;448else if (!alias.isEmpty())449actr++;450}451if (actr <= cctr)452throw new ParsingException(453st.lineno(),454LocalizedMessage.getNonlocalized455("SignedBy.has.empty.alias"));456457peekAndMatch(",");458} else if (peekAndMatch("Principal")) {459if (principals == null) {460principals = new LinkedList<>();461}462463String principalClass;464String principalName;465466if (peek("\"")) {467// both the principalClass and principalName468// will be replaced later469principalClass = PrincipalEntry.REPLACE_NAME;470principalName = match("principal type");471} else {472// check for principalClass wildcard473if (peek("*")) {474match("*");475principalClass = PrincipalEntry.WILDCARD_CLASS;476} else {477principalClass = match("principal type");478}479480// check for principalName wildcard481if (peek("*")) {482match("*");483principalName = PrincipalEntry.WILDCARD_NAME;484} else {485principalName = match("quoted string");486}487488// disallow WILDCARD_CLASS && actual name489if (principalClass.equals(PrincipalEntry.WILDCARD_CLASS) &&490!principalName.equals(PrincipalEntry.WILDCARD_NAME)) {491if (debug != null) {492debug.println("disallowing principal that " +493"has WILDCARD class but no WILDCARD name");494}495throw new ParsingException496(st.lineno(),497LocalizedMessage.getNonlocalized498("can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name"));499}500}501502try {503principalName = expand(principalName);504505if (principalClass.equals506("javax.security.auth.x500.X500Principal") &&507!principalName.equals(PrincipalEntry.WILDCARD_NAME)) {508509// 4702543: X500 names with an EmailAddress510// were encoded incorrectly. construct a new511// X500Principal with correct encoding.512513X500Principal p = new X500Principal514((new X500Principal(principalName)).toString());515principalName = p.getName();516}517518principals.add519(new PrincipalEntry(principalClass, principalName));520} catch (PropertyExpander.ExpandException peee) {521// ignore the entire policy entry522// but continue parsing all the info523// so we can get to the next entry524if (debug != null) {525debug.println("principal name expansion failed: " +526principalName);527}528ignoreEntry = true;529}530peekAndMatch(",");531532} else {533throw new ParsingException(st.lineno(),534LocalizedMessage.getNonlocalized535("expected.codeBase.or.SignedBy.or.Principal"));536}537}538539if (principals != null) e.principals = principals;540match("{");541542while(!peek("}")) {543if (peek("Permission")) {544try {545PermissionEntry pe = parsePermissionEntry();546e.add(pe);547} catch (PropertyExpander.ExpandException peee) {548// ignore. The add never happened549if (debug != null) {550debug.println(peee.toString());551}552skipEntry(); // BugId 4219343553}554match(";");555} else {556throw new557ParsingException(st.lineno(),558LocalizedMessage.getNonlocalized559("expected.permission.entry"));560}561}562match("}");563564try {565if (e.signedBy != null) e.signedBy = expand(e.signedBy);566if (e.codeBase != null) {567e.codeBase = expand(e.codeBase, true).replace568(File.separatorChar, '/');569}570} catch (PropertyExpander.ExpandException peee) {571if (debug != null) {572debug.println(peee.toString());573}574return null;575}576577return (ignoreEntry == true) ? null : e;578}579580/**581* parse a Permission entry582*/583private PermissionEntry parsePermissionEntry()584throws ParsingException, IOException, PropertyExpander.ExpandException585{586PermissionEntry e = new PermissionEntry();587588// Permission589match("Permission");590e.permission = match("permission type");591592if (peek("\"")) {593// Permission name594e.name = expand(match("quoted string"));595}596597if (!peek(",")) {598return e;599}600match(",");601602if (peek("\"")) {603e.action = expand(match("quoted string"));604if (!peek(",")) {605return e;606}607match(",");608}609610if (peekAndMatch("SignedBy")) {611e.signedBy = expand(match("quoted string"));612}613return e;614}615616/**617* parse a domain entry618*/619private DomainEntry parseDomainEntry()620throws ParsingException, IOException621{622boolean ignoreEntry = false;623DomainEntry domainEntry;624String name = null;625Map<String, String> properties = new HashMap<>();626627match("domain");628name = match("domain name");629630while(!peek("{")) {631// get the domain properties632properties = parseProperties("{");633}634match("{");635domainEntry = new DomainEntry(name, properties);636637while(!peek("}")) {638639match("keystore");640name = match("keystore name");641// get the keystore properties642if (!peek("}")) {643properties = parseProperties(";");644}645match(";");646domainEntry.add(new KeyStoreEntry(name, properties));647}648match("}");649650return (ignoreEntry == true) ? null : domainEntry;651}652653/*654* Return a collection of domain properties or keystore properties.655*/656private Map<String, String> parseProperties(String terminator)657throws ParsingException, IOException {658659Map<String, String> properties = new HashMap<>();660String key;661String value;662while (!peek(terminator)) {663key = match("property name");664match("=");665666try {667value = expand(match("quoted string"));668} catch (PropertyExpander.ExpandException peee) {669throw new IOException(peee.getLocalizedMessage());670}671properties.put(key.toLowerCase(Locale.ENGLISH), value);672}673674return properties;675}676677private boolean peekAndMatch(String expect)678throws ParsingException, IOException679{680if (peek(expect)) {681match(expect);682return true;683} else {684return false;685}686}687688private boolean peek(String expect) {689boolean found = false;690691switch (lookahead) {692693case StreamTokenizer.TT_WORD:694if (expect.equalsIgnoreCase(st.sval))695found = true;696break;697case ',':698if (expect.equalsIgnoreCase(","))699found = true;700break;701case '{':702if (expect.equalsIgnoreCase("{"))703found = true;704break;705case '}':706if (expect.equalsIgnoreCase("}"))707found = true;708break;709case '"':710if (expect.equalsIgnoreCase("\""))711found = true;712break;713case '*':714if (expect.equalsIgnoreCase("*"))715found = true;716break;717case ';':718if (expect.equalsIgnoreCase(";"))719found = true;720break;721default:722723}724return found;725}726727private String match(String expect)728throws ParsingException, IOException729{730String value = null;731732switch (lookahead) {733case StreamTokenizer.TT_NUMBER:734throw new ParsingException(st.lineno(), expect,735LocalizedMessage.getNonlocalized("number.") +736String.valueOf(st.nval));737case StreamTokenizer.TT_EOF:738LocalizedMessage localizedMsg = new LocalizedMessage739("expected.expect.read.end.of.file.");740Object[] source = {expect};741String msg = "expected [" + expect + "], read [end of file]";742throw new ParsingException(msg, localizedMsg, source);743case StreamTokenizer.TT_WORD:744if (expect.equalsIgnoreCase(st.sval)) {745lookahead = st.nextToken();746} else if (expect.equalsIgnoreCase("permission type")) {747value = st.sval;748lookahead = st.nextToken();749} else if (expect.equalsIgnoreCase("principal type")) {750value = st.sval;751lookahead = st.nextToken();752} else if (expect.equalsIgnoreCase("domain name") ||753expect.equalsIgnoreCase("keystore name") ||754expect.equalsIgnoreCase("property name")) {755value = st.sval;756lookahead = st.nextToken();757} else {758throw new ParsingException(st.lineno(), expect,759st.sval);760}761break;762case '"':763if (expect.equalsIgnoreCase("quoted string")) {764value = st.sval;765lookahead = st.nextToken();766} else if (expect.equalsIgnoreCase("permission type")) {767value = st.sval;768lookahead = st.nextToken();769} else if (expect.equalsIgnoreCase("principal type")) {770value = st.sval;771lookahead = st.nextToken();772} else {773throw new ParsingException(st.lineno(), expect, st.sval);774}775break;776case ',':777if (expect.equalsIgnoreCase(","))778lookahead = st.nextToken();779else780throw new ParsingException(st.lineno(), expect, ",");781break;782case '{':783if (expect.equalsIgnoreCase("{"))784lookahead = st.nextToken();785else786throw new ParsingException(st.lineno(), expect, "{");787break;788case '}':789if (expect.equalsIgnoreCase("}"))790lookahead = st.nextToken();791else792throw new ParsingException(st.lineno(), expect, "}");793break;794case ';':795if (expect.equalsIgnoreCase(";"))796lookahead = st.nextToken();797else798throw new ParsingException(st.lineno(), expect, ";");799break;800case '*':801if (expect.equalsIgnoreCase("*"))802lookahead = st.nextToken();803else804throw new ParsingException(st.lineno(), expect, "*");805break;806case '=':807if (expect.equalsIgnoreCase("="))808lookahead = st.nextToken();809else810throw new ParsingException(st.lineno(), expect, "=");811break;812default:813throw new ParsingException(st.lineno(), expect,814String.valueOf((char)lookahead));815}816return value;817}818819/**820* skip all tokens for this entry leaving the delimiter ";"821* in the stream.822*/823private void skipEntry() throws ParsingException, IOException {824while(lookahead != ';') {825switch (lookahead) {826case StreamTokenizer.TT_NUMBER:827throw new ParsingException(st.lineno(), ";",828LocalizedMessage.getNonlocalized("number.") +829String.valueOf(st.nval));830case StreamTokenizer.TT_EOF:831throw new ParsingException(LocalizedMessage.getNonlocalized832("expected.read.end.of.file."));833default:834lookahead = st.nextToken();835}836}837}838839/**840* Each grant entry in the policy configuration file is841* represented by a GrantEntry object.842*843* <p>844* For example, the entry845* <pre>846* grant signedBy "Duke" {847* permission java.io.FilePermission "/tmp", "read,write";848* };849*850* </pre>851* is represented internally852* <pre>853*854* pe = new PermissionEntry("java.io.FilePermission",855* "/tmp", "read,write");856*857* ge = new GrantEntry("Duke", null);858*859* ge.add(pe);860*861* </pre>862*863* @author Roland Schemers864*865* version 1.19, 05/21/98866*/867868public static class GrantEntry {869870public String signedBy;871public String codeBase;872public LinkedList<PrincipalEntry> principals;873public Vector<PermissionEntry> permissionEntries;874875public GrantEntry() {876principals = new LinkedList<PrincipalEntry>();877permissionEntries = new Vector<PermissionEntry>();878}879880public GrantEntry(String signedBy, String codeBase) {881this.codeBase = codeBase;882this.signedBy = signedBy;883principals = new LinkedList<PrincipalEntry>();884permissionEntries = new Vector<PermissionEntry>();885}886887public void add(PermissionEntry pe)888{889permissionEntries.addElement(pe);890}891892public boolean remove(PrincipalEntry pe)893{894return principals.remove(pe);895}896897public boolean remove(PermissionEntry pe)898{899return permissionEntries.removeElement(pe);900}901902public boolean contains(PrincipalEntry pe)903{904return principals.contains(pe);905}906907public boolean contains(PermissionEntry pe)908{909return permissionEntries.contains(pe);910}911912/**913* Enumerate all the permission entries in this GrantEntry.914*/915public Enumeration<PermissionEntry> permissionElements(){916return permissionEntries.elements();917}918919920public void write(PrintWriter out) {921out.print("grant");922if (signedBy != null) {923out.print(" signedBy \"");924out.print(signedBy);925out.print('"');926if (codeBase != null)927out.print(", ");928}929if (codeBase != null) {930out.print(" codeBase \"");931out.print(codeBase);932out.print('"');933if (principals != null && principals.size() > 0)934out.print(",\n");935}936if (principals != null && principals.size() > 0) {937Iterator<PrincipalEntry> pli = principals.iterator();938while (pli.hasNext()) {939out.print(" ");940PrincipalEntry pe = pli.next();941pe.write(out);942if (pli.hasNext())943out.print(",\n");944}945}946out.println(" {");947Enumeration<PermissionEntry> enum_ = permissionEntries.elements();948while (enum_.hasMoreElements()) {949PermissionEntry pe = enum_.nextElement();950out.write(" ");951pe.write(out);952}953out.println("};");954}955956public Object clone() {957GrantEntry ge = new GrantEntry();958ge.codeBase = this.codeBase;959ge.signedBy = this.signedBy;960ge.principals = new LinkedList<PrincipalEntry>(this.principals);961ge.permissionEntries =962new Vector<PermissionEntry>(this.permissionEntries);963return ge;964}965}966967/**968* Principal info (class and name) in a grant entry969*/970public static class PrincipalEntry implements Principal {971972public static final String WILDCARD_CLASS = "WILDCARD_PRINCIPAL_CLASS";973public static final String WILDCARD_NAME = "WILDCARD_PRINCIPAL_NAME";974public static final String REPLACE_NAME = "PolicyParser.REPLACE_NAME";975976String principalClass;977String principalName;978979/**980* A PrincipalEntry consists of the Principal class and Principal name.981*982* @param principalClass the Principal class983* @param principalName the Principal name984* @throws NullPointerException if principalClass or principalName985* are null986*/987public PrincipalEntry(String principalClass, String principalName) {988if (principalClass == null || principalName == null)989throw new NullPointerException(LocalizedMessage.getNonlocalized990("null.principalClass.or.principalName"));991this.principalClass = principalClass;992this.principalName = principalName;993}994995boolean isWildcardName() {996return principalName.equals(WILDCARD_NAME);997}998999boolean isWildcardClass() {1000return principalClass.equals(WILDCARD_CLASS);1001}10021003boolean isReplaceName() {1004return principalClass.equals(REPLACE_NAME);1005}10061007public String getPrincipalClass() {1008return principalClass;1009}10101011public String getPrincipalName() {1012return principalName;1013}10141015public String getDisplayClass() {1016if (isWildcardClass()) {1017return "*";1018} else if (isReplaceName()) {1019return "";1020}1021else return principalClass;1022}10231024public String getDisplayName() {1025return getDisplayName(false);1026}10271028public String getDisplayName(boolean addQuote) {1029if (isWildcardName()) {1030return "*";1031}1032else {1033if (addQuote) return "\"" + principalName + "\"";1034else return principalName;1035}1036}10371038@Override1039public String getName() {1040return principalName;1041}10421043@Override1044public String toString() {1045if (!isReplaceName()) {1046return getDisplayClass() + "/" + getDisplayName();1047} else {1048return getDisplayName();1049}1050}10511052/**1053* Test for equality between the specified object and this object.1054* Two PrincipalEntries are equal if their class and name values1055* are equal.1056*1057* @param obj the object to test for equality with this object1058* @return true if the objects are equal, false otherwise1059*/1060@Override1061public boolean equals(Object obj) {1062if (this == obj)1063return true;10641065if (!(obj instanceof PrincipalEntry))1066return false;10671068PrincipalEntry that = (PrincipalEntry)obj;1069return (principalClass.equals(that.principalClass) &&1070principalName.equals(that.principalName));1071}10721073/**1074* Return a hashcode for this PrincipalEntry.1075*1076* @return a hashcode for this PrincipalEntry1077*/1078@Override1079public int hashCode() {1080return principalClass.hashCode();1081}10821083public void write(PrintWriter out) {1084out.print("principal " + getDisplayClass() + " " +1085getDisplayName(true));1086}1087}10881089/**1090* Each permission entry in the policy configuration file is1091* represented by a1092* PermissionEntry object.1093*1094* <p>1095* For example, the entry1096* <pre>1097* permission java.io.FilePermission "/tmp", "read,write";1098* </pre>1099* is represented internally1100* <pre>1101*1102* pe = new PermissionEntry("java.io.FilePermission",1103* "/tmp", "read,write");1104* </pre>1105*1106* @author Roland Schemers1107*1108* version 1.19, 05/21/981109*/11101111public static class PermissionEntry {11121113public String permission;1114public String name;1115public String action;1116public String signedBy;11171118public PermissionEntry() {1119}11201121public PermissionEntry(String permission,1122String name,1123String action) {1124this.permission = permission;1125this.name = name;1126this.action = action;1127}11281129/**1130* Calculates a hash code value for the object. Objects1131* which are equal will also have the same hashcode.1132*/1133@Override1134public int hashCode() {1135int retval = permission.hashCode();1136if (name != null) retval ^= name.hashCode();1137if (action != null) retval ^= action.hashCode();1138return retval;1139}11401141@Override1142public boolean equals(Object obj) {1143if (obj == this)1144return true;11451146if (! (obj instanceof PermissionEntry))1147return false;11481149PermissionEntry that = (PermissionEntry) obj;11501151if (this.permission == null) {1152if (that.permission != null) return false;1153} else {1154if (!this.permission.equals(that.permission)) return false;1155}11561157if (this.name == null) {1158if (that.name != null) return false;1159} else {1160if (!this.name.equals(that.name)) return false;1161}11621163if (this.action == null) {1164if (that.action != null) return false;1165} else {1166if (!this.action.equals(that.action)) return false;1167}11681169if (this.signedBy == null) {1170if (that.signedBy != null) return false;1171} else {1172if (!this.signedBy.equals(that.signedBy)) return false;1173}11741175// everything matched -- the 2 objects are equal1176return true;1177}11781179public void write(PrintWriter out) {1180out.print("permission ");1181out.print(permission);1182if (name != null) {1183out.print(" \"");11841185// ATTENTION: regex with double escaping,1186// the normal forms look like:1187// $name =~ s/\\/\\\\/g; and1188// $name =~ s/\"/\\\"/g;1189// and then in a java string, it's escaped again11901191out.print(name.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\\"", "\\\\\\\""));1192out.print('"');1193}1194if (action != null) {1195out.print(", \"");1196out.print(action);1197out.print('"');1198}1199if (signedBy != null) {1200out.print(", signedBy \"");1201out.print(signedBy);1202out.print('"');1203}1204out.println(";");1205}1206}12071208/**1209* Each domain entry in the keystore domain configuration file is1210* represented by a DomainEntry object.1211*/1212static class DomainEntry {1213private final String name;1214private final Map<String, String> properties;1215private final Map<String, KeyStoreEntry> entries;12161217DomainEntry(String name, Map<String, String> properties) {1218this.name = name;1219this.properties = properties;1220entries = new HashMap<>();1221}12221223String getName() {1224return name;1225}12261227Map<String, String> getProperties() {1228return properties;1229}12301231Collection<KeyStoreEntry> getEntries() {1232return entries.values();1233}12341235void add(KeyStoreEntry entry) throws ParsingException {1236String keystoreName = entry.getName();1237if (!entries.containsKey(keystoreName)) {1238entries.put(keystoreName, entry);1239} else {1240LocalizedMessage localizedMsg = new LocalizedMessage1241("duplicate.keystore.name");1242Object[] source = {keystoreName};1243String msg = "duplicate keystore name: " + keystoreName;1244throw new ParsingException(msg, localizedMsg, source);1245}1246}12471248@Override1249public String toString() {1250StringBuilder s =1251new StringBuilder("\ndomain ").append(name);12521253if (properties != null) {1254for (Map.Entry<String, String> property :1255properties.entrySet()) {1256s.append("\n ").append(property.getKey()).append('=')1257.append(property.getValue());1258}1259}1260s.append(" {\n");12611262if (entries != null) {1263for (KeyStoreEntry entry : entries.values()) {1264s.append(entry).append("\n");1265}1266}1267s.append("}");12681269return s.toString();1270}1271}12721273/**1274* Each keystore entry in the keystore domain configuration file is1275* represented by a KeyStoreEntry object.1276*/12771278static class KeyStoreEntry {1279private final String name;1280private final Map<String, String> properties;12811282KeyStoreEntry(String name, Map<String, String> properties) {1283this.name = name;1284this.properties = properties;1285}12861287String getName() {1288return name;1289}12901291Map<String, String> getProperties() {1292return properties;1293}12941295@Override1296public String toString() {1297StringBuilder s = new StringBuilder("\n keystore ").append(name);1298if (properties != null) {1299for (Map.Entry<String, String> property :1300properties.entrySet()) {1301s.append("\n ").append(property.getKey()).append('=')1302.append(property.getValue());1303}1304}1305s.append(";");13061307return s.toString();1308}1309}13101311public static class ParsingException extends GeneralSecurityException {13121313@java.io.Serial1314private static final long serialVersionUID = -4330692689482574072L;13151316private String i18nMessage;1317@SuppressWarnings("serial") // Not statically typed as Serializable1318private LocalizedMessage localizedMsg;1319@SuppressWarnings("serial") // Not statically typed as Serializable1320private Object[] source;13211322/**1323* Constructs a ParsingException with the specified1324* detail message. A detail message is a String that describes1325* this particular exception, which may, for example, specify which1326* algorithm is not available.1327*1328* @param msg the detail message.1329*/1330public ParsingException(String msg) {1331super(msg);1332i18nMessage = msg;1333}13341335public ParsingException(String msg, LocalizedMessage localizedMsg,1336Object[] source) {1337super(msg);1338this.localizedMsg = localizedMsg;1339this.source = source;1340}13411342public ParsingException(int line, String msg) {1343super("line " + line + ": " + msg);1344localizedMsg = new LocalizedMessage("line.number.msg");1345source = new Object[] {line, msg};1346}13471348public ParsingException(int line, String expect, String actual) {1349super("line " + line + ": expected [" + expect +1350"], found [" + actual + "]");1351localizedMsg = new LocalizedMessage1352("line.number.expected.expect.found.actual.");1353source = new Object[] {line, expect, actual};1354}13551356public String getNonlocalizedMessage() {1357return i18nMessage != null ? i18nMessage :1358localizedMsg.formatNonlocalized(source);1359}1360}13611362public static void main(String[] arg) throws Exception {1363try (FileReader fr = new FileReader(arg[0]);1364FileWriter fw = new FileWriter(arg[1])) {1365PolicyParser pp = new PolicyParser(true);1366pp.read(fr);1367pp.write(fw);1368}1369}1370}137113721373