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