1 /*
  2  * Copyright (c) 1997, 2022, 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.ssl;
 27 
 28 import java.lang.invoke.MethodHandles;
 29 import java.net.Socket;
 30 import java.security.*;
 31 import java.security.cert.*;
 32 import java.util.*;
 33 import java.util.concurrent.locks.ReentrantLock;
 34 import javax.net.ssl.*;
 35 import sun.security.util.AnchorCertificates;
 36 import sun.security.util.HostnameChecker;
 37 import sun.security.validator.*;
 38 
 39 /**
 40  * This class implements the SunJSSE X.509 trust manager using the internal
 41  * validator API in J2SE core. The logic in this class is minimal.<p>
 42  * <p>
 43  * This class supports both the Simple validation algorithm from previous
 44  * JSSE versions and PKIX validation. Currently, it is not possible for the
 45  * application to specify PKIX parameters other than trust anchors. This will
 46  * be fixed in a future release using new APIs. When that happens, it may also
 47  * make sense to separate the Simple and PKIX trust managers into separate
 48  * classes.
 49  *
 50  * @author Andreas Sterbenz
 51  */
 52 final class X509TrustManagerImpl extends X509ExtendedTrustManager
 53         implements X509TrustManager {
 54 
 55     static {
 56         try {
 57             MethodHandles.lookup().ensureInitialized(AnchorCertificates.class);
 58         } catch (IllegalAccessException e) {
 59             throw new ExceptionInInitializerError(e);
 60         }
 61     }
 62 
 63     private final String validatorType;
 64 
 65     /**
 66      * The Set of trusted X509Certificates.
 67      */
 68     private final Collection<X509Certificate> trustedCerts;
 69 
 70     private final PKIXBuilderParameters pkixParams;
 71 
 72     // note that we need separate validator for client and server due to
 73     // the different extension checks. They are initialized lazily on demand.
 74     private volatile Validator clientValidator, serverValidator;
 75 
 76     private final ReentrantLock validatorLock = new ReentrantLock();
 77 
 78     X509TrustManagerImpl(String validatorType,
 79             Collection<X509Certificate> trustedCerts) {
 80 
 81         this.validatorType = validatorType;
 82         this.pkixParams = null;
 83 
 84         if (trustedCerts == null) {
 85             trustedCerts = Collections.emptySet();
 86         }
 87 
 88         this.trustedCerts = trustedCerts;
 89 
 90         if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
 91             SSLLogger.fine("adding as trusted certificates",
 92                     (Object[])trustedCerts.toArray(new X509Certificate[0]));
 93         }
 94     }
 95 
 96     X509TrustManagerImpl(String validatorType, PKIXBuilderParameters params) {
 97         this.validatorType = validatorType;
 98         this.pkixParams = params;
 99         // create server validator eagerly so that we can conveniently
100         // get the trusted certificates
101         // clients need it anyway eventually, and servers will not mind
102         // the little extra footprint
103         Validator v = getValidator(Validator.VAR_TLS_SERVER);
104         trustedCerts = v.getTrustedCertificates();
105         serverValidator = v;
106 
107         if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
108             SSLLogger.fine("adding as trusted certificates",
109                     (Object[])trustedCerts.toArray(new X509Certificate[0]));
110         }
111     }
112 
113     @Override
114     public void checkClientTrusted(X509Certificate[] chain, String authType)
115             throws CertificateException {
116         checkTrusted(chain, authType, (Socket)null, true);
117     }
118 
119     @Override
120     public void checkServerTrusted(X509Certificate[] chain, String authType)
121             throws CertificateException {
122         checkTrusted(chain, authType, (Socket)null, false);
123     }
124 
125     @Override
126     public X509Certificate[] getAcceptedIssuers() {
127         X509Certificate[] certsArray = new X509Certificate[trustedCerts.size()];
128         trustedCerts.toArray(certsArray);
129         return certsArray;
130     }
131 
132     @Override
133     public void checkClientTrusted(X509Certificate[] chain, String authType,
134                 Socket socket) throws CertificateException {
135         checkTrusted(chain, authType, socket, true);
136     }
137 
138     @Override
139     public void checkServerTrusted(X509Certificate[] chain, String authType,
140             Socket socket) throws CertificateException {
141         checkTrusted(chain, authType, socket, false);
142     }
143 
144     @Override
145     public void checkClientTrusted(X509Certificate[] chain, String authType,
146             SSLEngine engine) throws CertificateException {
147         checkTrusted(chain, authType, engine, true);
148     }
149 
150     @Override
151     public void checkServerTrusted(X509Certificate[] chain, String authType,
152             SSLEngine engine) throws CertificateException {
153         checkTrusted(chain, authType, engine, false);
154     }
155 
156     private Validator checkTrustedInit(X509Certificate[] chain,
157             String authType, boolean checkClientTrusted) {
158         if (chain == null || chain.length == 0) {
159             throw new IllegalArgumentException(
160                 "null or zero-length certificate chain");
161         }
162 
163         if (authType == null || authType.isEmpty()) {
164             throw new IllegalArgumentException(
165                 "null or zero-length authentication type");
166         }
167 
168         Validator v;
169         if (checkClientTrusted) {
170             v = clientValidator;
171             if (v == null) {
172                 validatorLock.lock();
173                 try {
174                     v = clientValidator;
175                     if (v == null) {
176                         v = getValidator(Validator.VAR_TLS_CLIENT);
177                         clientValidator = v;
178                     }
179                 } finally {
180                     validatorLock.unlock();
181                 }
182             }
183         } else {
184             // assume double-checked locking with a volatile flag works
185             // (guaranteed under the new Tiger memory model)
186             v = serverValidator;
187             if (v == null) {
188                 validatorLock.lock();
189                 try {
190                     v = serverValidator;
191                     if (v == null) {
192                         v = getValidator(Validator.VAR_TLS_SERVER);
193                         serverValidator = v;
194                     }
195                 } finally {
196                     validatorLock.unlock();
197                 }
198             }
199         }
200 
201         return v;
202     }
203 
204     private void checkTrusted(X509Certificate[] chain,
205             String authType, Socket socket,
206             boolean checkClientTrusted) throws CertificateException {
207         Validator v = checkTrustedInit(chain, authType, checkClientTrusted);
208 
209         X509Certificate[] trustedChain;
210         if ((socket != null) && socket.isConnected() &&
211                 (socket instanceof SSLSocket sslSocket)) {
212 
213             SSLSession session = sslSocket.getHandshakeSession();
214             if (session == null) {
215                 throw new CertificateException("No handshake session");
216             }
217 
218             // create the algorithm constraints
219             boolean isExtSession = (session instanceof ExtendedSSLSession);
220             AlgorithmConstraints constraints;
221             if (isExtSession &&
222                     ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
223                 ExtendedSSLSession extSession = (ExtendedSSLSession)session;
224                 String[] localSupportedSignAlgs =
225                         extSession.getLocalSupportedSignatureAlgorithms();
226 
227                 constraints = SSLAlgorithmConstraints.forSocket(
228                                 sslSocket, localSupportedSignAlgs, false);
229             } else {
230                 constraints = SSLAlgorithmConstraints.forSocket(sslSocket, false);
231             }
232 
233             // Grab any stapled OCSP responses for use in validation
234             List<byte[]> responseList = Collections.emptyList();
235             if (!checkClientTrusted && isExtSession) {
236                 responseList =
237                         ((ExtendedSSLSession)session).getStatusResponses();
238             }
239             trustedChain = v.validate(chain, null, responseList,
240                     constraints, checkClientTrusted ? null : authType);
241 
242             // check endpoint identity
243             String identityAlg = sslSocket.getSSLParameters().
244                     getEndpointIdentificationAlgorithm();
245             if (identityAlg != null && !identityAlg.isEmpty()) {
246                 checkIdentity(session,
247                         trustedChain, identityAlg, checkClientTrusted);
248             }
249         } else {
250             trustedChain = v.validate(chain, null, Collections.emptyList(),
251                     null, checkClientTrusted ? null : authType);
252         }
253 
254         if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
255             SSLLogger.fine("Found trusted certificate",
256                     trustedChain[trustedChain.length - 1]);
257         }
258     }
259 
260     private void checkTrusted(X509Certificate[] chain,
261             String authType, SSLEngine engine,
262             boolean checkClientTrusted) throws CertificateException {
263         Validator v = checkTrustedInit(chain, authType, checkClientTrusted);
264 
265         X509Certificate[] trustedChain;
266         if (engine != null) {
267             SSLSession session = engine.getHandshakeSession();
268             if (session == null) {
269                 throw new CertificateException("No handshake session");
270             }
271 
272             // create the algorithm constraints
273             boolean isExtSession = (session instanceof ExtendedSSLSession);
274             AlgorithmConstraints constraints;
275             if (isExtSession &&
276                     ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
277                 ExtendedSSLSession extSession = (ExtendedSSLSession)session;
278                 String[] localSupportedSignAlgs =
279                         extSession.getLocalSupportedSignatureAlgorithms();
280 
281                 constraints = SSLAlgorithmConstraints.forEngine(
282                                 engine, localSupportedSignAlgs, false);
283             } else {
284                 constraints = SSLAlgorithmConstraints.forEngine(engine, false);
285             }
286 
287             // Grab any stapled OCSP responses for use in validation
288             List<byte[]> responseList = Collections.emptyList();
289             if (!checkClientTrusted && isExtSession) {
290                 responseList =
291                         ((ExtendedSSLSession)session).getStatusResponses();
292             }
293             trustedChain = v.validate(chain, null, responseList,
294                     constraints, checkClientTrusted ? null : authType);
295 
296             // check endpoint identity
297             String identityAlg = engine.getSSLParameters().
298                     getEndpointIdentificationAlgorithm();
299             if (identityAlg != null && !identityAlg.isEmpty()) {
300                 checkIdentity(session, trustedChain,
301                         identityAlg, checkClientTrusted);
302             }
303         } else {
304             trustedChain = v.validate(chain, null, Collections.emptyList(),
305                     null, checkClientTrusted ? null : authType);
306         }
307 
308         if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
309             SSLLogger.fine("Found trusted certificate",
310                     trustedChain[trustedChain.length - 1]);
311         }
312     }
313 
314     private Validator getValidator(String variant) {
315         Validator v;
316         if (pkixParams == null) {
317             v = Validator.getInstance(validatorType, variant, trustedCerts);
318         } else {
319             v = Validator.getInstance(validatorType, variant, pkixParams);
320         }
321         return v;
322     }
323 
324     // Get string representation of HostName from a list of server names.
325     //
326     // We are only accepting host_name name type in the list.
327     private static String getHostNameInSNI(List<SNIServerName> sniNames) {
328 
329         SNIHostName hostname = null;
330         for (SNIServerName sniName : sniNames) {
331             if (sniName.getType() != StandardConstants.SNI_HOST_NAME) {
332                 continue;
333             }
334 
335             if (sniName instanceof SNIHostName) {
336                 hostname = (SNIHostName)sniName;
337             } else {
338                 try {
339                     hostname = new SNIHostName(sniName.getEncoded());
340                 } catch (IllegalArgumentException iae) {
341                     // unlikely to happen, just in case ...
342                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
343                         SSLLogger.fine("Illegal server name: " + sniName);
344                     }
345                 }
346             }
347 
348             // no more than server name of the same name type
349             break;
350         }
351 
352         if (hostname != null) {
353             return hostname.getAsciiName();
354         }
355 
356         return null;
357     }
358 
359     // Also used by X509KeyManagerImpl
360     static List<SNIServerName> getRequestedServerNames(Socket socket) {
361         if (socket != null && socket.isConnected() &&
362                                         socket instanceof SSLSocket) {
363             return getRequestedServerNames(
364                     ((SSLSocket)socket).getHandshakeSession());
365         }
366 
367         return Collections.emptyList();
368     }
369 
370     // Also used by X509KeyManagerImpl
371     static List<SNIServerName> getRequestedServerNames(SSLEngine engine) {
372         if (engine != null) {
373             return getRequestedServerNames(engine.getHandshakeSession());
374         }
375 
376         return Collections.emptyList();
377     }
378 
379     private static List<SNIServerName> getRequestedServerNames(
380             SSLSession session) {
381         if (session instanceof ExtendedSSLSession) {
382             return ((ExtendedSSLSession)session).getRequestedServerNames();
383         }
384 
385         return Collections.emptyList();
386     }
387 
388     /*
389      * Per RFC 6066, if an application negotiates a server name using an
390      * application protocol and then upgrades to TLS, and if a server_name
391      * extension is sent, then the extension SHOULD contain the same name
392      * that was negotiated in the application protocol.  If the server_name
393      * is established in the TLS session handshake, the client SHOULD NOT
394      * attempt to request a different server name at the application layer.
395      *
396      * According to the above spec, we only need to check either the identity
397      * in server_name extension or the peer host of the connection.  Peer host
398      * is not always a reliable fully qualified domain name. The HostName in
399      * server_name extension is more reliable than peer host. So we prefer
400      * the identity checking against the server_name extension if present, and
401      * may failover to peer host checking.
402      */
403     static void checkIdentity(SSLSession session,
404             X509Certificate[] trustedChain,
405             String algorithm,
406             boolean checkClientTrusted) throws CertificateException {
407 
408         // check if EE certificate chains to a public root CA (as
409         // pre-installed in cacerts)
410         boolean chainsToPublicCA = AnchorCertificates.contains(
411                 trustedChain[trustedChain.length - 1]);
412 
413         boolean identifiable = false;
414         String peerHost = session.getPeerHost();
415         // Is it a Fully-Qualified Domain Names (FQDN) ending with a dot?
416         if (peerHost != null && peerHost.endsWith(".")) {
417             // Remove the ending dot, which is not allowed in SNIHostName.
418             peerHost = peerHost.substring(0, peerHost.length() - 1);
419         }
420 
421         if (!checkClientTrusted) {
422             List<SNIServerName> sniNames = getRequestedServerNames(session);
423             String sniHostName = getHostNameInSNI(sniNames);
424             if (sniHostName != null) {
425                 try {
426                     checkIdentity(sniHostName,
427                             trustedChain[0], algorithm, chainsToPublicCA);
428                     identifiable = true;
429                 } catch (CertificateException ce) {
430                     if (sniHostName.equalsIgnoreCase(peerHost)) {
431                         throw ce;
432                     }
433 
434                     // otherwise, failover to check peer host
435                 }
436             }
437         }
438 
439         if (!identifiable) {
440             checkIdentity(peerHost,
441                     trustedChain[0], algorithm, chainsToPublicCA);
442         }
443     }
444 
445     /*
446      * Identify the peer by its certificate and hostname.
447      *
448      * Lifted from sun.net.www.protocol.https.HttpsClient.
449      */
450     static void checkIdentity(String hostname, X509Certificate cert,
451             String algorithm) throws CertificateException {
452         checkIdentity(hostname, cert, algorithm, false);
453     }
454 
455     private static void checkIdentity(String hostname, X509Certificate cert,
456             String algorithm, boolean chainsToPublicCA)
457             throws CertificateException {
458         if (algorithm != null && !algorithm.isEmpty()) {
459             // if IPv6 strip off the "[]"
460             if ((hostname != null) && hostname.startsWith("[") &&
461                     hostname.endsWith("]")) {
462                 hostname = hostname.substring(1, hostname.length() - 1);
463             }
464 
465             if (algorithm.equalsIgnoreCase("HTTPS")) {
466                 HostnameChecker.getInstance(HostnameChecker.TYPE_TLS).match(
467                         hostname, cert, chainsToPublicCA);
468             } else if (algorithm.equalsIgnoreCase("LDAP") ||
469                     algorithm.equalsIgnoreCase("LDAPS")) {
470                 HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP).match(
471                         hostname, cert, chainsToPublicCA);
472             } else {
473                 throw new CertificateException(
474                         "Unknown identification algorithm: " + algorithm);
475             }
476         }
477     }
478 }
479