| /* |
| * 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 <crypto permission class name>[, <algorithm name> |
| * [[, <exemption mechanism name>][, <maxKeySize> |
| * [, <AlgrithomParameterSpec class name>, <parameters |
| * for constructing an AlgrithomParameterSpec object>]]]]; |
| * |
| * @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. <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. |
| */ |
| |
| 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. <p> |
| * |
| * <p> |
| * For example, the entry |
| * <pre> |
| * grant { |
| * permission javax.crypto.CryptoPermission "DES", 56; |
| * }; |
| * |
| * </pre> |
| * is represented internally |
| * <pre> |
| * |
| * pe = new CryptoPermissionEntry("javax.crypto.CryptoPermission", |
| * "DES", 56); |
| * |
| * ge = new GrantEntry(); |
| * |
| * ge.add(pe); |
| * |
| * </pre> |
| * |
| * @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. <p> |
| * |
| * <p> |
| * For example, the entry |
| * <pre> |
| * permission javax.crypto.CryptoPermission "DES", 56; |
| * </pre> |
| * is represented internally |
| * <pre> |
| * |
| * pe = new CryptoPermissionEntry("javax.crypto.cryptoPermission", |
| * "DES", 56); |
| * </pre> |
| * |
| * @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+"'"); |
| } |
| } |
| } |