1 /*
   2  * Copyright (c) 2012, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 //
  25 // SunJSSE does not support dynamic system properties, no way to re-use
  26 // system properties in samevm/agentvm mode.
  27 //
  28 
  29 /*
  30  * @test
  31  * @bug 7200295
  32  * @summary CertificateRequest message is wrapping when using large
  33  *          numbers of Certs
  34  * @run main/othervm CertRequestOverflow
  35  */
  36 
  37 import java.io.*;
  38 import java.net.*;
  39 import java.util.*;
  40 import javax.net.ssl.*;
  41 import java.security.cert.*;
  42 import java.security.*;
  43 
  44 public class CertRequestOverflow {
  45 
  46     /*
  47      * =============================================================
  48      * Set the various variables needed for the tests, then
  49      * specify what tests to run on each side.
  50      */
  51 
  52     /*
  53      * Should we run the client or server in a separate thread?
  54      * Both sides can throw exceptions, but do you have a preference
  55      * as to which side should be the main thread.
  56      */
  57     static boolean separateServerThread = false;
  58 
  59     /*
  60      * Where do we find the keystores?
  61      */
  62     static String pathToStores = "../../../../javax/net/ssl/etc";
  63     static String keyStoreFile = "keystore";
  64     static String trustStoreFile = "truststore";
  65     static String passwd = "passphrase";
  66     private final static char[] cpasswd = "passphrase".toCharArray();
  67 
  68     /*
  69      * Is the server ready to serve?
  70      */
  71     volatile static boolean serverReady = false;
  72 
  73     /*
  74      * Turn on SSL debugging?
  75      */
  76     static boolean debug = false;
  77 
  78     /*
  79      * If the client or server is doing some kind of object creation
  80      * that the other side depends on, and that thread prematurely
  81      * exits, you may experience a hang.  The test harness will
  82      * terminate all hung threads after its timeout has expired,
  83      * currently 3 minutes by default, but you might try to be
  84      * smart about it....
  85      */
  86 
  87     /*
  88      * Define the server side of the test.
  89      *
  90      * If the server prematurely exits, serverReady will be set to true
  91      * to avoid infinite hangs.
  92      */
  93     void doServerSide() throws Exception {
  94         SSLServerSocketFactory sslssf =
  95                                 getContext(true).getServerSocketFactory();
  96         SSLServerSocket sslServerSocket =
  97             (SSLServerSocket) sslssf.createServerSocket(serverPort);
  98         serverPort = sslServerSocket.getLocalPort();
  99 
 100         // enable endpoint identification
 101         // ignore, we may test the feature when known how to parse client
 102         // hostname
 103         //SSLParameters params = sslServerSocket.getSSLParameters();
 104         //params.setEndpointIdentificationAlgorithm("HTTPS");
 105         //sslServerSocket.setSSLParameters(params);
 106 
 107         /*
 108          * Signal Client, we're ready for his connect.
 109          */
 110         serverReady = true;
 111 
 112         SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
 113         sslSocket.setNeedClientAuth(true);
 114         InputStream sslIS = sslSocket.getInputStream();
 115         OutputStream sslOS = sslSocket.getOutputStream();
 116 
 117         try {
 118             sslIS.read();
 119             sslOS.write(85);
 120             sslOS.flush();
 121 
 122             throw new Exception("SERVER TEST FAILED!  " +
 123                         "It is expected to fail with field length overflow");
 124         } catch (SSLException ssle) {
 125             Throwable cause = ssle.getCause();
 126             if (!(cause instanceof RuntimeException)) {
 127                 System.out.println("We are expecting a RuntimeException!");
 128                 throw ssle;
 129             }
 130             System.out.println("The expected exception!  " + ssle);
 131         } finally {
 132             sslSocket.close();
 133         }
 134 
 135         System.out.println("SERVER TEST PASSED!");
 136     }
 137 
 138     /*
 139      * Define the client side of the test.
 140      *
 141      * If the server prematurely exits, serverReady will be set to true
 142      * to avoid infinite hangs.
 143      */
 144     void doClientSide() throws Exception {
 145 
 146         /*
 147          * Wait for server to get started.
 148          */
 149         while (!serverReady) {
 150             Thread.sleep(50);
 151         }
 152 
 153         SSLSocketFactory sslsf = getContext(false).getSocketFactory();
 154         SSLSocket sslSocket = (SSLSocket)
 155             sslsf.createSocket("localhost", serverPort);
 156 
 157         // enable endpoint identification
 158         SSLParameters params = sslSocket.getSSLParameters();
 159         params.setEndpointIdentificationAlgorithm("HTTPS");
 160         sslSocket.setSSLParameters(params);
 161 
 162         InputStream sslIS = sslSocket.getInputStream();
 163         OutputStream sslOS = sslSocket.getOutputStream();
 164 
 165         try {
 166             sslOS.write(280);
 167             sslOS.flush();
 168             sslIS.read();
 169         } catch (SSLException ssle) {
 170             System.out.println("An expected exception!");
 171         } finally {
 172             sslSocket.close();
 173         }
 174     }
 175 
 176     MyExtendedX509TM serverTM;
 177     MyExtendedX509TM clientTM;
 178 
 179     private SSLContext getContext(boolean server) throws Exception {
 180         String keyFilename =
 181             System.getProperty("test.src", "./") + "/" + pathToStores +
 182                 "/" + keyStoreFile;
 183         String trustFilename =
 184             System.getProperty("test.src", "./") + "/" + pathToStores +
 185                 "/" + trustStoreFile;
 186 
 187         KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
 188         KeyStore ks = KeyStore.getInstance("JKS");
 189         ks.load(new FileInputStream(keyFilename), cpasswd);
 190         kmf.init(ks, cpasswd);
 191 
 192         TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
 193         KeyStore ts = KeyStore.getInstance("JKS");
 194         ts.load(new FileInputStream(trustFilename), cpasswd);
 195         tmf.init(ts);
 196 
 197         TrustManager tms[] = tmf.getTrustManagers();
 198         if (tms == null || tms.length == 0) {
 199             throw new Exception("unexpected trust manager implementation");
 200         } else {
 201            if (!(tms[0] instanceof X509TrustManager)) {
 202             throw new Exception("unexpected trust manager implementation: "
 203                                 + tms[0].getClass().getCanonicalName());
 204            }
 205         }
 206 
 207         if (server) {
 208             serverTM = new MyExtendedX509TM((X509TrustManager)tms[0]);
 209 
 210             tms = new TrustManager[] {serverTM};
 211         } else {
 212             clientTM = new MyExtendedX509TM((X509TrustManager)tms[0]);
 213 
 214             tms = new TrustManager[] {clientTM};
 215         }
 216 
 217         SSLContext ctx = SSLContext.getInstance("TLS");
 218         ctx.init(kmf.getKeyManagers(), tms, null);
 219 
 220         return ctx;
 221     }
 222 
 223     static class MyExtendedX509TM extends X509ExtendedTrustManager
 224             implements X509TrustManager {
 225 
 226         X509TrustManager tm;
 227 
 228         boolean clientChecked;
 229         boolean serverChecked;
 230 
 231         MyExtendedX509TM(X509TrustManager tm) {
 232             clientChecked = false;
 233             serverChecked = false;
 234 
 235             this.tm = tm;
 236         }
 237 
 238         public boolean wasClientChecked() {
 239             return clientChecked;
 240         }
 241 
 242         public boolean wasServerChecked() {
 243             return serverChecked;
 244         }
 245 
 246 
 247         public void checkClientTrusted(X509Certificate chain[], String authType)
 248                 throws CertificateException {
 249             tm.checkClientTrusted(chain, authType);
 250         }
 251 
 252         public void checkServerTrusted(X509Certificate chain[], String authType)
 253                 throws CertificateException {
 254             tm.checkServerTrusted(chain, authType);
 255         }
 256 
 257         public X509Certificate[] getAcceptedIssuers() {
 258             // (hack code) increase the size of the returned array to make a
 259             // overflow CertificateRequest.
 260             List<X509Certificate> issuersList = new LinkedList<>();
 261             X509Certificate[] issuers = tm.getAcceptedIssuers();
 262             for (int i = 0; i < 800; i += issuers.length) {
 263                 for (X509Certificate issuer : issuers) {
 264                     issuersList.add(issuer);
 265                 }
 266             }
 267 
 268             return issuersList.toArray(issuers);
 269         }
 270 
 271         public void checkClientTrusted(X509Certificate[] chain, String authType,
 272                 Socket socket) throws CertificateException {
 273             clientChecked = true;
 274             tm.checkClientTrusted(chain, authType);
 275         }
 276 
 277         public void checkServerTrusted(X509Certificate[] chain, String authType,
 278                 Socket socket) throws CertificateException {
 279             serverChecked = true;
 280             tm.checkServerTrusted(chain, authType);
 281         }
 282 
 283         public void checkClientTrusted(X509Certificate[] chain, String authType,
 284             SSLEngine engine) throws CertificateException {
 285             clientChecked = true;
 286             tm.checkClientTrusted(chain, authType);
 287         }
 288 
 289         public void checkServerTrusted(X509Certificate[] chain, String authType,
 290             SSLEngine engine) throws CertificateException {
 291             serverChecked = true;
 292             tm.checkServerTrusted(chain, authType);
 293         }
 294     }
 295 
 296     /*
 297      * =============================================================
 298      * The remainder is just support stuff
 299      */
 300 
 301     // use any free port by default
 302     volatile int serverPort = 0;
 303 
 304     volatile Exception serverException = null;
 305     volatile Exception clientException = null;
 306 
 307     public static void main(String[] args) throws Exception {
 308 
 309         if (debug)
 310             System.setProperty("javax.net.debug", "all");
 311 
 312         /*
 313          * Start the tests.
 314          */
 315         new CertRequestOverflow();
 316     }
 317 
 318     Thread clientThread = null;
 319     Thread serverThread = null;
 320 
 321     /*
 322      * Primary constructor, used to drive remainder of the test.
 323      *
 324      * Fork off the other side, then do your work.
 325      */
 326     CertRequestOverflow() throws Exception {
 327         if (separateServerThread) {
 328             startServer(true);
 329             startClient(false);
 330         } else {
 331             startClient(true);
 332             startServer(false);
 333         }
 334 
 335         /*
 336          * Wait for other side to close down.
 337          */
 338         if (separateServerThread) {
 339             serverThread.join();
 340         } else {
 341             clientThread.join();
 342         }
 343 
 344         /*
 345          * When we get here, the test is pretty much over.
 346          *
 347          * If the main thread excepted, that propagates back
 348          * immediately.  If the other thread threw an exception, we
 349          * should report back.
 350          */
 351         if (serverException != null)
 352             throw serverException;
 353         if (clientException != null)
 354             throw clientException;
 355     }
 356 
 357     void startServer(boolean newThread) throws Exception {
 358         if (newThread) {
 359             serverThread = new Thread() {
 360                 public void run() {
 361                     try {
 362                         doServerSide();
 363                     } catch (Exception e) {
 364                         /*
 365                          * Our server thread just died.
 366                          *
 367                          * Release the client, if not active already...
 368                          */
 369                         System.err.println("Server died...");
 370                         serverReady = true;
 371                         serverException = e;
 372                     }
 373                 }
 374             };
 375             serverThread.start();
 376         } else {
 377             doServerSide();
 378         }
 379     }
 380 
 381     void startClient(boolean newThread) throws Exception {
 382         if (newThread) {
 383             clientThread = new Thread() {
 384                 public void run() {
 385                     try {
 386                         doClientSide();
 387                     } catch (Exception e) {
 388                         /*
 389               * Our client thread just died.
 390                          */
 391                         System.err.println("Client died...");
 392                         clientException = e;
 393                     }
 394                 }
 395             };
 396             clientThread.start();
 397         } else {
 398             doClientSide();
 399         }
 400     }
 401 }
 402