1 /*
   2  * Copyright (c) 2016, 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 import java.io.FileInputStream;
  25 import java.io.IOException;
  26 import java.io.InputStream;
  27 import java.io.OutputStream;
  28 import java.net.InetSocketAddress;
  29 import java.net.SocketTimeoutException;
  30 import java.security.KeyStore;
  31 import java.util.Arrays;
  32 import java.util.concurrent.CountDownLatch;
  33 import java.util.concurrent.TimeUnit;
  34 import javax.net.ssl.SSLContext;
  35 import javax.net.ssl.SSLServerSocket;
  36 import javax.net.ssl.SSLServerSocketFactory;
  37 import javax.net.ssl.SSLSocket;
  38 import javax.net.ssl.SSLSocketFactory;
  39 
  40 /**
  41  * Helper class for JSSE tests.
  42  *
  43  * Please run in othervm mode.  SunJSSE does not support dynamic system
  44  * properties, no way to re-use system properties in samevm/agentvm mode.
  45  */
  46 public class SSLTest {
  47 
  48     public static final String TEST_SRC = System.getProperty("test.src", ".");
  49 
  50     /*
  51      * Where do we find the keystores?
  52      */
  53     public static final String PATH_TO_STORES = "../etc";
  54     public static final String KEY_STORE_FILE = "keystore";
  55     public static final String TRUST_STORE_FILE = "truststore";
  56     public static final String PASSWORD = "passphrase";
  57 
  58     public static final int FREE_PORT = 0;
  59 
  60     // in seconds
  61     public static final long CLIENT_SIGNAL_TIMEOUT = 30L;
  62     public static final long SERVER_SIGNAL_TIMEOUT = 90L;
  63 
  64     // in millis
  65     public static final int CLIENT_TIMEOUT = 15000;
  66     public static final int SERVER_TIMEOUT = 30000;
  67 
  68     /*
  69      * Should we run the client or server in a separate thread?
  70      * Both sides can throw exceptions, but do you have a preference
  71      * as to which side should be the main thread.
  72      */
  73     private boolean separateServerThread = false;
  74 
  75     /*
  76      * What's the server port?  Use any free port by default
  77      */
  78     private volatile int serverPort;
  79 
  80     private volatile Exception serverException;
  81     private volatile Exception clientException;
  82 
  83     private Thread clientThread;
  84     private Thread serverThread;
  85 
  86     private Peer serverPeer;
  87     private Peer clientPeer;
  88 
  89     private Application serverApplication;
  90     private Application clientApplication;
  91 
  92     private SSLContext context;
  93 
  94     /*
  95      * Is the server ready to serve?
  96      */
  97     private final CountDownLatch serverCondition = new CountDownLatch(1);
  98 
  99     /*
 100      * Is the client ready to handshake?
 101      */
 102     private final CountDownLatch clientCondition = new CountDownLatch(1);
 103 
 104     /*
 105      * Public API.
 106      */
 107 
 108     public static interface Peer {
 109         void run(SSLTest test) throws Exception;
 110     }
 111 
 112     public static interface Application {
 113         void run(SSLSocket socket, SSLTest test) throws Exception;
 114     }
 115 
 116     public static void debug() {
 117         debug("ssl");
 118     }
 119 
 120     public static void debug(String mode) {
 121         System.setProperty("javax.net.debug", mode);
 122     }
 123 
 124     public static void setup(String keyFilename, String trustFilename,
 125             String password) {
 126 
 127         System.setProperty("javax.net.ssl.keyStore", keyFilename);
 128         System.setProperty("javax.net.ssl.keyStorePassword", password);
 129         System.setProperty("javax.net.ssl.trustStore", trustFilename);
 130         System.setProperty("javax.net.ssl.trustStorePassword", password);
 131     }
 132 
 133     public static void setup() throws Exception {
 134         String keyFilename = TEST_SRC + "/" + PATH_TO_STORES + "/"
 135                 + KEY_STORE_FILE;
 136         String trustFilename = TEST_SRC + "/" + PATH_TO_STORES + "/"
 137                 + TRUST_STORE_FILE;
 138 
 139         setup(keyFilename, trustFilename, PASSWORD);
 140     }
 141 
 142     public static void print(String message, Throwable... errors) {
 143         synchronized (System.out) {
 144             System.out.println(message);
 145             Arrays.stream(errors).forEach(e -> e.printStackTrace(System.out));
 146         }
 147     }
 148 
 149     public static KeyStore loadJksKeyStore(String filename, String password)
 150             throws Exception {
 151 
 152         return loadKeyStore(filename, password, "JKS");
 153     }
 154 
 155     public static KeyStore loadKeyStore(String filename, String password,
 156             String type) throws Exception {
 157 
 158         KeyStore keystore = KeyStore.getInstance(type);
 159         try (FileInputStream fis = new FileInputStream(filename)) {
 160             keystore.load(fis, password.toCharArray());
 161         }
 162         return keystore;
 163     }
 164 
 165     public SSLTest setSeparateServerThread(boolean separateServerThread) {
 166         this.separateServerThread = separateServerThread;
 167         return this;
 168     }
 169 
 170     public SSLTest setServerPort(int serverPort) {
 171         this.serverPort = serverPort;
 172         return this;
 173     }
 174 
 175     public int getServerPort() {
 176         return serverPort;
 177     }
 178 
 179     public SSLTest setSSLContext(SSLContext context) {
 180         this.context = context;
 181         return this;
 182     }
 183 
 184     public SSLContext getSSLContext() {
 185         return context;
 186     }
 187 
 188     public SSLServerSocketFactory getSSLServerSocketFactory() {
 189         if (context != null) {
 190             return context.getServerSocketFactory();
 191         }
 192 
 193         return (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
 194     }
 195 
 196     public SSLSocketFactory getSSLSocketFactory() {
 197         if (context != null) {
 198             return context.getSocketFactory();
 199         }
 200 
 201         return (SSLSocketFactory) SSLSocketFactory.getDefault();
 202     }
 203 
 204     public void signalServerReady() {
 205         serverCondition.countDown();
 206     }
 207 
 208     public boolean waitForClientSignal(long timeout, TimeUnit unit)
 209             throws InterruptedException {
 210 
 211         return clientCondition.await(timeout, unit);
 212     }
 213 
 214     public boolean waitForClientSignal() throws InterruptedException {
 215         return waitForClientSignal(CLIENT_SIGNAL_TIMEOUT, TimeUnit.SECONDS);
 216     }
 217 
 218     public void signalClientReady() {
 219         clientCondition.countDown();
 220     }
 221 
 222     public boolean waitForServerSignal(long timeout, TimeUnit unit)
 223             throws InterruptedException {
 224 
 225         return serverCondition.await(timeout, unit);
 226     }
 227 
 228     public boolean waitForServerSignal() throws InterruptedException {
 229         return waitForServerSignal(SERVER_SIGNAL_TIMEOUT, TimeUnit.SECONDS);
 230     }
 231 
 232     public SSLTest setServerPeer(Peer serverPeer) {
 233         this.serverPeer = serverPeer;
 234         return this;
 235     }
 236 
 237     public Peer getServerPeer() {
 238         return serverPeer;
 239     }
 240 
 241     public SSLTest setServerApplication(Application serverApplication) {
 242         this.serverApplication = serverApplication;
 243         return this;
 244     }
 245 
 246     public Application getServerApplication() {
 247         return serverApplication;
 248     }
 249 
 250     public SSLTest setClientPeer(Peer clientPeer) {
 251         this.clientPeer = clientPeer;
 252         return this;
 253     }
 254 
 255     public Peer getClientPeer() {
 256         return clientPeer;
 257     }
 258 
 259     public SSLTest setClientApplication(Application clientApplication) {
 260         this.clientApplication = clientApplication;
 261         return this;
 262     }
 263 
 264     public Application getClientApplication() {
 265         return clientApplication;
 266     }
 267 
 268     public void runTest() throws Exception {
 269         if (separateServerThread) {
 270             startServer(true, this);
 271             startClient(false, this);
 272             serverThread.join();
 273         } else {
 274             startClient(true, this);
 275             startServer(false, this);
 276             clientThread.join();
 277         }
 278 
 279         if (clientException != null || serverException != null) {
 280             throw new RuntimeException("Test failed");
 281         }
 282     }
 283 
 284     public SSLTest() {
 285         serverPeer = (test) -> doServerSide(test);
 286         clientPeer = (test) -> doClientSide(test);
 287         serverApplication = (socket, test) -> runServerApplication(socket);
 288         clientApplication = (socket, test) -> runClientApplication(socket);
 289     }
 290 
 291     /*
 292      * Private part.
 293      */
 294 
 295 
 296     /*
 297      * Define the server side of the test.
 298      */
 299     private static void doServerSide(SSLTest test) throws Exception {
 300         SSLServerSocket sslServerSocket;
 301 
 302         // kick start the server side service
 303         SSLServerSocketFactory sslssf = test.getSSLServerSocketFactory();
 304         sslServerSocket = (SSLServerSocket)sslssf.createServerSocket(FREE_PORT);
 305 
 306         test.setServerPort(sslServerSocket.getLocalPort());
 307         print("Server is listening on port " + test.getServerPort());
 308 
 309         // Signal the client, the server is ready to accept connection.
 310         test.signalServerReady();
 311 
 312         // Try to accept a connection in 30 seconds.
 313         SSLSocket sslSocket;
 314         try {
 315             sslServerSocket.setSoTimeout(SERVER_TIMEOUT);
 316             sslSocket = (SSLSocket) sslServerSocket.accept();
 317             print("Server accepted connection");
 318         } catch (SocketTimeoutException ste) {
 319             sslServerSocket.close();
 320 
 321             // Ignore the test case if no connection within 30 seconds.
 322             print("No incoming client connection in 30 seconds. "
 323                     + "Ignore in server side.", ste);
 324             return;
 325         }
 326 
 327         // handle the connection
 328         try {
 329             // Is it the expected client connection?
 330             //
 331             // Naughty test cases or third party routines may try to
 332             // connection to this server port unintentionally.  In
 333             // order to mitigate the impact of unexpected client
 334             // connections and avoid intermittent failure, it should
 335             // be checked that the accepted connection is really linked
 336             // to the expected client.
 337             boolean clientIsReady = test.waitForClientSignal();
 338 
 339             if (clientIsReady) {
 340                 // Run the application in server side.
 341                 print("Run server application");
 342                 test.getServerApplication().run(sslSocket, test);
 343             } else {    // Otherwise, ignore
 344                 // We don't actually care about plain socket connections
 345                 // for TLS communication testing generally.  Just ignore
 346                 // the test if the accepted connection is not linked to
 347                 // the expected client or the client connection timeout
 348                 // in 30 seconds.
 349                 print("The client is not the expected one or timeout. "
 350                         + "Ignore in server side.");
 351             }
 352         } finally {
 353             sslSocket.close();
 354             sslServerSocket.close();
 355         }
 356     }
 357 
 358     /*
 359      * Define the server side application of the test for the specified socket.
 360      */
 361     private static void runServerApplication(SSLSocket socket)
 362             throws Exception {
 363 
 364         // here comes the test logic
 365         InputStream sslIS = socket.getInputStream();
 366         OutputStream sslOS = socket.getOutputStream();
 367 
 368         sslIS.read();
 369         sslOS.write(85);
 370         sslOS.flush();
 371     }
 372 
 373     /*
 374      * Define the client side of the test.
 375      */
 376     private static void doClientSide(SSLTest test) throws Exception {
 377 
 378         // Wait for server to get started.
 379         //
 380         // The server side takes care of the issue if the server cannot
 381         // get started in 90 seconds.  The client side would just ignore
 382         // the test case if the serer is not ready.
 383         boolean serverIsReady = test.waitForServerSignal();
 384         if (!serverIsReady) {
 385             print("The server is not ready yet in 90 seconds. "
 386                     + "Ignore in client side.");
 387             return;
 388         }
 389 
 390         SSLSocketFactory sslsf = test.getSSLSocketFactory();
 391         try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {
 392             try {
 393                 sslSocket.connect(
 394                         new InetSocketAddress("localhost",
 395                                 test.getServerPort()), CLIENT_TIMEOUT);
 396                 print("Client connected to server");
 397             } catch (IOException ioe) {
 398                 // The server side may be impacted by naughty test cases or
 399                 // third party routines, and cannot accept connections.
 400                 //
 401                 // Just ignore the test if the connection cannot be
 402                 // established.
 403                 print("Cannot make a connection in 15 seconds. "
 404                         + "Ignore in client side.", ioe);
 405                 return;
 406             }
 407 
 408             // OK, here the client and server get connected.
 409 
 410             // Signal the server, the client is ready to communicate.
 411             test.signalClientReady();
 412 
 413             // There is still a chance in theory that the server thread may
 414             // wait client-ready timeout and then quit.  The chance should
 415             // be really rare so we don't consider it until it becomes a
 416             // real problem.
 417 
 418             // Run the application in client side.
 419             print("Run client application");
 420             test.getClientApplication().run(sslSocket, test);
 421         }
 422     }
 423 
 424     /*
 425      * Define the client side application of the test for the specified socket.
 426      */
 427     private static void runClientApplication(SSLSocket socket)
 428             throws Exception {
 429 
 430         InputStream sslIS = socket.getInputStream();
 431         OutputStream sslOS = socket.getOutputStream();
 432 
 433         sslOS.write(280);
 434         sslOS.flush();
 435         sslIS.read();
 436     }
 437 
 438     private void startServer(boolean newThread, SSLTest test) throws Exception {
 439         if (newThread) {
 440             serverThread = new Thread() {
 441                 @Override
 442                 public void run() {
 443                     try {
 444                         serverPeer.run(test);
 445                     } catch (Exception e) {
 446                         /*
 447                          * Our server thread just died.
 448                          *
 449                          * Release the client, if not active already...
 450                          */
 451                         print("Server died ...", e);
 452                         serverException = e;
 453                     }
 454                 }
 455             };
 456             serverThread.start();
 457         } else {
 458             try {
 459                 serverPeer.run(test);
 460             } catch (Exception e) {
 461                 print("Server failed ...", e);
 462                 serverException = e;
 463             }
 464         }
 465     }
 466 
 467     private void startClient(boolean newThread, SSLTest test) throws Exception {
 468         if (newThread) {
 469             clientThread = new Thread() {
 470                 @Override
 471                 public void run() {
 472                     try {
 473                         clientPeer.run(test);
 474                     } catch (Exception e) {
 475                         /*
 476                          * Our client thread just died.
 477                          */
 478                         print("Client died ...", e);
 479                         clientException = e;
 480                     }
 481                 }
 482             };
 483             clientThread.start();
 484         } else {
 485             try {
 486                 clientPeer.run(test);
 487             } catch (Exception e) {
 488                 print("Client failed ...", e);
 489                 clientException = e;
 490             }
 491         }
 492     }
 493 }