/* |
|
* Copyright (c) 2000, 2020, 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 sun.security.provider.certpath; |
|
import sun.security.util.KnownOIDs; |
|
import java.util.Collections; |
|
import java.util.HashSet; |
|
import java.util.Iterator; |
|
import java.util.Set; |
|
import java.security.cert.*; |
|
/** |
|
* Implements the <code>PolicyNode</code> interface. |
|
* <p> |
|
* This class provides an implementation of the <code>PolicyNode</code> |
|
* interface, and is used internally to build and search Policy Trees. |
|
* While the implementation is mutable during construction, it is immutable |
|
* before returning to a client and no mutable public or protected methods |
|
* are exposed by this implementation, as per the contract of PolicyNode. |
|
* |
|
* @since 1.4 |
|
* @author Seth Proctor |
|
* @author Sean Mullan |
|
*/ |
|
final class PolicyNodeImpl implements PolicyNode { |
|
/** |
|
* Use to specify the special policy "Any Policy" |
|
*/ |
|
private static final String ANY_POLICY |
|
= KnownOIDs.CE_CERT_POLICIES_ANY.value(); |
|
// every node has one parent, and zero or more children |
|
private PolicyNodeImpl mParent; |
|
private HashSet<PolicyNodeImpl> mChildren; |
|
// the 4 fields specified by RFC 5280 |
|
private String mValidPolicy; |
|
private HashSet<PolicyQualifierInfo> mQualifierSet; |
|
private boolean mCriticalityIndicator; |
|
private HashSet<String> mExpectedPolicySet; |
|
private boolean mOriginalExpectedPolicySet; |
|
// the tree depth |
|
private int mDepth; |
|
// immutability flag |
|
private boolean isImmutable = false; |
|
/** |
|
* Constructor which takes a <code>PolicyNodeImpl</code> representing the |
|
* parent in the Policy Tree to this node. If null, this is the |
|
* root of the tree. The constructor also takes the associated data |
|
* for this node, as found in the certificate. It also takes a boolean |
|
* argument specifying whether this node is being created as a result |
|
* of policy mapping. |
|
* |
|
* @param parent the PolicyNode above this in the tree, or null if this |
|
* node is the tree's root node |
|
* @param validPolicy a String representing this node's valid policy OID |
|
* @param qualifierSet the Set of qualifiers for this policy |
|
* @param criticalityIndicator a boolean representing whether or not the |
|
* extension is critical |
|
* @param expectedPolicySet a Set of expected policies |
|
* @param generatedByPolicyMapping a boolean indicating whether this |
|
* node was generated by a policy mapping |
|
*/ |
|
PolicyNodeImpl(PolicyNodeImpl parent, String validPolicy, |
|
Set<PolicyQualifierInfo> qualifierSet, |
|
boolean criticalityIndicator, Set<String> expectedPolicySet, |
|
boolean generatedByPolicyMapping) { |
|
mParent = parent; |
|
mChildren = new HashSet<PolicyNodeImpl>(); |
|
if (validPolicy != null) |
|
mValidPolicy = validPolicy; |
|
else |
|
mValidPolicy = ""; |
|
if (qualifierSet != null) |
|
mQualifierSet = new HashSet<PolicyQualifierInfo>(qualifierSet); |
|
else |
|
mQualifierSet = new HashSet<PolicyQualifierInfo>(); |
|
mCriticalityIndicator = criticalityIndicator; |
|
if (expectedPolicySet != null) |
|
mExpectedPolicySet = new HashSet<String>(expectedPolicySet); |
|
else |
|
mExpectedPolicySet = new HashSet<String>(); |
|
mOriginalExpectedPolicySet = !generatedByPolicyMapping; |
|
// see if we're the root, and act appropriately |
|
if (mParent != null) { |
|
mDepth = mParent.getDepth() + 1; |
|
mParent.addChild(this); |
|
} else { |
|
mDepth = 0; |
|
} |
|
} |
|
/** |
|
* Alternate constructor which makes a new node with the policy data |
|
* in an existing <code>PolicyNodeImpl</code>. |
|
* |
|
* @param parent a PolicyNode that's the new parent of the node, or |
|
* null if this is the root node |
|
* @param node a PolicyNode containing the policy data to copy |
|
*/ |
|
PolicyNodeImpl(PolicyNodeImpl parent, PolicyNodeImpl node) { |
|
this(parent, node.mValidPolicy, node.mQualifierSet, |
|
node.mCriticalityIndicator, node.mExpectedPolicySet, false); |
|
} |
|
@Override |
|
public PolicyNode getParent() { |
|
return mParent; |
|
} |
|
@Override |
|
public Iterator<PolicyNodeImpl> getChildren() { |
|
return Collections.unmodifiableSet(mChildren).iterator(); |
|
} |
|
@Override |
|
public int getDepth() { |
|
return mDepth; |
|
} |
|
@Override |
|
public String getValidPolicy() { |
|
return mValidPolicy; |
|
} |
|
@Override |
|
public Set<PolicyQualifierInfo> getPolicyQualifiers() { |
|
return Collections.unmodifiableSet(mQualifierSet); |
|
} |
|
@Override |
|
public Set<String> getExpectedPolicies() { |
|
return Collections.unmodifiableSet(mExpectedPolicySet); |
|
} |
|
@Override |
|
public boolean isCritical() { |
|
return mCriticalityIndicator; |
|
} |
|
/** |
|
* Return a printable representation of the PolicyNode. |
|
* Starting at the node on which this method is called, |
|
* it recurses through the tree and prints out each node. |
|
* |
|
* @return a String describing the contents of the Policy Node |
|
*/ |
|
@Override |
|
public String toString() { |
|
StringBuilder buffer = new StringBuilder(this.asString()); |
|
for (PolicyNodeImpl node : mChildren) { |
|
buffer.append(node); |
|
} |
|
return buffer.toString(); |
|
} |
|
// private methods and package private operations |
|
boolean isImmutable() { |
|
return isImmutable; |
|
} |
|
/** |
|
* Sets the immutability flag of this node and all of its children |
|
* to true. |
|
*/ |
|
void setImmutable() { |
|
if (isImmutable) |
|
return; |
|
for (PolicyNodeImpl node : mChildren) { |
|
node.setImmutable(); |
|
} |
|
isImmutable = true; |
|
} |
|
/** |
|
* Private method sets a child node. This is called from the child's |
|
* constructor. |
|
* |
|
* @param child new <code>PolicyNodeImpl</code> child node |
|
*/ |
|
private void addChild(PolicyNodeImpl child) { |
|
if (isImmutable) { |
|
throw new IllegalStateException("PolicyNode is immutable"); |
|
} |
|
mChildren.add(child); |
|
} |
|
/** |
|
* Adds an expectedPolicy to the expected policy set. |
|
* If this is the original expected policy set initialized |
|
* by the constructor, then the expected policy set is cleared |
|
* before the expected policy is added. |
|
* |
|
* @param expectedPolicy a String representing an expected policy. |
|
*/ |
|
void addExpectedPolicy(String expectedPolicy) { |
|
if (isImmutable) { |
|
throw new IllegalStateException("PolicyNode is immutable"); |
|
} |
|
if (mOriginalExpectedPolicySet) { |
|
mExpectedPolicySet.clear(); |
|
mOriginalExpectedPolicySet = false; |
|
} |
|
mExpectedPolicySet.add(expectedPolicy); |
|
} |
|
/** |
|
* Removes all paths which don't reach the specified depth. |
|
* |
|
* @param depth an int representing the desired minimum depth of all paths |
|
*/ |
|
void prune(int depth) { |
|
if (isImmutable) |
|
throw new IllegalStateException("PolicyNode is immutable"); |
|
// if we have no children, we can't prune below us... |
|
if (mChildren.size() == 0) |
|
return; |
|
Iterator<PolicyNodeImpl> it = mChildren.iterator(); |
|
while (it.hasNext()) { |
|
PolicyNodeImpl node = it.next(); |
|
node.prune(depth); |
|
// now that we've called prune on the child, see if we should |
|
// remove it from the tree |
|
if ((node.mChildren.size() == 0) && (depth > mDepth + 1)) |
|
it.remove(); |
|
} |
|
} |
|
/** |
|
* Deletes the specified child node of this node, if it exists. |
|
* |
|
* @param childNode the child node to be deleted |
|
*/ |
|
void deleteChild(PolicyNode childNode) { |
|
if (isImmutable) { |
|
throw new IllegalStateException("PolicyNode is immutable"); |
|
} |
|
mChildren.remove(childNode); |
|
} |
|
/** |
|
* Returns a copy of the tree, without copying the policy-related data, |
|
* rooted at the node on which this was called. |
|
* |
|
* @return a copy of the tree |
|
*/ |
|
PolicyNodeImpl copyTree() { |
|
return copyTree(null); |
|
} |
|
private PolicyNodeImpl copyTree(PolicyNodeImpl parent) { |
|
PolicyNodeImpl newNode = new PolicyNodeImpl(parent, this); |
|
for (PolicyNodeImpl node : mChildren) { |
|
node.copyTree(newNode); |
|
} |
|
return newNode; |
|
} |
|
/** |
|
* Returns all nodes at the specified depth in the tree. |
|
* |
|
* @param depth an int representing the depth of the desired nodes |
|
* @return a <code>Set</code> of all nodes at the specified depth |
|
*/ |
|
Set<PolicyNodeImpl> getPolicyNodes(int depth) { |
|
Set<PolicyNodeImpl> set = new HashSet<>(); |
|
getPolicyNodes(depth, set); |
|
return set; |
|
} |
|
/** |
|
* Add all nodes at depth to set and return the Set. |
|
* Internal recursion helper. |
|
*/ |
|
private void getPolicyNodes(int depth, Set<PolicyNodeImpl> set) { |
|
// if we've reached the desired depth, then return ourself |
|
if (mDepth == depth) { |
|
set.add(this); |
|
} else { |
|
for (PolicyNodeImpl node : mChildren) { |
|
node.getPolicyNodes(depth, set); |
|
} |
|
} |
|
} |
|
/** |
|
* Finds all nodes at the specified depth whose expected_policy_set |
|
* contains the specified expected OID (if matchAny is false) |
|
* or the special OID "any value" (if matchAny is true). |
|
* |
|
* @param depth an int representing the desired depth |
|
* @param expectedOID a String encoding the valid OID to match |
|
* @param matchAny a boolean indicating whether an expected_policy_set |
|
* containing ANY_POLICY should be considered a match |
|
* @return a Set of matched <code>PolicyNode</code>s |
|
*/ |
|
Set<PolicyNodeImpl> getPolicyNodesExpected(int depth, |
|
String expectedOID, boolean matchAny) { |
|
if (expectedOID.equals(ANY_POLICY)) { |
|
return getPolicyNodes(depth); |
|
} else { |
|
return getPolicyNodesExpectedHelper(depth, expectedOID, matchAny); |
|
} |
|
} |
|
private Set<PolicyNodeImpl> getPolicyNodesExpectedHelper(int depth, |
|
String expectedOID, boolean matchAny) { |
|
HashSet<PolicyNodeImpl> set = new HashSet<>(); |
|
if (mDepth < depth) { |
|
for (PolicyNodeImpl node : mChildren) { |
|
set.addAll(node.getPolicyNodesExpectedHelper(depth, |
|
expectedOID, |
|
matchAny)); |
|
} |
|
} else { |
|
if (matchAny) { |
|
if (mExpectedPolicySet.contains(ANY_POLICY)) |
|
set.add(this); |
|
} else { |
|
if (mExpectedPolicySet.contains(expectedOID)) |
|
set.add(this); |
|
} |
|
} |
|
return set; |
|
} |
|
/** |
|
* Finds all nodes at the specified depth that contains the |
|
* specified valid OID |
|
* |
|
* @param depth an int representing the desired depth |
|
* @param validOID a String encoding the valid OID to match |
|
* @return a Set of matched <code>PolicyNode</code>s |
|
*/ |
|
Set<PolicyNodeImpl> getPolicyNodesValid(int depth, String validOID) { |
|
HashSet<PolicyNodeImpl> set = new HashSet<>(); |
|
if (mDepth < depth) { |
|
for (PolicyNodeImpl node : mChildren) { |
|
set.addAll(node.getPolicyNodesValid(depth, validOID)); |
|
} |
|
} else { |
|
if (mValidPolicy.equals(validOID)) |
|
set.add(this); |
|
} |
|
return set; |
|
} |
|
private static String policyToString(String oid) { |
|
if (oid.equals(ANY_POLICY)) { |
|
return "anyPolicy"; |
|
} else { |
|
return oid; |
|
} |
|
} |
|
/** |
|
* Prints out some data on this node. |
|
*/ |
|
String asString() { |
|
if (mParent == null) { |
|
return "anyPolicy ROOT\n"; |
|
} else { |
|
StringBuilder sb = new StringBuilder(); |
|
for (int i = 0, n = getDepth(); i < n; i++) { |
|
sb.append(" "); |
|
} |
|
sb.append(policyToString(getValidPolicy())); |
|
sb.append(" CRIT: "); |
|
sb.append(isCritical()); |
|
sb.append(" EP: "); |
|
for (String policy : getExpectedPolicies()) { |
|
sb.append(policyToString(policy)); |
|
sb.append(" "); |
|
} |
|
sb.append(" ("); |
|
sb.append(getDepth()); |
|
sb.append(")\n"); |
|
return sb.toString(); |
|
} |
|
} |
|
} |