src/java.base/share/classes/javax/crypto/CryptoPermissions.java
changeset 47216 71c04702a3d5
parent 39880 d4cfdddbc897
child 57950 4612a3cfb927
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/javax/crypto/CryptoPermissions.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,518 @@
+/*
+ * Copyright (c) 1999, 2016, 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.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+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;
+
+/**
+ * This class contains CryptoPermission objects, organized into
+ * PermissionCollections according to algorithm names.
+ *
+ * <p>When the <code>add</code> method is called to add a
+ * CryptoPermission, the CryptoPermission is stored in the
+ * appropriate PermissionCollection. If no such
+ * collection exists yet, the algorithm name associated with
+ * the CryptoPermission object is
+ * determined and the <code>newPermissionCollection</code> method
+ * is called on the CryptoPermission or CryptoAllPermission class to
+ * create the PermissionCollection and add it to the 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 {
+
+    private static final long serialVersionUID = 4946547168093391015L;
+
+    /**
+     * @serialField perms java.util.Hashtable
+     */
+    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 CryptoPermissions object containing
+     * no CryptoPermissionCollections.
+     */
+    CryptoPermissions() {
+        perms = new ConcurrentHashMap<>(7);
+    }
+
+    /**
+     * Populates the crypto policy from the specified
+     * InputStream into this 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 true if this CryptoPermissions object doesn't
+     * contain any CryptoPermission objects; otherwise, returns
+     * false.
+     */
+    boolean isEmpty() {
+        return perms.isEmpty();
+    }
+
+    /**
+     * Adds a permission object to the PermissionCollection for the
+     * algorithm returned by
+     * <code>(CryptoPermission)permission.getAlgorithm()</code>.
+     *
+     * This method creates
+     * a new PermissionCollection object (and adds the permission to it)
+     * if an appropriate collection does not yet exist. <p>
+     *
+     * @param permission the Permission object to add.
+     *
+     * @exception SecurityException if this CryptoPermissions object is
+     * marked as readonly.
+     *
+     * @see 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)) {
+            return;
+        }
+
+        CryptoPermission cryptoPerm = (CryptoPermission)permission;
+        PermissionCollection pc =
+                        getPermissionCollection(cryptoPerm);
+        pc.add(cryptoPerm);
+        String alg = cryptoPerm.getAlgorithm();
+        perms.putIfAbsent(alg, pc);
+    }
+
+    /**
+     * Checks if this object's PermissionCollection for permissons
+     * of the specified permission's algorithm implies the specified
+     * permission. Returns true if the checking succeeded.
+     *
+     * @param permission the Permission object to check.
+     *
+     * @return true if "permission" is implied by the permissions
+     * in the PermissionCollection it belongs to, false if not.
+     *
+     */
+    @Override
+    public boolean implies(Permission permission) {
+        if (!(permission instanceof CryptoPermission)) {
+            return false;
+        }
+
+        CryptoPermission cryptoPerm = (CryptoPermission)permission;
+
+        PermissionCollection pc =
+            getPermissionCollection(cryptoPerm.getAlgorithm());
+
+        if (pc != null) {
+            return pc.implies(cryptoPerm);
+        } else {
+            // none found
+            return false;
+        }
+    }
+
+    /**
+     * Returns an enumeration of all the Permission objects in all the
+     * PermissionCollections in this CryptoPermissions object.
+     *
+     * @return an enumeration of all the Permissions.
+     */
+    @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 CryptoPermissions object which
+     * represents the minimum of the specified
+     * CryptoPermissions object and this
+     * CryptoPermissions object.
+     *
+     * @param other the 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.
+        Enumeration<String> thisKeys = this.perms.keys();
+        while (thisKeys.hasMoreElements()) {
+            String alg = thisKeys.nextElement();
+
+            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();
+        Enumeration<String> thatKeys = other.perms.keys();
+        while (thatKeys.hasMoreElements()) {
+            String alg = thatKeys.nextElement();
+
+            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 PermissionCollection
+     * <code>thisPc</code> and <code>thatPc</code>.
+     *
+     * @param thisPc the first given PermissionColloection
+     * object.
+     *
+     * @param thatPc the second given PermissionCollection
+     * object.
+     */
+    private CryptoPermission[] getMinimum(PermissionCollection thisPc,
+                                          PermissionCollection thatPc) {
+        Vector<CryptoPermission> permVector = new Vector<>(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)) {
+                    permVector.addElement(thisCp);
+                    break;
+                }
+                if (thisCp.implies(thatCp)) {
+                    permVector.addElement(thatCp);
+                }
+            }
+        }
+
+        CryptoPermission[] ret = new CryptoPermission[permVector.size()];
+        permVector.copyInto(ret);
+        return ret;
+    }
+
+    /**
+     * Returns all the CryptoPermission objects in the given
+     * PermissionCollection object
+     * whose maximum keysize no greater than <code>maxKeySize</code>.
+     * For all CryptoPermission objects with a maximum keysize greater
+     * than <code>maxKeySize</code>, this method constructs a
+     * corresponding CryptoPermission object whose maximum keysize is
+     * set to <code>maxKeySize</code>, and includes that in the result.
+     *
+     * @param maxKeySize the given maximum key size.
+     *
+     * @param pc the given PermissionCollection object.
+     */
+    private CryptoPermission[] getMinimum(int maxKeySize,
+                                          PermissionCollection pc) {
+        Vector<CryptoPermission> permVector = new Vector<>(1);
+
+        Enumeration<Permission> enum_ = pc.elements();
+
+        while (enum_.hasMoreElements()) {
+            CryptoPermission cp =
+                (CryptoPermission)enum_.nextElement();
+            if (cp.getMaxKeySize() <= maxKeySize) {
+                permVector.addElement(cp);
+            } else {
+                if (cp.getCheckParam()) {
+                    permVector.addElement(
+                           new CryptoPermission(cp.getAlgorithm(),
+                                                maxKeySize,
+                                                cp.getAlgorithmParameterSpec(),
+                                                cp.getExemptionMechanism()));
+                } else {
+                    permVector.addElement(
+                           new CryptoPermission(cp.getAlgorithm(),
+                                                maxKeySize,
+                                                cp.getExemptionMechanism()));
+                }
+            }
+        }
+
+        CryptoPermission[] ret = new CryptoPermission[permVector.size()];
+        permVector.copyInto(ret);
+        return ret;
+    }
+
+    /**
+     * Returns the PermissionCollection for the
+     * specified algorithm. Returns null if there
+     * isn't such a 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 PermissionCollection for the algorithm
+     * associated with the specified CryptoPermission
+     * object. Creates such a PermissionCollection
+     * if such a PermissionCollection does not
+     * exist yet.
+     *
+     * @param cryptoPerm the CryptoPermission object.
+     */
+    private PermissionCollection getPermissionCollection(
+                                          CryptoPermission cryptoPerm) {
+
+        String alg = cryptoPerm.getAlgorithm();
+
+        PermissionCollection pc = perms.get(alg);
+
+        if (pc == null) {
+            pc = cryptoPerm.newPermissionCollection();
+        }
+        return pc;
+    }
+
+    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<>();
+        }
+    }
+
+    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;
+    }
+}