| /* | |
| * Copyright (c) 1999, 2022, 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.security.*; | |
| import java.util.ArrayList; | |
| import java.util.Enumeration; | |
| import java.util.Hashtable; | |
| import java.util.NoSuchElementException; | |
| import java.util.concurrent.ConcurrentHashMap; | |
| import java.io.Serializable; | |
| import java.io.InputStream; | |
| import java.io.InputStreamReader; | |
| import java.io.BufferedReader; | |
| import java.io.ObjectStreamField; | |
| import java.io.ObjectInputStream; | |
| import java.io.ObjectOutputStream; | |
| import java.io.IOException; | |
| import static java.nio.charset.StandardCharsets.UTF_8; | |
| /** | |
| * This class contains {@code CryptoPermission} objects, organized into | |
| * {@code PermissionCollection} objects according to algorithm names. | |
| * | |
| * <p>When the {@code add} method is called to add a | |
| * {@code CryptoPermission}, the {@code CryptoPermission} is stored in the | |
| * appropriate {@code PermissionCollection}. If no such | |
| * collection exists yet, the algorithm name associated with | |
| * the {@code CryptoPermission} object is | |
| * determined and the {@code newPermissionCollection} method | |
| * is called on the {@code CryptoPermission} or {@code CryptoAllPermission} class to | |
| * create the {@code PermissionCollection} and add it to the {@code Permissions} object. | |
| * | |
| * @see javax.crypto.CryptoPermission | |
| * @see java.security.PermissionCollection | |
| * @see java.security.Permissions | |
| * | |
| * @author Sharon Liu | |
| * @since 1.4 | |
| */ | |
| final class CryptoPermissions extends PermissionCollection | |
| implements Serializable { | |
| @java.io.Serial | |
| private static final long serialVersionUID = 4946547168093391015L; | |
| /** | |
| * @serialField perms java.util.Hashtable | |
| */ | |
| @java.io.Serial | |
| private static final ObjectStreamField[] serialPersistentFields = { | |
| new ObjectStreamField("perms", Hashtable.class), | |
| }; | |
| // Switched from Hashtable to ConcurrentHashMap to improve scalability. | |
| // To maintain serialization compatibility, this field is made transient | |
| // and custom readObject/writeObject methods are used. | |
| private transient ConcurrentHashMap<String,PermissionCollection> perms; | |
| /** | |
| * Creates a new {@code CryptoPermissions} object containing | |
| * no {@code CryptoPermissionCollection} objects. | |
| */ | |
| CryptoPermissions() { | |
| perms = new ConcurrentHashMap<>(7); | |
| } | |
| /** | |
| * Populates the crypto policy from the specified | |
| * {@code InputStream} into this {@code CryptoPermissions} object. | |
| * | |
| * @param in the InputStream to load from. | |
| * | |
| * @exception SecurityException if cannot load | |
| * successfully. | |
| */ | |
| void load(InputStream in) | |
| throws IOException, CryptoPolicyParser.ParsingException { | |
| CryptoPolicyParser parser = new CryptoPolicyParser(); | |
| parser.read(new BufferedReader(new InputStreamReader(in, UTF_8))); | |
| CryptoPermission[] parsingResult = parser.getPermissions(); | |
| for (int i = 0; i < parsingResult.length; i++) { | |
| this.add(parsingResult[i]); | |
| } | |
| } | |
| /** | |
| * Returns {@code true} if this {@code CryptoPermissions} object doesn't | |
| * contain any {@code CryptoPermission} objects; otherwise, returns | |
| * {@code false}. | |
| */ | |
| boolean isEmpty() { | |
| return perms.isEmpty(); | |
| } | |
| /** | |
| * Adds a permission object to the | |
| * {@code PermissionCollection} for the algorithm returned by | |
| * {@code (CryptoPermission)permission.getAlgorithm()}. | |
| * | |
| * This method creates | |
| * a new {@code PermissionCollection} object (and adds the | |
| * permission to it) if an appropriate collection does not yet exist. | |
| * | |
| * @param permission the {@code Permission} object to add. | |
| * | |
| * @exception SecurityException if this {@code CryptoPermissions} | |
| * object is marked as readonly. | |
| * | |
| * @see PermissionCollection#isReadOnly | |
| */ | |
| @Override | |
| public void add(Permission permission) { | |
| if (isReadOnly()) { | |
| throw new SecurityException("Attempt to add a Permission " + | |
| "to a readonly CryptoPermissions " + | |
| "object"); | |
| } | |
| if (!(permission instanceof CryptoPermission cryptoPerm)) { | |
| return; | |
| } | |
| PermissionCollection pc = | |
| getPermissionCollection(cryptoPerm); | |
| pc.add(cryptoPerm); | |
| String alg = cryptoPerm.getAlgorithm(); | |
| perms.putIfAbsent(alg, pc); | |
| } | |
| /** | |
| * Checks if this object's {@code PermissionCollection} for permissions | |
| * of the specified permission's algorithm implies the specified | |
| * permission. Returns {@code true} if the checking succeeded. | |
| * | |
| * @param permission the {@code Permission} object to check. | |
| * | |
| * @return {@code true} if {@code permission} is implied by the permissions | |
| * in the {@code PermissionCollection} it belongs to, {@code false} if not. | |
| * | |
| */ | |
| @Override | |
| public boolean implies(Permission permission) { | |
| if (!(permission instanceof CryptoPermission cryptoPerm)) { | |
| return false; | |
| } | |
| PermissionCollection pc = | |
| getPermissionCollection(cryptoPerm.getAlgorithm()); | |
| if (pc != null) { | |
| return pc.implies(cryptoPerm); | |
| } else { | |
| // none found | |
| return false; | |
| } | |
| } | |
| /** | |
| * Returns an enumeration of all the {@code Permission} objects | |
| * in this {@code CryptoPermissions} object. | |
| * @return an enumeration of all the {@code Permission} objects. | |
| */ | |
| @Override | |
| public Enumeration<Permission> elements() { | |
| // go through each Permissions in the hash table | |
| // and call their elements() function. | |
| return new PermissionsEnumerator(perms.elements()); | |
| } | |
| /** | |
| * Returns a {@code CryptoPermissions} object which | |
| * represents the minimum of the specified | |
| * {@code CryptoPermissions} object and this | |
| * {@code CryptoPermissions} object. | |
| * | |
| * @param other the {@code CryptoPermission} | |
| * object to compare with this object. | |
| */ | |
| CryptoPermissions getMinimum(CryptoPermissions other) { | |
| if (other == null) { | |
| return null; | |
| } | |
| if (this.perms.containsKey(CryptoAllPermission.ALG_NAME)) { | |
| return other; | |
| } | |
| if (other.perms.containsKey(CryptoAllPermission.ALG_NAME)) { | |
| return this; | |
| } | |
| CryptoPermissions ret = new CryptoPermissions(); | |
| PermissionCollection thatWildcard = | |
| other.perms.get(CryptoPermission.ALG_NAME_WILDCARD); | |
| int maxKeySize = 0; | |
| if (thatWildcard != null) { | |
| maxKeySize = ((CryptoPermission) | |
| thatWildcard.elements().nextElement()).getMaxKeySize(); | |
| } | |
| // For each algorithm in this CryptoPermissions, | |
| // find out if there is anything we should add into | |
| // ret. | |
| for (String alg : this.perms.keySet()) { | |
| PermissionCollection thisPc = this.perms.get(alg); | |
| PermissionCollection thatPc = other.perms.get(alg); | |
| CryptoPermission[] partialResult; | |
| if (thatPc == null) { | |
| if (thatWildcard == null) { | |
| // The other CryptoPermissions | |
| // doesn't allow this given | |
| // algorithm at all. Just skip this | |
| // algorithm. | |
| continue; | |
| } | |
| partialResult = getMinimum(maxKeySize, thisPc); | |
| } else { | |
| partialResult = getMinimum(thisPc, thatPc); | |
| } | |
| for (int i = 0; i < partialResult.length; i++) { | |
| ret.add(partialResult[i]); | |
| } | |
| } | |
| PermissionCollection thisWildcard = | |
| this.perms.get(CryptoPermission.ALG_NAME_WILDCARD); | |
| // If this CryptoPermissions doesn't | |
| // have a wildcard, we are done. | |
| if (thisWildcard == null) { | |
| return ret; | |
| } | |
| // Deal with the algorithms only appear | |
| // in the other CryptoPermissions. | |
| maxKeySize = | |
| ((CryptoPermission) | |
| thisWildcard.elements().nextElement()).getMaxKeySize(); | |
| for (String alg : other.perms.keySet()) { | |
| if (this.perms.containsKey(alg)) { | |
| continue; | |
| } | |
| PermissionCollection thatPc = other.perms.get(alg); | |
| CryptoPermission[] partialResult; | |
| partialResult = getMinimum(maxKeySize, thatPc); | |
| for (int i = 0; i < partialResult.length; i++) { | |
| ret.add(partialResult[i]); | |
| } | |
| } | |
| return ret; | |
| } | |
| /** | |
| * Get the minimum of the two given {@code PermissionCollection} | |
| * {@code thisPc} and {@code thatPc}. | |
| * | |
| * @param thisPc the first given {@code PermissionCollection} | |
| * object. | |
| * | |
| * @param thatPc the second given {@code PermissionCollection} | |
| * object. | |
| */ | |
| private CryptoPermission[] getMinimum(PermissionCollection thisPc, | |
| PermissionCollection thatPc) { | |
| ArrayList<CryptoPermission> permList = new ArrayList<>(2); | |
| Enumeration<Permission> thisPcPermissions = thisPc.elements(); | |
| // For each CryptoPermission in | |
| // thisPc object, do the following: | |
| // 1) if this CryptoPermission is implied | |
| // by thatPc, this CryptoPermission | |
| // should be returned, and we can | |
| // move on to check the next | |
| // CryptoPermission in thisPc. | |
| // 2) otherwise, we should return | |
| // all CryptoPermissions in thatPc | |
| // which | |
| // are implied by this CryptoPermission. | |
| // Then we can move on to the | |
| // next CryptoPermission in thisPc. | |
| while (thisPcPermissions.hasMoreElements()) { | |
| CryptoPermission thisCp = | |
| (CryptoPermission)thisPcPermissions.nextElement(); | |
| Enumeration<Permission> thatPcPermissions = thatPc.elements(); | |
| while (thatPcPermissions.hasMoreElements()) { | |
| CryptoPermission thatCp = | |
| (CryptoPermission)thatPcPermissions.nextElement(); | |
| if (thatCp.implies(thisCp)) { | |
| permList.add(thisCp); | |
| break; | |
| } | |
| if (thisCp.implies(thatCp)) { | |
| permList.add(thatCp); | |
| } | |
| } | |
| } | |
| return permList.toArray(new CryptoPermission[0]); | |
| } | |
| /** | |
| * Returns all the {@code CryptoPermission} objects in the given | |
| * {@code PermissionCollection} object | |
| * whose maximum keysize no greater than {@code maxKeySize}. | |
| * For all {@code CryptoPermission} objects with a maximum keysize | |
| * greater than {@code maxKeySize}, this method constructs a | |
| * corresponding {@code CryptoPermission} object whose maximum | |
| * keysize is set to {@code maxKeySize}, and includes that in | |
| * the result. | |
| * | |
| * @param maxKeySize the given maximum key size. | |
| * | |
| * @param pc the given {@code PermissionCollection} object. | |
| */ | |
| private CryptoPermission[] getMinimum(int maxKeySize, | |
| PermissionCollection pc) { | |
| ArrayList<CryptoPermission> permList = new ArrayList<>(1); | |
| Enumeration<Permission> enum_ = pc.elements(); | |
| while (enum_.hasMoreElements()) { | |
| CryptoPermission cp = | |
| (CryptoPermission)enum_.nextElement(); | |
| if (cp.getMaxKeySize() <= maxKeySize) { | |
| permList.add(cp); | |
| } else { | |
| if (cp.getCheckParam()) { | |
| permList.add( | |
| new CryptoPermission(cp.getAlgorithm(), | |
| maxKeySize, | |
| cp.getAlgorithmParameterSpec(), | |
| cp.getExemptionMechanism())); | |
| } else { | |
| permList.add( | |
| new CryptoPermission(cp.getAlgorithm(), | |
| maxKeySize, | |
| cp.getExemptionMechanism())); | |
| } | |
| } | |
| } | |
| return permList.toArray(new CryptoPermission[0]); | |
| } | |
| /** | |
| * Returns the {@code PermissionCollection} for the | |
| * specified algorithm. Returns {@code null} if there | |
| * isn't such a {@code PermissionCollection}. | |
| * | |
| * @param alg the algorithm name. | |
| */ | |
| PermissionCollection getPermissionCollection(String alg) { | |
| // If this CryptoPermissions includes CryptoAllPermission, | |
| // we should return CryptoAllPermission. | |
| PermissionCollection pc = perms.get(CryptoAllPermission.ALG_NAME); | |
| if (pc == null) { | |
| pc = perms.get(alg); | |
| // If there isn't a PermissionCollection for | |
| // the given algorithm,we should return the | |
| // PermissionCollection for the wildcard | |
| // if there is one. | |
| if (pc == null) { | |
| pc = perms.get(CryptoPermission.ALG_NAME_WILDCARD); | |
| } | |
| } | |
| return pc; | |
| } | |
| /** | |
| * Returns the {@code PermissionCollection} for the algorithm | |
| * associated with the specified {@code CryptoPermission} | |
| * object. Creates such a {@code PermissionCollection} | |
| * if such a {@code PermissionCollection} does not | |
| * exist yet. | |
| * | |
| * @param cryptoPerm the {@code CryptoPermission} object. | |
| */ | |
| private PermissionCollection getPermissionCollection( | |
| CryptoPermission cryptoPerm) { | |
| String alg = cryptoPerm.getAlgorithm(); | |
| PermissionCollection pc = perms.get(alg); | |
| if (pc == null) { | |
| pc = cryptoPerm.newPermissionCollection(); | |
| } | |
| return pc; | |
| } | |
| @java.io.Serial | |
| private void readObject(ObjectInputStream s) | |
| throws IOException, ClassNotFoundException { | |
| ObjectInputStream.GetField fields = s.readFields(); | |
| @SuppressWarnings("unchecked") | |
| Hashtable<String,PermissionCollection> permTable = | |
| (Hashtable<String,PermissionCollection>) | |
| (fields.get("perms", null)); | |
| if (permTable != null) { | |
| perms = new ConcurrentHashMap<>(permTable); | |
| } else { | |
| perms = new ConcurrentHashMap<>(); | |
| } | |
| } | |
| @java.io.Serial | |
| private void writeObject(ObjectOutputStream s) throws IOException { | |
| Hashtable<String,PermissionCollection> permTable = | |
| new Hashtable<>(perms); | |
| ObjectOutputStream.PutField fields = s.putFields(); | |
| fields.put("perms", permTable); | |
| s.writeFields(); | |
| } | |
| } | |
| final class PermissionsEnumerator implements Enumeration<Permission> { | |
| // all the perms | |
| private final Enumeration<PermissionCollection> perms; | |
| // the current set | |
| private Enumeration<Permission> permset; | |
| PermissionsEnumerator(Enumeration<PermissionCollection> e) { | |
| perms = e; | |
| permset = getNextEnumWithMore(); | |
| } | |
| @Override | |
| public synchronized boolean hasMoreElements() { | |
| // if we enter with permissionimpl null, we know | |
| // there are no more left. | |
| if (permset == null) { | |
| return false; | |
| } | |
| // try to see if there are any left in the current one | |
| if (permset.hasMoreElements()) { | |
| return true; | |
| } | |
| // get the next one that has something in it... | |
| permset = getNextEnumWithMore(); | |
| // if it is null, we are done! | |
| return (permset != null); | |
| } | |
| @Override | |
| public synchronized Permission nextElement() { | |
| // hasMoreElements will update permset to the next permset | |
| // with something in it... | |
| if (hasMoreElements()) { | |
| return permset.nextElement(); | |
| } else { | |
| throw new NoSuchElementException("PermissionsEnumerator"); | |
| } | |
| } | |
| private Enumeration<Permission> getNextEnumWithMore() { | |
| while (perms.hasMoreElements()) { | |
| PermissionCollection pc = perms.nextElement(); | |
| Enumeration<Permission> next = pc.elements(); | |
| if (next.hasMoreElements()) { | |
| return next; | |
| } | |
| } | |
| return null; | |
| } | |
| } |