/* * Copyright (c) 1999, 2007, 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 javax.crypto; import java.io.*; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import java.util.StringTokenizer; import static java.util.Locale.ENGLISH; import java.security.GeneralSecurityException; import java.security.spec.AlgorithmParameterSpec; import java.lang.reflect.*; /** * JCE has two pairs of jurisdiction policy files: one represents U.S. export * laws, and the other represents the local laws of the country where the * JCE will be used. * * The jurisdiction policy file has the same syntax as JDK policy files except * that JCE has new permission classes called javax.crypto.CryptoPermission * and javax.crypto.CryptoAllPermission. * * The format of a permission entry in the jurisdiction policy file is: * * permission [, * [[, ][, * [, , ]]]]; * * @author Sharon Liu * * @see java.security.Permissions * @see java.security.spec.AlgrithomParameterSpec * @see javax.crypto.CryptoPermission * @see javax.crypto.CryptoAllPermission * @see javax.crypto.CryptoPermissions * @since 1.4 */ final class CryptoPolicyParser { private Vector grantEntries; // Convenience variables for parsing private StreamTokenizer st; private int lookahead; /** * Creates a CryptoPolicyParser object. */ CryptoPolicyParser() { grantEntries = new Vector(); } /** * Reads a policy configuration using a Reader object.

* * @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. */ 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); st.parseNumbers(); /* * The crypto jurisdiction policy must be consistent. The * following hashtable is used for checking consistency. */ Hashtable processedPermissions = null; /* * The main parsing loop. The loop is executed once for each entry * in the policy 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 grantEntries. */ lookahead = st.nextToken(); while (lookahead != StreamTokenizer.TT_EOF) { if (peek("grant")) { GrantEntry ge = parseGrantEntry(processedPermissions); if (ge != null) grantEntries.addElement(ge); } else { throw new ParsingException(st.lineno(), "expected grant " + "statement"); } match(";"); } } /** * parse a Grant entry */ private GrantEntry parseGrantEntry(Hashtable processedPermissions) throws ParsingException, IOException { GrantEntry e = new GrantEntry(); match("grant"); match("{"); while(!peek("}")) { if (peek("Permission")) { CryptoPermissionEntry pe = parsePermissionEntry(processedPermissions); e.add(pe); match(";"); } else { throw new ParsingException(st.lineno(), "expected permission entry"); } } match("}"); return e; } /** * parse a CryptoPermission entry */ private CryptoPermissionEntry parsePermissionEntry( Hashtable processedPermissions) throws ParsingException, IOException { CryptoPermissionEntry e = new CryptoPermissionEntry(); match("Permission"); e.cryptoPermission = match("permission type"); if (e.cryptoPermission.equals("javax.crypto.CryptoAllPermission")) { // Done with the CryptoAllPermission entry. e.alg = CryptoAllPermission.ALG_NAME; e.maxKeySize = Integer.MAX_VALUE; return e; } // Should see the algorithm name. if (peek("\"")) { // Algorithm name - always convert to upper case after parsing. e.alg = match("quoted string").toUpperCase(ENGLISH); } else { // The algorithm name can be a wildcard. if (peek("*")) { match("*"); e.alg = CryptoPermission.ALG_NAME_WILDCARD; } else { throw new ParsingException(st.lineno(), "Missing the algorithm name"); } } peekAndMatch(","); // May see the exemption mechanism name. if (peek("\"")) { // Exemption mechanism name - convert to upper case too. e.exemptionMechanism = match("quoted string").toUpperCase(ENGLISH); } peekAndMatch(","); // Check whether this entry is consistent with other permission entries // that have been read. if (!isConsistent(e.alg, e.exemptionMechanism, processedPermissions)) { throw new ParsingException(st.lineno(), "Inconsistent policy"); } // Should see the maxKeySize if not at the end of this entry yet. if (peek("number")) { e.maxKeySize = match(); } else { if (peek("*")) { match("*"); e.maxKeySize = Integer.MAX_VALUE; } else { if (!peek(";")) { throw new ParsingException(st.lineno(), "Missing the maximum " + "allowable key size"); } else { // At the end of this permission entry e.maxKeySize = Integer.MAX_VALUE; } } } peekAndMatch(","); // May see an AlgorithmParameterSpec class name. if (peek("\"")) { // AlgorithmParameterSpec class name. String algParamSpecClassName = match("quoted string"); Vector paramsV = new Vector(1); while (peek(",")) { match(","); if (peek("number")) { paramsV.addElement(new Integer(match())); } else { if (peek("*")) { match("*"); paramsV.addElement(new Integer(Integer.MAX_VALUE)); } else { throw new ParsingException(st.lineno(), "Expecting an integer"); } } } Integer[] params = new Integer[paramsV.size()]; paramsV.copyInto(params); e.checkParam = true; e.algParamSpec = getInstance(algParamSpecClassName, params); } return e; } private static final AlgorithmParameterSpec getInstance(String type, Integer[] params) throws ParsingException { AlgorithmParameterSpec ret = null; try { Class apsClass = Class.forName(type); Class[] paramClasses = new Class[params.length]; for (int i = 0; i < params.length; i++) { paramClasses[i] = int.class; } Constructor c = apsClass.getConstructor(paramClasses); ret = (AlgorithmParameterSpec) c.newInstance((Object[]) params); } catch (Exception e) { throw new ParsingException("Cannot call the constructor of " + type + e); } return ret; } private boolean peekAndMatch(String expect) throws ParsingException, IOException { if (peek(expect)) { match(expect); return true; } 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 StreamTokenizer.TT_NUMBER: if (expect.equalsIgnoreCase("number")) { found = true; } break; case ',': if (expect.equals(",")) found = true; break; case '{': if (expect.equals("{")) found = true; break; case '}': if (expect.equals("}")) found = true; break; case '"': if (expect.equals("\"")) found = true; break; case '*': if (expect.equals("*")) found = true; break; case ';': if (expect.equals(";")) found = true; break; default: break; } return found; } /** * Excepts to match a non-negative number. */ private int match() throws ParsingException, IOException { int value = -1; int lineno = st.lineno(); String sValue = null; switch (lookahead) { case StreamTokenizer.TT_NUMBER: value = (int)st.nval; if (value < 0) { sValue = String.valueOf(st.nval); } lookahead = st.nextToken(); break; default: sValue = st.sval; break; } if (value <= 0) { throw new ParsingException(lineno, "a non-negative number", sValue); } return value; } private String match(String expect) throws ParsingException, IOException { String value = null; switch (lookahead) { case StreamTokenizer.TT_NUMBER: throw new ParsingException(st.lineno(), expect, "number "+String.valueOf(st.nval)); case StreamTokenizer.TT_EOF: throw new ParsingException("expected "+expect+", read end of file"); 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 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 throw new ParsingException(st.lineno(), expect, st.sval); break; case ',': if (expect.equals(",")) lookahead = st.nextToken(); else throw new ParsingException(st.lineno(), expect, ","); break; case '{': if (expect.equals("{")) lookahead = st.nextToken(); else throw new ParsingException(st.lineno(), expect, "{"); break; case '}': if (expect.equals("}")) lookahead = st.nextToken(); else throw new ParsingException(st.lineno(), expect, "}"); break; case ';': if (expect.equals(";")) lookahead = st.nextToken(); else throw new ParsingException(st.lineno(), expect, ";"); break; case '*': if (expect.equals("*")) 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; } CryptoPermission[] getPermissions() { Vector result = new Vector(); Enumeration grantEnum = grantEntries.elements(); while (grantEnum.hasMoreElements()) { GrantEntry ge = (GrantEntry)grantEnum.nextElement(); Enumeration permEnum = ge.permissionElements(); while (permEnum.hasMoreElements()) { CryptoPermissionEntry pe = (CryptoPermissionEntry)permEnum.nextElement(); if (pe.cryptoPermission.equals( "javax.crypto.CryptoAllPermission")) { result.addElement(CryptoAllPermission.INSTANCE); } else { if (pe.checkParam) { result.addElement(new CryptoPermission( pe.alg, pe.maxKeySize, pe.algParamSpec, pe.exemptionMechanism)); } else { result.addElement(new CryptoPermission( pe.alg, pe.maxKeySize, pe.exemptionMechanism)); } } } } CryptoPermission[] ret = new CryptoPermission[result.size()]; result.copyInto(ret); return ret; } private boolean isConsistent(String alg, String exemptionMechanism, Hashtable processedPermissions) { String thisExemptionMechanism = exemptionMechanism == null ? "none" : exemptionMechanism; if (processedPermissions == null) { processedPermissions = new Hashtable(); Vector exemptionMechanisms = new Vector(1); exemptionMechanisms.addElement(thisExemptionMechanism); processedPermissions.put(alg, exemptionMechanisms); return true; } if (processedPermissions.containsKey(CryptoAllPermission.ALG_NAME)) { return false; } Vector exemptionMechanisms; if (processedPermissions.containsKey(alg)) { exemptionMechanisms = (Vector)processedPermissions.get(alg); if (exemptionMechanisms.contains(thisExemptionMechanism)) { return false; } } else { exemptionMechanisms = new Vector(1); } exemptionMechanisms.addElement(thisExemptionMechanism); processedPermissions.put(alg, exemptionMechanisms); return true; } /** * Each grant entry in the policy configuration file is represented by a * GrantEntry object.

* *

* For example, the entry *

     *      grant {
     *       permission javax.crypto.CryptoPermission "DES", 56;
     *      };
     *
     * 
* is represented internally *
     *
     * pe = new CryptoPermissionEntry("javax.crypto.CryptoPermission",
     *                           "DES", 56);
     *
     * ge = new GrantEntry();
     *
     * ge.add(pe);
     *
     * 
* * @see java.security.Permission * @see javax.crypto.CryptoPermission * @see javax.crypto.CryptoPermissions */ private static class GrantEntry { private Vector permissionEntries; GrantEntry() { permissionEntries = new Vector(); } void add(CryptoPermissionEntry pe) { permissionEntries.addElement(pe); } boolean remove(CryptoPermissionEntry pe) { return permissionEntries.removeElement(pe); } boolean contains(CryptoPermissionEntry pe) { return permissionEntries.contains(pe); } /** * Enumerate all the permission entries in this GrantEntry. */ Enumeration permissionElements(){ return permissionEntries.elements(); } } /** * Each crypto permission entry in the policy configuration file is * represented by a CryptoPermissionEntry object.

* *

* For example, the entry *

     *     permission javax.crypto.CryptoPermission "DES", 56;
     * 
* is represented internally *
     *
     * pe = new CryptoPermissionEntry("javax.crypto.cryptoPermission",
     *                           "DES", 56);
     * 
* * @see java.security.Permissions * @see javax.crypto.CryptoPermission * @see javax.crypto.CryptoAllPermission */ private static class CryptoPermissionEntry { String cryptoPermission; String alg; String exemptionMechanism; int maxKeySize; boolean checkParam; AlgorithmParameterSpec algParamSpec; CryptoPermissionEntry() { // Set default values. maxKeySize = 0; alg = null; exemptionMechanism = null; checkParam = false; algParamSpec = null; } /** * Calculates a hash code value for the object. Objects * which are equal will also have the same hashcode. */ public int hashCode() { int retval = cryptoPermission.hashCode(); if (alg != null) retval ^= alg.hashCode(); if (exemptionMechanism != null) { retval ^= exemptionMechanism.hashCode(); } retval ^= maxKeySize; if (checkParam) retval ^= 100; if (algParamSpec != null) { retval ^= algParamSpec.hashCode(); } return retval; } public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof CryptoPermissionEntry)) return false; CryptoPermissionEntry that = (CryptoPermissionEntry) obj; if (this.cryptoPermission == null) { if (that.cryptoPermission != null) return false; } else { if (!this.cryptoPermission.equals( that.cryptoPermission)) return false; } if (this.alg == null) { if (that.alg != null) return false; } else { if (!this.alg.equalsIgnoreCase(that.alg)) return false; } if (!(this.maxKeySize == that.maxKeySize)) return false; if (this.checkParam != that.checkParam) return false; if (this.algParamSpec == null) { if (that.algParamSpec != null) return false; } else { if (!this.algParamSpec.equals(that.algParamSpec)) return false; } // everything matched -- the 2 objects are equal return true; } } static final class ParsingException extends GeneralSecurityException { private static final long serialVersionUID = 7147241245566588374L; /** * Constructs a ParsingException with the specified * detail message. * @param msg the detail message. */ ParsingException(String msg) { super(msg); } ParsingException(int line, String msg) { super("line " + line + ": " + msg); } ParsingException(int line, String expect, String actual) { super("line "+line+": expected '"+expect+"', found '"+actual+"'"); } } }