Path: blob/master/src/java.base/share/classes/sun/security/provider/certpath/PolicyNodeImpl.java
41161 views
/*1* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.provider.certpath;2627import sun.security.util.KnownOIDs;2829import java.util.Collections;30import java.util.HashSet;31import java.util.Iterator;32import java.util.Set;3334import java.security.cert.*;3536/**37* Implements the <code>PolicyNode</code> interface.38* <p>39* This class provides an implementation of the <code>PolicyNode</code>40* interface, and is used internally to build and search Policy Trees.41* While the implementation is mutable during construction, it is immutable42* before returning to a client and no mutable public or protected methods43* are exposed by this implementation, as per the contract of PolicyNode.44*45* @since 1.446* @author Seth Proctor47* @author Sean Mullan48*/49final class PolicyNodeImpl implements PolicyNode {5051/**52* Use to specify the special policy "Any Policy"53*/54private static final String ANY_POLICY55= KnownOIDs.CE_CERT_POLICIES_ANY.value();5657// every node has one parent, and zero or more children58private PolicyNodeImpl mParent;59private HashSet<PolicyNodeImpl> mChildren;6061// the 4 fields specified by RFC 528062private String mValidPolicy;63private HashSet<PolicyQualifierInfo> mQualifierSet;64private boolean mCriticalityIndicator;65private HashSet<String> mExpectedPolicySet;66private boolean mOriginalExpectedPolicySet;6768// the tree depth69private int mDepth;70// immutability flag71private boolean isImmutable = false;7273/**74* Constructor which takes a <code>PolicyNodeImpl</code> representing the75* parent in the Policy Tree to this node. If null, this is the76* root of the tree. The constructor also takes the associated data77* for this node, as found in the certificate. It also takes a boolean78* argument specifying whether this node is being created as a result79* of policy mapping.80*81* @param parent the PolicyNode above this in the tree, or null if this82* node is the tree's root node83* @param validPolicy a String representing this node's valid policy OID84* @param qualifierSet the Set of qualifiers for this policy85* @param criticalityIndicator a boolean representing whether or not the86* extension is critical87* @param expectedPolicySet a Set of expected policies88* @param generatedByPolicyMapping a boolean indicating whether this89* node was generated by a policy mapping90*/91PolicyNodeImpl(PolicyNodeImpl parent, String validPolicy,92Set<PolicyQualifierInfo> qualifierSet,93boolean criticalityIndicator, Set<String> expectedPolicySet,94boolean generatedByPolicyMapping) {95mParent = parent;96mChildren = new HashSet<PolicyNodeImpl>();9798if (validPolicy != null)99mValidPolicy = validPolicy;100else101mValidPolicy = "";102103if (qualifierSet != null)104mQualifierSet = new HashSet<PolicyQualifierInfo>(qualifierSet);105else106mQualifierSet = new HashSet<PolicyQualifierInfo>();107108mCriticalityIndicator = criticalityIndicator;109110if (expectedPolicySet != null)111mExpectedPolicySet = new HashSet<String>(expectedPolicySet);112else113mExpectedPolicySet = new HashSet<String>();114115mOriginalExpectedPolicySet = !generatedByPolicyMapping;116117// see if we're the root, and act appropriately118if (mParent != null) {119mDepth = mParent.getDepth() + 1;120mParent.addChild(this);121} else {122mDepth = 0;123}124}125126/**127* Alternate constructor which makes a new node with the policy data128* in an existing <code>PolicyNodeImpl</code>.129*130* @param parent a PolicyNode that's the new parent of the node, or131* null if this is the root node132* @param node a PolicyNode containing the policy data to copy133*/134PolicyNodeImpl(PolicyNodeImpl parent, PolicyNodeImpl node) {135this(parent, node.mValidPolicy, node.mQualifierSet,136node.mCriticalityIndicator, node.mExpectedPolicySet, false);137}138139@Override140public PolicyNode getParent() {141return mParent;142}143144@Override145public Iterator<PolicyNodeImpl> getChildren() {146return Collections.unmodifiableSet(mChildren).iterator();147}148149@Override150public int getDepth() {151return mDepth;152}153154@Override155public String getValidPolicy() {156return mValidPolicy;157}158159@Override160public Set<PolicyQualifierInfo> getPolicyQualifiers() {161return Collections.unmodifiableSet(mQualifierSet);162}163164@Override165public Set<String> getExpectedPolicies() {166return Collections.unmodifiableSet(mExpectedPolicySet);167}168169@Override170public boolean isCritical() {171return mCriticalityIndicator;172}173174/**175* Return a printable representation of the PolicyNode.176* Starting at the node on which this method is called,177* it recurses through the tree and prints out each node.178*179* @return a String describing the contents of the Policy Node180*/181@Override182public String toString() {183StringBuilder buffer = new StringBuilder(this.asString());184185for (PolicyNodeImpl node : mChildren) {186buffer.append(node);187}188return buffer.toString();189}190191// private methods and package private operations192193boolean isImmutable() {194return isImmutable;195}196197/**198* Sets the immutability flag of this node and all of its children199* to true.200*/201void setImmutable() {202if (isImmutable)203return;204for (PolicyNodeImpl node : mChildren) {205node.setImmutable();206}207isImmutable = true;208}209210/**211* Private method sets a child node. This is called from the child's212* constructor.213*214* @param child new <code>PolicyNodeImpl</code> child node215*/216private void addChild(PolicyNodeImpl child) {217if (isImmutable) {218throw new IllegalStateException("PolicyNode is immutable");219}220mChildren.add(child);221}222223/**224* Adds an expectedPolicy to the expected policy set.225* If this is the original expected policy set initialized226* by the constructor, then the expected policy set is cleared227* before the expected policy is added.228*229* @param expectedPolicy a String representing an expected policy.230*/231void addExpectedPolicy(String expectedPolicy) {232if (isImmutable) {233throw new IllegalStateException("PolicyNode is immutable");234}235if (mOriginalExpectedPolicySet) {236mExpectedPolicySet.clear();237mOriginalExpectedPolicySet = false;238}239mExpectedPolicySet.add(expectedPolicy);240}241242/**243* Removes all paths which don't reach the specified depth.244*245* @param depth an int representing the desired minimum depth of all paths246*/247void prune(int depth) {248if (isImmutable)249throw new IllegalStateException("PolicyNode is immutable");250251// if we have no children, we can't prune below us...252if (mChildren.size() == 0)253return;254255Iterator<PolicyNodeImpl> it = mChildren.iterator();256while (it.hasNext()) {257PolicyNodeImpl node = it.next();258node.prune(depth);259// now that we've called prune on the child, see if we should260// remove it from the tree261if ((node.mChildren.size() == 0) && (depth > mDepth + 1))262it.remove();263}264}265266/**267* Deletes the specified child node of this node, if it exists.268*269* @param childNode the child node to be deleted270*/271void deleteChild(PolicyNode childNode) {272if (isImmutable) {273throw new IllegalStateException("PolicyNode is immutable");274}275mChildren.remove(childNode);276}277278/**279* Returns a copy of the tree, without copying the policy-related data,280* rooted at the node on which this was called.281*282* @return a copy of the tree283*/284PolicyNodeImpl copyTree() {285return copyTree(null);286}287288private PolicyNodeImpl copyTree(PolicyNodeImpl parent) {289PolicyNodeImpl newNode = new PolicyNodeImpl(parent, this);290291for (PolicyNodeImpl node : mChildren) {292node.copyTree(newNode);293}294295return newNode;296}297298/**299* Returns all nodes at the specified depth in the tree.300*301* @param depth an int representing the depth of the desired nodes302* @return a <code>Set</code> of all nodes at the specified depth303*/304Set<PolicyNodeImpl> getPolicyNodes(int depth) {305Set<PolicyNodeImpl> set = new HashSet<>();306getPolicyNodes(depth, set);307return set;308}309310/**311* Add all nodes at depth to set and return the Set.312* Internal recursion helper.313*/314private void getPolicyNodes(int depth, Set<PolicyNodeImpl> set) {315// if we've reached the desired depth, then return ourself316if (mDepth == depth) {317set.add(this);318} else {319for (PolicyNodeImpl node : mChildren) {320node.getPolicyNodes(depth, set);321}322}323}324325/**326* Finds all nodes at the specified depth whose expected_policy_set327* contains the specified expected OID (if matchAny is false)328* or the special OID "any value" (if matchAny is true).329*330* @param depth an int representing the desired depth331* @param expectedOID a String encoding the valid OID to match332* @param matchAny a boolean indicating whether an expected_policy_set333* containing ANY_POLICY should be considered a match334* @return a Set of matched <code>PolicyNode</code>s335*/336Set<PolicyNodeImpl> getPolicyNodesExpected(int depth,337String expectedOID, boolean matchAny) {338339if (expectedOID.equals(ANY_POLICY)) {340return getPolicyNodes(depth);341} else {342return getPolicyNodesExpectedHelper(depth, expectedOID, matchAny);343}344}345346private Set<PolicyNodeImpl> getPolicyNodesExpectedHelper(int depth,347String expectedOID, boolean matchAny) {348349HashSet<PolicyNodeImpl> set = new HashSet<>();350351if (mDepth < depth) {352for (PolicyNodeImpl node : mChildren) {353set.addAll(node.getPolicyNodesExpectedHelper(depth,354expectedOID,355matchAny));356}357} else {358if (matchAny) {359if (mExpectedPolicySet.contains(ANY_POLICY))360set.add(this);361} else {362if (mExpectedPolicySet.contains(expectedOID))363set.add(this);364}365}366367return set;368}369370/**371* Finds all nodes at the specified depth that contains the372* specified valid OID373*374* @param depth an int representing the desired depth375* @param validOID a String encoding the valid OID to match376* @return a Set of matched <code>PolicyNode</code>s377*/378Set<PolicyNodeImpl> getPolicyNodesValid(int depth, String validOID) {379HashSet<PolicyNodeImpl> set = new HashSet<>();380381if (mDepth < depth) {382for (PolicyNodeImpl node : mChildren) {383set.addAll(node.getPolicyNodesValid(depth, validOID));384}385} else {386if (mValidPolicy.equals(validOID))387set.add(this);388}389390return set;391}392393private static String policyToString(String oid) {394if (oid.equals(ANY_POLICY)) {395return "anyPolicy";396} else {397return oid;398}399}400401/**402* Prints out some data on this node.403*/404String asString() {405if (mParent == null) {406return "anyPolicy ROOT\n";407} else {408StringBuilder sb = new StringBuilder();409for (int i = 0, n = getDepth(); i < n; i++) {410sb.append(" ");411}412sb.append(policyToString(getValidPolicy()));413sb.append(" CRIT: ");414sb.append(isCritical());415sb.append(" EP: ");416for (String policy : getExpectedPolicies()) {417sb.append(policyToString(policy));418sb.append(" ");419}420sb.append(" (");421sb.append(getDepth());422sb.append(")\n");423return sb.toString();424}425}426}427428429