1 /*
2  * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.provider.certpath;
27 
28 import java.io.IOException;
29 import java.security.cert.Certificate;
30 import java.security.cert.CertificateException;
31 import java.security.cert.CertPathValidatorException;
32 import java.security.cert.PKIXCertPathChecker;
33 import java.security.cert.PKIXReason;
34 import java.security.cert.X509Certificate;
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.HashSet;
38 import java.util.Set;
39 
40 import sun.security.util.Debug;
41 import static sun.security.x509.PKIXExtensions.*;
42 import sun.security.x509.NameConstraintsExtension;
43 import sun.security.x509.X509CertImpl;
44 
45 /**
46  * ConstraintsChecker is a <code>PKIXCertPathChecker</code> that checks
47  * constraints information on a PKIX certificate, namely basic constraints
48  * and name constraints.
49  *
50  * @since       1.4
51  * @author      Yassir Elley
52  */
53 class ConstraintsChecker extends PKIXCertPathChecker {
54 
55     private static final Debug debug = Debug.getInstance("certpath");
56     /* length of cert path */
57     private final int certPathLength;
58     /* current maximum path length (as defined in PKIX) */
59     private int maxPathLength;
60     /* current index of cert */
61     private int i;
62     private NameConstraintsExtension prevNC;
63 
64     private Set<String> supportedExts;
65 
66     /**
67      * Creates a ConstraintsChecker.
68      *
69      * @param certPathLength the length of the certification path
70      */
ConstraintsChecker(int certPathLength)71     ConstraintsChecker(int certPathLength) {
72         this.certPathLength = certPathLength;
73     }
74 
75     @Override
init(boolean forward)76     public void init(boolean forward) throws CertPathValidatorException {
77         if (!forward) {
78             i = 0;
79             maxPathLength = certPathLength;
80             prevNC = null;
81         } else {
82             throw new CertPathValidatorException
83                 ("forward checking not supported");
84         }
85     }
86 
87     @Override
isForwardCheckingSupported()88     public boolean isForwardCheckingSupported() {
89         return false;
90     }
91 
92     @Override
getSupportedExtensions()93     public Set<String> getSupportedExtensions() {
94         if (supportedExts == null) {
95             supportedExts = new HashSet<String>(2);
96             supportedExts.add(BasicConstraints_Id.toString());
97             supportedExts.add(NameConstraints_Id.toString());
98             supportedExts = Collections.unmodifiableSet(supportedExts);
99         }
100         return supportedExts;
101     }
102 
103     /**
104      * Performs the basic constraints and name constraints
105      * checks on the certificate using its internal state.
106      *
107      * @param cert the <code>Certificate</code> to be checked
108      * @param unresCritExts a <code>Collection</code> of OID strings
109      *        representing the current set of unresolved critical extensions
110      * @throws CertPathValidatorException if the specified certificate
111      *         does not pass the check
112      */
113     @Override
check(Certificate cert, Collection<String> unresCritExts)114     public void check(Certificate cert, Collection<String> unresCritExts)
115         throws CertPathValidatorException
116     {
117         X509Certificate currCert = (X509Certificate)cert;
118 
119         i++;
120         // MUST run NC check second, since it depends on BC check to
121         // update remainingCerts
122         checkBasicConstraints(currCert);
123         verifyNameConstraints(currCert);
124 
125         if (unresCritExts != null && !unresCritExts.isEmpty()) {
126             unresCritExts.remove(BasicConstraints_Id.toString());
127             unresCritExts.remove(NameConstraints_Id.toString());
128         }
129     }
130 
131     /**
132      * Internal method to check the name constraints against a cert
133      */
verifyNameConstraints(X509Certificate currCert)134     private void verifyNameConstraints(X509Certificate currCert)
135         throws CertPathValidatorException
136     {
137         String msg = "name constraints";
138         if (debug != null) {
139             debug.println("---checking " + msg + "...");
140         }
141 
142         // check name constraints only if there is a previous name constraint
143         // and either the currCert is the final cert or the currCert is not
144         // self-issued
145         if (prevNC != null && ((i == certPathLength) ||
146                 !X509CertImpl.isSelfIssued(currCert))) {
147             if (debug != null) {
148                 debug.println("prevNC = " + prevNC +
149                     ", currDN = " + currCert.getSubjectX500Principal());
150             }
151 
152             try {
153                 if (!prevNC.verify(currCert)) {
154                     throw new CertPathValidatorException(msg + " check failed",
155                         null, null, -1, PKIXReason.INVALID_NAME);
156                 }
157             } catch (IOException ioe) {
158                 throw new CertPathValidatorException(ioe);
159             }
160         }
161 
162         // merge name constraints regardless of whether cert is self-issued
163         prevNC = mergeNameConstraints(currCert, prevNC);
164 
165         if (debug != null)
166             debug.println(msg + " verified.");
167     }
168 
169     /**
170      * Helper to fold sets of name constraints together
171      */
mergeNameConstraints( X509Certificate currCert, NameConstraintsExtension prevNC)172     static NameConstraintsExtension mergeNameConstraints(
173         X509Certificate currCert, NameConstraintsExtension prevNC)
174         throws CertPathValidatorException
175     {
176         X509CertImpl currCertImpl;
177         try {
178             currCertImpl = X509CertImpl.toImpl(currCert);
179         } catch (CertificateException ce) {
180             throw new CertPathValidatorException(ce);
181         }
182 
183         NameConstraintsExtension newConstraints =
184             currCertImpl.getNameConstraintsExtension();
185 
186         if (debug != null) {
187             debug.println("prevNC = " + prevNC +
188                         ", newNC = " + String.valueOf(newConstraints));
189         }
190 
191         // if there are no previous name constraints, we just return the
192         // new name constraints.
193         if (prevNC == null) {
194             if (debug != null) {
195                 debug.println("mergedNC = " + String.valueOf(newConstraints));
196             }
197             if (newConstraints == null) {
198                 return newConstraints;
199             } else {
200                 // Make sure we do a clone here, because we're probably
201                 // going to modify this object later and we don't want to
202                 // be sharing it with a Certificate object!
203                 return (NameConstraintsExtension)newConstraints.clone();
204             }
205         } else {
206             try {
207                 // after merge, prevNC should contain the merged constraints
208                 prevNC.merge(newConstraints);
209             } catch (IOException ioe) {
210                 throw new CertPathValidatorException(ioe);
211             }
212             if (debug != null) {
213                 debug.println("mergedNC = " + prevNC);
214             }
215             return prevNC;
216         }
217     }
218 
219     /**
220      * Internal method to check that a given cert meets basic constraints.
221      */
checkBasicConstraints(X509Certificate currCert)222     private void checkBasicConstraints(X509Certificate currCert)
223         throws CertPathValidatorException
224     {
225         String msg = "basic constraints";
226         if (debug != null) {
227             debug.println("---checking " + msg + "...");
228             debug.println("i = " + i +
229                         ", maxPathLength = " + maxPathLength);
230         }
231 
232         /* check if intermediate cert */
233         if (i < certPathLength) {
234             // RFC5280: If certificate i is a version 3 certificate, verify
235             // that the basicConstraints extension is present and that cA is
236             // set to TRUE.  (If certificate i is a version 1 or version 2
237             // certificate, then the application MUST either verify that
238             // certificate i is a CA certificate through out-of-band means
239             // or reject the certificate.  Conforming implementations may
240             // choose to reject all version 1 and version 2 intermediate
241             // certificates.)
242             //
243             // We choose to reject all version 1 and version 2 intermediate
244             // certificates except that it is self issued by the trust
245             // anchor in order to support key rollover or changes in
246             // certificate policies.
247             int pathLenConstraint = -1;
248             if (currCert.getVersion() < 3) {    // version 1 or version 2
249                 if (i == 1) {                   // issued by a trust anchor
250                     if (X509CertImpl.isSelfIssued(currCert)) {
251                         pathLenConstraint = Integer.MAX_VALUE;
252                     }
253                 }
254             } else {
255                 pathLenConstraint = currCert.getBasicConstraints();
256             }
257 
258             if (pathLenConstraint == -1) {
259                 throw new CertPathValidatorException
260                     (msg + " check failed: this is not a CA certificate",
261                      null, null, -1, PKIXReason.NOT_CA_CERT);
262             }
263 
264             if (!X509CertImpl.isSelfIssued(currCert)) {
265                 if (maxPathLength <= 0) {
266                    throw new CertPathValidatorException
267                         (msg + " check failed: pathLenConstraint violated - "
268                          + "this cert must be the last cert in the "
269                          + "certification path", null, null, -1,
270                          PKIXReason.PATH_TOO_LONG);
271                 }
272                 maxPathLength--;
273             }
274             if (pathLenConstraint < maxPathLength)
275                 maxPathLength = pathLenConstraint;
276         }
277 
278         if (debug != null) {
279             debug.println("after processing, maxPathLength = " + maxPathLength);
280             debug.println(msg + " verified.");
281         }
282     }
283 
284     /**
285      * Merges the specified maxPathLength with the pathLenConstraint
286      * obtained from the certificate.
287      *
288      * @param cert the <code>X509Certificate</code>
289      * @param maxPathLength the previous maximum path length
290      * @return the new maximum path length constraint (-1 means no more
291      * certificates can follow, Integer.MAX_VALUE means path length is
292      * unconstrained)
293      */
mergeBasicConstraints(X509Certificate cert, int maxPathLength)294     static int mergeBasicConstraints(X509Certificate cert, int maxPathLength) {
295 
296         int pathLenConstraint = cert.getBasicConstraints();
297 
298         if (!X509CertImpl.isSelfIssued(cert)) {
299             maxPathLength--;
300         }
301 
302         if (pathLenConstraint < maxPathLength) {
303             maxPathLength = pathLenConstraint;
304         }
305 
306         return maxPathLength;
307     }
308 }
309