| /* |
| * Copyright (c) 2000, 2014, 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 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 = "2.5.29.32.0"; |
| |
| // 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 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(); |
| } |
| } |
| } |