1 /*
   2  * Copyright (c) 2011, 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 //
  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 7031830
  32  * @summary bad_record_mac failure on TLSv1.2 enabled connection with SSLEngine
  33  * @run main/othervm SSLEngineBadBufferArrayAccess
  34  */
  35 
  36 /**
  37  * A SSLSocket/SSLEngine interop test case.  This is not the way to
  38  * code SSLEngine-based servers, but works for what we need to do here,
  39  * which is to make sure that SSLEngine/SSLSockets can talk to each other.
  40  * SSLEngines can use direct or indirect buffers, and different code
  41  * is used to get at the buffer contents internally, so we test that here.
  42  *
  43  * The test creates one SSLSocket (client) and one SSLEngine (server).
  44  * The SSLSocket talks to a raw ServerSocket, and the server code
  45  * does the translation between byte [] and ByteBuffers that the SSLEngine
  46  * can use.  The "transport" layer consists of a Socket Input/OutputStream
  47  * and two byte buffers for the SSLEngines:  think of them
  48  * as directly connected pipes.
  49  *
  50  * Again, this is a *very* simple example: real code will be much more
  51  * involved.  For example, different threading and I/O models could be
  52  * used, transport mechanisms could close unexpectedly, and so on.
  53  *
  54  * When this application runs, notice that several messages
  55  * (wrap/unwrap) pass before any application data is consumed or
  56  * produced.  (For more information, please see the SSL/TLS
  57  * specifications.)  There may several steps for a successful handshake,
  58  * so it's typical to see the following series of operations:
  59  *
  60  *      client          server          message
  61  *      ======          ======          =======
  62  *      write()         ...             ClientHello
  63  *      ...             unwrap()        ClientHello
  64  *      ...             wrap()          ServerHello/Certificate
  65  *      read()         ...             ServerHello/Certificate
  66  *      write()         ...             ClientKeyExchange
  67  *      write()         ...             ChangeCipherSpec
  68  *      write()         ...             Finished
  69  *      ...             unwrap()        ClientKeyExchange
  70  *      ...             unwrap()        ChangeCipherSpec
  71  *      ...             unwrap()        Finished
  72  *      ...             wrap()          ChangeCipherSpec
  73  *      ...             wrap()          Finished
  74  *      read()          ...             ChangeCipherSpec
  75  *      read()          ...             Finished
  76  *
  77  * This particular bug had a problem where byte buffers backed by an
  78  * array didn't offset correctly, and we got bad MAC errors.
  79  */
  80 import javax.net.ssl.*;
  81 import javax.net.ssl.SSLEngineResult.*;
  82 import java.io.*;
  83 import java.net.*;
  84 import java.security.*;
  85 import java.nio.*;
  86 import java.util.concurrent.CountDownLatch;
  87 import java.util.concurrent.TimeUnit;
  88 
  89 public class SSLEngineBadBufferArrayAccess {
  90 
  91     /*
  92      * Enables logging of the SSL/TLS operations.
  93      */
  94     private static boolean logging = true;
  95 
  96     /*
  97      * Enables the JSSE system debugging system property:
  98      *
  99      *     -Djavax.net.debug=all
 100      *
 101      * This gives a lot of low-level information about operations underway,
 102      * including specific handshake messages, and might be best examined
 103      * after gaining some familiarity with this application.
 104      */
 105     private static boolean debug = false;
 106     private SSLContext sslc;
 107     private SSLEngine serverEngine;     // server-side SSLEngine
 108 
 109     private final byte[] serverMsg = "Hi there Client, I'm a Server".getBytes();
 110     private final byte[] clientMsg = "Hello Server, I'm a Client".getBytes();
 111 
 112     private ByteBuffer serverOut;       // write side of serverEngine
 113     private ByteBuffer serverIn;        // read side of serverEngine
 114 
 115     private volatile Exception clientException;
 116     private volatile Exception serverException;
 117 
 118     /*
 119      * For data transport, this example uses local ByteBuffers.
 120      */
 121     private ByteBuffer cTOs;            // "reliable" transport client->server
 122     private ByteBuffer sTOc;            // "reliable" transport server->client
 123 
 124     /*
 125      * The following is to set up the keystores/trust material.
 126      */
 127     private static final String pathToStores = "../../../../javax/net/ssl/etc";
 128     private static final String keyStoreFile = "keystore";
 129     private static final String trustStoreFile = "truststore";
 130     private static final String passwd = "passphrase";
 131     private static String keyFilename =
 132             System.getProperty("test.src", ".") + "/" + pathToStores
 133             + "/" + keyStoreFile;
 134     private static String trustFilename =
 135             System.getProperty("test.src", ".") + "/" + pathToStores
 136             + "/" + trustStoreFile;
 137 
 138     /*
 139      * Is the server ready to serve?
 140      */
 141     private static final CountDownLatch serverCondition = new CountDownLatch(1);
 142 
 143     /*
 144      * Is the client ready to handshake?
 145      */
 146     private static final CountDownLatch clientCondition = new CountDownLatch(1);
 147 
 148     /*
 149      * What's the server port?  Use any free port by default
 150      */
 151     private volatile int serverPort = 0;
 152 
 153     /*
 154      * Main entry point for this test.
 155      */
 156     public static void main(String args[]) throws Exception {
 157         if (debug) {
 158             System.setProperty("javax.net.debug", "all");
 159         }
 160 
 161         String [] protocols = new String [] {
 162             "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" };
 163 
 164         for (String protocol : protocols) {
 165             /*
 166              * Run the tests with direct and indirect buffers.
 167              */
 168             log("Testing " + protocol + ":true");
 169             new SSLEngineBadBufferArrayAccess(protocol).runTest(true);
 170 
 171             log("Testing " + protocol + ":false");
 172             new SSLEngineBadBufferArrayAccess(protocol).runTest(false);
 173         }
 174 
 175         System.out.println("Test Passed.");
 176     }
 177 
 178     /*
 179      * Create an initialized SSLContext to use for these tests.
 180      */
 181     public SSLEngineBadBufferArrayAccess(String protocol) throws Exception {
 182 
 183         KeyStore ks = KeyStore.getInstance("JKS");
 184         KeyStore ts = KeyStore.getInstance("JKS");
 185 
 186         char[] passphrase = "passphrase".toCharArray();
 187 
 188         try (FileInputStream fis = new FileInputStream(keyFilename)) {
 189             ks.load(fis, passphrase);
 190         }
 191 
 192         try (FileInputStream fis = new FileInputStream(trustFilename)) {
 193             ts.load(fis, passphrase);
 194         }
 195 
 196         KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
 197         kmf.init(ks, passphrase);
 198 
 199         TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
 200         tmf.init(ts);
 201 
 202         SSLContext sslCtx = SSLContext.getInstance(protocol);
 203 
 204         sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 205 
 206         sslc = sslCtx;
 207     }
 208 
 209     /*
 210      * Run the test.
 211      *
 212      * Sit in a tight loop, with the server engine calling wrap/unwrap
 213      * regardless of whether data is available or not.  We do this until
 214      * we get the application data.  Then we shutdown and go to the next one.
 215      *
 216      * The main loop handles all of the I/O phases of the SSLEngine's
 217      * lifetime:
 218      *
 219      *     initial handshaking
 220      *     application data transfer
 221      *     engine closing
 222      *
 223      * One could easily separate these phases into separate
 224      * sections of code.
 225      */
 226     private void runTest(boolean direct) throws Exception {
 227         boolean serverClose = direct;
 228 
 229         ServerSocket serverSocket = new ServerSocket(0);
 230         serverPort = serverSocket.getLocalPort();
 231 
 232         // Signal the client, the server is ready to accept connection.
 233         serverCondition.countDown();
 234 
 235         Thread clientThread = runClient(serverClose);
 236 
 237         // Try to accept a connection in 30 seconds.
 238         Socket socket;
 239         try {
 240             serverSocket.setSoTimeout(30000);
 241             socket = (Socket) serverSocket.accept();
 242         } catch (SocketTimeoutException ste) {
 243             serverSocket.close();
 244 
 245             // Ignore the test case if no connection within 30 seconds.
 246             System.out.println(
 247                 "No incoming client connection in 30 seconds. " +
 248                 "Ignore in server side.");
 249             return;
 250         }
 251 
 252         // handle the connection
 253         try {
 254             // Is it the expected client connection?
 255             //
 256             // Naughty test cases or third party routines may try to
 257             // connection to this server port unintentionally.  In
 258             // order to mitigate the impact of unexpected client
 259             // connections and avoid intermittent failure, it should
 260             // be checked that the accepted connection is really linked
 261             // to the expected client.
 262             boolean clientIsReady =
 263                     clientCondition.await(30L, TimeUnit.SECONDS);
 264 
 265             if (clientIsReady) {
 266                 // Run the application in server side.
 267                 runServerApplication(socket, direct, serverClose);
 268             } else {    // Otherwise, ignore
 269                 // We don't actually care about plain socket connections
 270                 // for TLS communication testing generally.  Just ignore
 271                 // the test if the accepted connection is not linked to
 272                 // the expected client or the client connection timeout
 273                 // in 30 seconds.
 274                 System.out.println(
 275                         "The client is not the expected one or timeout. " +
 276                         "Ignore in server side.");
 277             }
 278         } catch (Exception e) {
 279             System.out.println("Server died ...");
 280             e.printStackTrace(System.out);
 281             serverException = e;
 282         } finally {
 283             socket.close();
 284             serverSocket.close();
 285         }
 286 
 287         clientThread.join();
 288 
 289         if (clientException != null || serverException != null) {
 290             throw new RuntimeException("Test failed");
 291         }
 292     }
 293 
 294     /*
 295      * Define the server side application of the test for the specified socket.
 296      */
 297     void runServerApplication(Socket socket, boolean direct,
 298             boolean serverClose) throws Exception {
 299 
 300         socket.setSoTimeout(500);
 301 
 302         createSSLEngine();
 303         createBuffers(direct);
 304 
 305         boolean closed = false;
 306 
 307         InputStream is = socket.getInputStream();
 308         OutputStream os = socket.getOutputStream();
 309 
 310         SSLEngineResult serverResult;   // results from last operation
 311 
 312         /*
 313          * Examining the SSLEngineResults could be much more involved,
 314          * and may alter the overall flow of the application.
 315          *
 316          * For example, if we received a BUFFER_OVERFLOW when trying
 317          * to write to the output pipe, we could reallocate a larger
 318          * pipe, but instead we wait for the peer to drain it.
 319          */
 320         byte[] inbound = new byte[8192];
 321         byte[] outbound = new byte[8192];
 322 
 323         while (!isEngineClosed(serverEngine)) {
 324             int len = 0;
 325 
 326             // Inbound data
 327             log("================");
 328 
 329             // Read from the Client side.
 330             try {
 331                 len = is.read(inbound);
 332                 if (len == -1) {
 333                     throw new Exception("Unexpected EOF");
 334                 }
 335                 cTOs.put(inbound, 0, len);
 336             } catch (SocketTimeoutException ste) {
 337                 // swallow.  Nothing yet, probably waiting on us.
 338                 System.out.println("Warning: " + ste);
 339             }
 340 
 341             cTOs.flip();
 342 
 343             serverResult = serverEngine.unwrap(cTOs, serverIn);
 344             log("server unwrap: ", serverResult);
 345             runDelegatedTasks(serverResult, serverEngine);
 346             cTOs.compact();
 347 
 348             // Outbound data
 349             log("----");
 350 
 351             serverResult = serverEngine.wrap(serverOut, sTOc);
 352             log("server wrap: ", serverResult);
 353             runDelegatedTasks(serverResult, serverEngine);
 354 
 355             sTOc.flip();
 356 
 357             if ((len = sTOc.remaining()) != 0) {
 358                 sTOc.get(outbound, 0, len);
 359                 os.write(outbound, 0, len);
 360                 // Give the other side a chance to process
 361             }
 362 
 363             sTOc.compact();
 364 
 365             if (!closed && (serverOut.remaining() == 0)) {
 366                 closed = true;
 367 
 368                 /*
 369                  * We'll alternate initiatating the shutdown.
 370                  * When the server initiates, it will take one more
 371                  * loop, but tests the orderly shutdown.
 372                  */
 373                 if (serverClose) {
 374                     serverEngine.closeOutbound();
 375                 }
 376             }
 377 
 378             if (closed && isEngineClosed(serverEngine)) {
 379                 serverIn.flip();
 380 
 381                 /*
 382                  * A sanity check to ensure we got what was sent.
 383                  */
 384                 if (serverIn.remaining() != clientMsg.length) {
 385                     throw new Exception("Client: Data length error -" +
 386                         " IF THIS FAILS, PLEASE REPORT THIS TO THE" +
 387                         " SECURITY TEAM.  WE HAVE BEEN UNABLE TO" +
 388                         " RELIABLY DUPLICATE.");
 389                 }
 390 
 391                 for (int i = 0; i < clientMsg.length; i++) {
 392                     if (clientMsg[i] != serverIn.get()) {
 393                         throw new Exception("Client: Data content error -" +
 394                         " IF THIS FAILS, PLEASE REPORT THIS TO THE" +
 395                         " SECURITY TEAM.  WE HAVE BEEN UNABLE TO" +
 396                         " RELIABLY DUPLICATE.");
 397                     }
 398                 }
 399                 serverIn.compact();
 400             }
 401         }
 402     }
 403 
 404     /*
 405      * Create a client thread which does simple SSLSocket operations.
 406      * We'll write and read one data packet.
 407      */
 408     private Thread runClient(final boolean serverClose)
 409             throws Exception {
 410 
 411         Thread t = new Thread("ClientThread") {
 412 
 413             @Override
 414             public void run() {
 415                 try {
 416                     doClientSide(serverClose);
 417                 } catch (Exception e) {
 418                     System.out.println("Client died ...");
 419                     e.printStackTrace(System.out);
 420                     clientException = e;
 421                 }
 422             }
 423         };
 424 
 425         t.start();
 426         return t;
 427     }
 428 
 429     /*
 430      * Define the client side of the test.
 431      */
 432     void doClientSide(boolean serverClose) throws Exception {
 433         // Wait for server to get started.
 434         //
 435         // The server side takes care of the issue if the server cannot
 436         // get started in 90 seconds.  The client side would just ignore
 437         // the test case if the serer is not ready.
 438         boolean serverIsReady =
 439                 serverCondition.await(90L, TimeUnit.SECONDS);
 440         if (!serverIsReady) {
 441             System.out.println(
 442                     "The server is not ready yet in 90 seconds. " +
 443                     "Ignore in client side.");
 444             return;
 445         }
 446 
 447         SSLSocketFactory sslsf = sslc.getSocketFactory();
 448         try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {
 449             try {
 450                 sslSocket.connect(
 451                         new InetSocketAddress("localhost", serverPort), 15000);
 452             } catch (IOException ioe) {
 453                 // The server side may be impacted by naughty test cases or
 454                 // third party routines, and cannot accept connections.
 455                 //
 456                 // Just ignore the test if the connection cannot be
 457                 // established.
 458                 System.out.println(
 459                         "Cannot make a connection in 15 seconds. " +
 460                         "Ignore in client side.");
 461                 return;
 462             }
 463 
 464             // OK, here the client and server get connected.
 465 
 466             // Signal the server, the client is ready to communicate.
 467             clientCondition.countDown();
 468 
 469             // There is still a chance in theory that the server thread may
 470             // wait client-ready timeout and then quit.  The chance should
 471             // be really rare so we don't consider it until it becomes a
 472             // real problem.
 473 
 474             // Run the application in client side.
 475             runClientApplication(sslSocket, serverClose);
 476         }
 477     }
 478 
 479     /*
 480      * Define the server side application of the test for the specified socket.
 481      */
 482     void runClientApplication(SSLSocket sslSocket, boolean serverClose)
 483             throws Exception {
 484 
 485         OutputStream os = sslSocket.getOutputStream();
 486         InputStream is = sslSocket.getInputStream();
 487 
 488         // write(byte[]) goes in one shot.
 489         os.write(clientMsg);
 490 
 491         byte[] inbound = new byte[2048];
 492         int pos = 0;
 493 
 494         int len;
 495         while ((len = is.read(inbound, pos, 2048 - pos)) != -1) {
 496             pos += len;
 497             // Let the client do the closing.
 498             if ((pos == serverMsg.length) && !serverClose) {
 499                 sslSocket.close();
 500                 break;
 501             }
 502         }
 503 
 504         if (pos != serverMsg.length) {
 505             throw new Exception("Client:  Data length error");
 506         }
 507 
 508         for (int i = 0; i < serverMsg.length; i++) {
 509             if (inbound[i] != serverMsg[i]) {
 510                 throw new Exception("Client:  Data content error");
 511             }
 512         }
 513     }
 514 
 515     /*
 516      * Using the SSLContext created during object creation,
 517      * create/configure the SSLEngines we'll use for this test.
 518      */
 519     private void createSSLEngine() throws Exception {
 520         /*
 521          * Configure the serverEngine to act as a server in the SSL/TLS
 522          * handshake.
 523          */
 524         serverEngine = sslc.createSSLEngine();
 525         serverEngine.setUseClientMode(false);
 526         serverEngine.getNeedClientAuth();
 527     }
 528 
 529     /*
 530      * Create and size the buffers appropriately.
 531      */
 532     private void createBuffers(boolean direct) {
 533 
 534         SSLSession session = serverEngine.getSession();
 535         int appBufferMax = session.getApplicationBufferSize();
 536         int netBufferMax = session.getPacketBufferSize();
 537 
 538         /*
 539          * We'll make the input buffers a bit bigger than the max needed
 540          * size, so that unwrap()s following a successful data transfer
 541          * won't generate BUFFER_OVERFLOWS.
 542          *
 543          * We'll use a mix of direct and indirect ByteBuffers for
 544          * tutorial purposes only.  In reality, only use direct
 545          * ByteBuffers when they give a clear performance enhancement.
 546          */
 547         if (direct) {
 548             serverIn = ByteBuffer.allocateDirect(appBufferMax + 50);
 549             cTOs = ByteBuffer.allocateDirect(netBufferMax);
 550             sTOc = ByteBuffer.allocateDirect(netBufferMax);
 551         } else {
 552             serverIn = ByteBuffer.allocate(appBufferMax + 50);
 553             cTOs = ByteBuffer.allocate(netBufferMax);
 554             sTOc = ByteBuffer.allocate(netBufferMax);
 555         }
 556 
 557         serverOut = ByteBuffer.wrap(serverMsg);
 558     }
 559 
 560     /*
 561      * If the result indicates that we have outstanding tasks to do,
 562      * go ahead and run them in this thread.
 563      */
 564     private static void runDelegatedTasks(SSLEngineResult result,
 565             SSLEngine engine) throws Exception {
 566 
 567         if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
 568             Runnable runnable;
 569             while ((runnable = engine.getDelegatedTask()) != null) {
 570                 log("\trunning delegated task...");
 571                 runnable.run();
 572             }
 573             HandshakeStatus hsStatus = engine.getHandshakeStatus();
 574             if (hsStatus == HandshakeStatus.NEED_TASK) {
 575                 throw new Exception(
 576                         "handshake shouldn't need additional tasks");
 577             }
 578             log("\tnew HandshakeStatus: " + hsStatus);
 579         }
 580     }
 581 
 582     private static boolean isEngineClosed(SSLEngine engine) {
 583         return (engine.isOutboundDone() && engine.isInboundDone());
 584     }
 585 
 586     /*
 587      * Logging code
 588      */
 589     private static boolean resultOnce = true;
 590 
 591     private static void log(String str, SSLEngineResult result) {
 592         if (!logging) {
 593             return;
 594         }
 595         if (resultOnce) {
 596             resultOnce = false;
 597             System.out.println("The format of the SSLEngineResult is: \n"
 598                     + "\t\"getStatus() / getHandshakeStatus()\" +\n"
 599                     + "\t\"bytesConsumed() / bytesProduced()\"\n");
 600         }
 601         HandshakeStatus hsStatus = result.getHandshakeStatus();
 602         log(str
 603                 + result.getStatus() + "/" + hsStatus + ", "
 604                 + result.bytesConsumed() + "/" + result.bytesProduced()
 605                 + " bytes");
 606         if (hsStatus == HandshakeStatus.FINISHED) {
 607             log("\t...ready for application data");
 608         }
 609     }
 610 
 611     private static void log(String str) {
 612         if (logging) {
 613             System.out.println(str);
 614         }
 615     }
 616 }