Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/crypto/CryptoPolicyParser.java
38829 views
/*1* Copyright (c) 1999, 2012, 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 javax.crypto;2627import java.io.*;28import java.util.Enumeration;29import java.util.Hashtable;30import java.util.Vector;31import static java.util.Locale.ENGLISH;3233import java.security.GeneralSecurityException;34import java.security.spec.AlgorithmParameterSpec;35import java.lang.reflect.*;3637/**38* JCE has two pairs of jurisdiction policy files: one represents U.S. export39* laws, and the other represents the local laws of the country where the40* JCE will be used.41*42* The jurisdiction policy file has the same syntax as JDK policy files except43* that JCE has new permission classes called javax.crypto.CryptoPermission44* and javax.crypto.CryptoAllPermission.45*46* The format of a permission entry in the jurisdiction policy file is:47*48* permission <crypto permission class name>[, <algorithm name>49* [[, <exemption mechanism name>][, <maxKeySize>50* [, <AlgrithomParameterSpec class name>, <parameters51* for constructing an AlgrithomParameterSpec object>]]]];52*53* @author Sharon Liu54*55* @see java.security.Permissions56* @see java.security.spec.AlgorithmParameterSpec57* @see javax.crypto.CryptoPermission58* @see javax.crypto.CryptoAllPermission59* @see javax.crypto.CryptoPermissions60* @since 1.461*/6263final class CryptoPolicyParser {6465private Vector<GrantEntry> grantEntries;6667// Convenience variables for parsing68private StreamTokenizer st;69private int lookahead;7071/**72* Creates a CryptoPolicyParser object.73*/74CryptoPolicyParser() {75grantEntries = new Vector<GrantEntry>();76}7778/**79* Reads a policy configuration using a Reader object. <p>80*81* @param policy the policy Reader object.82*83* @exception ParsingException if the policy configuration84* contains a syntax error.85*86* @exception IOException if an error occurs while reading87* the policy configuration.88*/8990void read(Reader policy)91throws ParsingException, IOException92{93if (!(policy instanceof BufferedReader)) {94policy = new BufferedReader(policy);95}9697/*98* Configure the stream tokenizer:99* Recognize strings between "..."100* Don't convert words to lowercase101* Recognize both C-style and C++-style comments102* Treat end-of-line as white space, not as a token103*/104st = new StreamTokenizer(policy);105106st.resetSyntax();107st.wordChars('a', 'z');108st.wordChars('A', 'Z');109st.wordChars('.', '.');110st.wordChars('0', '9');111st.wordChars('_', '_');112st.wordChars('$', '$');113st.wordChars(128 + 32, 255);114st.whitespaceChars(0, ' ');115st.commentChar('/');116st.quoteChar('\'');117st.quoteChar('"');118st.lowerCaseMode(false);119st.ordinaryChar('/');120st.slashSlashComments(true);121st.slashStarComments(true);122st.parseNumbers();123124/*125* The crypto jurisdiction policy must be consistent. The126* following hashtable is used for checking consistency.127*/128Hashtable<String, Vector<String>> processedPermissions = null;129130/*131* The main parsing loop. The loop is executed once for each entry132* in the policy file. The entries are delimited by semicolons. Once133* we've read in the information for an entry, go ahead and try to134* add it to the grantEntries.135*/136lookahead = st.nextToken();137while (lookahead != StreamTokenizer.TT_EOF) {138if (peek("grant")) {139GrantEntry ge = parseGrantEntry(processedPermissions);140if (ge != null)141grantEntries.addElement(ge);142} else {143throw new ParsingException(st.lineno(), "expected grant " +144"statement");145}146match(";");147}148}149150/**151* parse a Grant entry152*/153private GrantEntry parseGrantEntry(154Hashtable<String, Vector<String>> processedPermissions)155throws ParsingException, IOException156{157GrantEntry e = new GrantEntry();158159match("grant");160match("{");161162while(!peek("}")) {163if (peek("Permission")) {164CryptoPermissionEntry pe =165parsePermissionEntry(processedPermissions);166e.add(pe);167match(";");168} else {169throw new170ParsingException(st.lineno(), "expected permission entry");171}172}173match("}");174175return e;176}177178/**179* parse a CryptoPermission entry180*/181private CryptoPermissionEntry parsePermissionEntry(182Hashtable<String, Vector<String>> processedPermissions)183throws ParsingException, IOException184{185CryptoPermissionEntry e = new CryptoPermissionEntry();186187match("Permission");188e.cryptoPermission = match("permission type");189190if (e.cryptoPermission.equals("javax.crypto.CryptoAllPermission")) {191// Done with the CryptoAllPermission entry.192e.alg = CryptoAllPermission.ALG_NAME;193e.maxKeySize = Integer.MAX_VALUE;194return e;195}196197// Should see the algorithm name.198if (peek("\"")) {199// Algorithm name - always convert to upper case after parsing.200e.alg = match("quoted string").toUpperCase(ENGLISH);201} else {202// The algorithm name can be a wildcard.203if (peek("*")) {204match("*");205e.alg = CryptoPermission.ALG_NAME_WILDCARD;206} else {207throw new ParsingException(st.lineno(),208"Missing the algorithm name");209}210}211212peekAndMatch(",");213214// May see the exemption mechanism name.215if (peek("\"")) {216// Exemption mechanism name - convert to upper case too.217e.exemptionMechanism = match("quoted string").toUpperCase(ENGLISH);218}219220peekAndMatch(",");221222// Check whether this entry is consistent with other permission entries223// that have been read.224if (!isConsistent(e.alg, e.exemptionMechanism, processedPermissions)) {225throw new ParsingException(st.lineno(), "Inconsistent policy");226}227228// Should see the maxKeySize if not at the end of this entry yet.229if (peek("number")) {230e.maxKeySize = match();231} else {232if (peek("*")) {233match("*");234e.maxKeySize = Integer.MAX_VALUE;235} else {236if (!peek(";")) {237throw new ParsingException(st.lineno(),238"Missing the maximum " +239"allowable key size");240} else {241// At the end of this permission entry242e.maxKeySize = Integer.MAX_VALUE;243}244}245}246247peekAndMatch(",");248249// May see an AlgorithmParameterSpec class name.250if (peek("\"")) {251// AlgorithmParameterSpec class name.252String algParamSpecClassName = match("quoted string");253254Vector<Integer> paramsV = new Vector<>(1);255while (peek(",")) {256match(",");257if (peek("number")) {258paramsV.addElement(new Integer(match()));259} else {260if (peek("*")) {261match("*");262paramsV.addElement(new Integer(Integer.MAX_VALUE));263} else {264throw new ParsingException(st.lineno(),265"Expecting an integer");266}267}268}269270Integer[] params = new Integer[paramsV.size()];271paramsV.copyInto(params);272273e.checkParam = true;274e.algParamSpec = getInstance(algParamSpecClassName, params);275}276277return e;278}279280private static final AlgorithmParameterSpec getInstance(String type,281Integer[] params)282throws ParsingException283{284AlgorithmParameterSpec ret = null;285286try {287Class<?> apsClass = Class.forName(type);288Class<?>[] paramClasses = new Class<?>[params.length];289290for (int i = 0; i < params.length; i++) {291paramClasses[i] = int.class;292}293294Constructor<?> c = apsClass.getConstructor(paramClasses);295ret = (AlgorithmParameterSpec) c.newInstance((Object[]) params);296} catch (Exception e) {297throw new ParsingException("Cannot call the constructor of " +298type + e);299}300return ret;301}302303304private boolean peekAndMatch(String expect)305throws ParsingException, IOException306{307if (peek(expect)) {308match(expect);309return true;310}311return false;312}313314private boolean peek(String expect) {315boolean found = false;316317switch (lookahead) {318319case StreamTokenizer.TT_WORD:320if (expect.equalsIgnoreCase(st.sval))321found = true;322break;323case StreamTokenizer.TT_NUMBER:324if (expect.equalsIgnoreCase("number")) {325found = true;326}327break;328case ',':329if (expect.equals(","))330found = true;331break;332case '{':333if (expect.equals("{"))334found = true;335break;336case '}':337if (expect.equals("}"))338found = true;339break;340case '"':341if (expect.equals("\""))342found = true;343break;344case '*':345if (expect.equals("*"))346found = true;347break;348case ';':349if (expect.equals(";"))350found = true;351break;352default:353break;354}355return found;356}357358/**359* Excepts to match a non-negative number.360*/361private int match()362throws ParsingException, IOException363{364int value = -1;365int lineno = st.lineno();366String sValue = null;367368switch (lookahead) {369case StreamTokenizer.TT_NUMBER:370value = (int)st.nval;371if (value < 0) {372sValue = String.valueOf(st.nval);373}374lookahead = st.nextToken();375break;376default:377sValue = st.sval;378break;379}380if (value <= 0) {381throw new ParsingException(lineno, "a non-negative number",382sValue);383}384return value;385}386387private String match(String expect)388throws ParsingException, IOException389{390String value = null;391392switch (lookahead) {393case StreamTokenizer.TT_NUMBER:394throw new ParsingException(st.lineno(), expect,395"number "+String.valueOf(st.nval));396case StreamTokenizer.TT_EOF:397throw new ParsingException("expected "+expect+", read end of file");398case StreamTokenizer.TT_WORD:399if (expect.equalsIgnoreCase(st.sval)) {400lookahead = st.nextToken();401}402else if (expect.equalsIgnoreCase("permission type")) {403value = st.sval;404lookahead = st.nextToken();405}406else407throw new ParsingException(st.lineno(), expect, st.sval);408break;409case '"':410if (expect.equalsIgnoreCase("quoted string")) {411value = st.sval;412lookahead = st.nextToken();413} else if (expect.equalsIgnoreCase("permission type")) {414value = st.sval;415lookahead = st.nextToken();416}417else418throw new ParsingException(st.lineno(), expect, st.sval);419break;420case ',':421if (expect.equals(","))422lookahead = st.nextToken();423else424throw new ParsingException(st.lineno(), expect, ",");425break;426case '{':427if (expect.equals("{"))428lookahead = st.nextToken();429else430throw new ParsingException(st.lineno(), expect, "{");431break;432case '}':433if (expect.equals("}"))434lookahead = st.nextToken();435else436throw new ParsingException(st.lineno(), expect, "}");437break;438case ';':439if (expect.equals(";"))440lookahead = st.nextToken();441else442throw new ParsingException(st.lineno(), expect, ";");443break;444case '*':445if (expect.equals("*"))446lookahead = st.nextToken();447else448throw new ParsingException(st.lineno(), expect, "*");449break;450default:451throw new ParsingException(st.lineno(), expect,452new String(new char[] {(char)lookahead}));453}454return value;455}456457CryptoPermission[] getPermissions() {458Vector<CryptoPermission> result = new Vector<>();459460Enumeration<GrantEntry> grantEnum = grantEntries.elements();461while (grantEnum.hasMoreElements()) {462GrantEntry ge = grantEnum.nextElement();463Enumeration<CryptoPermissionEntry> permEnum =464ge.permissionElements();465while (permEnum.hasMoreElements()) {466CryptoPermissionEntry pe = permEnum.nextElement();467if (pe.cryptoPermission.equals(468"javax.crypto.CryptoAllPermission")) {469result.addElement(CryptoAllPermission.INSTANCE);470} else {471if (pe.checkParam) {472result.addElement(new CryptoPermission(473pe.alg,474pe.maxKeySize,475pe.algParamSpec,476pe.exemptionMechanism));477} else {478result.addElement(new CryptoPermission(479pe.alg,480pe.maxKeySize,481pe.exemptionMechanism));482}483}484}485}486487CryptoPermission[] ret = new CryptoPermission[result.size()];488result.copyInto(ret);489490return ret;491}492493private boolean isConsistent(String alg, String exemptionMechanism,494Hashtable<String, Vector<String>> processedPermissions) {495String thisExemptionMechanism =496exemptionMechanism == null ? "none" : exemptionMechanism;497498if (processedPermissions == null) {499processedPermissions = new Hashtable<String, Vector<String>>();500Vector<String> exemptionMechanisms = new Vector<>(1);501exemptionMechanisms.addElement(thisExemptionMechanism);502processedPermissions.put(alg, exemptionMechanisms);503return true;504}505506if (processedPermissions.containsKey(CryptoAllPermission.ALG_NAME)) {507return false;508}509510Vector<String> exemptionMechanisms;511512if (processedPermissions.containsKey(alg)) {513exemptionMechanisms = processedPermissions.get(alg);514if (exemptionMechanisms.contains(thisExemptionMechanism)) {515return false;516}517} else {518exemptionMechanisms = new Vector<String>(1);519}520521exemptionMechanisms.addElement(thisExemptionMechanism);522processedPermissions.put(alg, exemptionMechanisms);523return true;524}525526/**527* Each grant entry in the policy configuration file is represented by a528* GrantEntry object. <p>529*530* <p>531* For example, the entry532* <pre>533* grant {534* permission javax.crypto.CryptoPermission "DES", 56;535* };536*537* </pre>538* is represented internally539* <pre>540*541* pe = new CryptoPermissionEntry("javax.crypto.CryptoPermission",542* "DES", 56);543*544* ge = new GrantEntry();545*546* ge.add(pe);547*548* </pre>549*550* @see java.security.Permission551* @see javax.crypto.CryptoPermission552* @see javax.crypto.CryptoPermissions553*/554555private static class GrantEntry {556557private Vector<CryptoPermissionEntry> permissionEntries;558559GrantEntry() {560permissionEntries = new Vector<CryptoPermissionEntry>();561}562563void add(CryptoPermissionEntry pe)564{565permissionEntries.addElement(pe);566}567568boolean remove(CryptoPermissionEntry pe)569{570return permissionEntries.removeElement(pe);571}572573boolean contains(CryptoPermissionEntry pe)574{575return permissionEntries.contains(pe);576}577578/**579* Enumerate all the permission entries in this GrantEntry.580*/581Enumeration<CryptoPermissionEntry> permissionElements(){582return permissionEntries.elements();583}584585}586587/**588* Each crypto permission entry in the policy configuration file is589* represented by a CryptoPermissionEntry object. <p>590*591* <p>592* For example, the entry593* <pre>594* permission javax.crypto.CryptoPermission "DES", 56;595* </pre>596* is represented internally597* <pre>598*599* pe = new CryptoPermissionEntry("javax.crypto.cryptoPermission",600* "DES", 56);601* </pre>602*603* @see java.security.Permissions604* @see javax.crypto.CryptoPermission605* @see javax.crypto.CryptoAllPermission606*/607608private static class CryptoPermissionEntry {609610String cryptoPermission;611String alg;612String exemptionMechanism;613int maxKeySize;614boolean checkParam;615AlgorithmParameterSpec algParamSpec;616617CryptoPermissionEntry() {618// Set default values.619maxKeySize = 0;620alg = null;621exemptionMechanism = null;622checkParam = false;623algParamSpec = null;624}625626/**627* Calculates a hash code value for the object. Objects628* which are equal will also have the same hashcode.629*/630public int hashCode() {631int retval = cryptoPermission.hashCode();632if (alg != null) retval ^= alg.hashCode();633if (exemptionMechanism != null) {634retval ^= exemptionMechanism.hashCode();635}636retval ^= maxKeySize;637if (checkParam) retval ^= 100;638if (algParamSpec != null) {639retval ^= algParamSpec.hashCode();640}641return retval;642}643644public boolean equals(Object obj) {645if (obj == this)646return true;647648if (!(obj instanceof CryptoPermissionEntry))649return false;650651CryptoPermissionEntry that = (CryptoPermissionEntry) obj;652653if (this.cryptoPermission == null) {654if (that.cryptoPermission != null) return false;655} else {656if (!this.cryptoPermission.equals(657that.cryptoPermission))658return false;659}660661if (this.alg == null) {662if (that.alg != null) return false;663} else {664if (!this.alg.equalsIgnoreCase(that.alg))665return false;666}667668if (!(this.maxKeySize == that.maxKeySize)) return false;669670if (this.checkParam != that.checkParam) return false;671672if (this.algParamSpec == null) {673if (that.algParamSpec != null) return false;674} else {675if (!this.algParamSpec.equals(that.algParamSpec))676return false;677}678679// everything matched -- the 2 objects are equal680return true;681}682}683684static final class ParsingException extends GeneralSecurityException {685686private static final long serialVersionUID = 7147241245566588374L;687688/**689* Constructs a ParsingException with the specified690* detail message.691* @param msg the detail message.692*/693ParsingException(String msg) {694super(msg);695}696697ParsingException(int line, String msg) {698super("line " + line + ": " + msg);699}700701ParsingException(int line, String expect, String actual) {702super("line "+line+": expected '"+expect+"', found '"+actual+"'");703}704}705}706707708