mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
512 lines
17 KiB
Java
512 lines
17 KiB
Java
/*
|
|
* 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;
|
|
}
|
|
}
|