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      */
main(String args[])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      */
SSLEngineBadBufferArrayAccess(String protocol)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      */
runTest(boolean direct)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      */
runServerApplication(Socket socket, boolean direct, boolean serverClose)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      */
runClient(final boolean serverClose)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      */
doClientSide(boolean serverClose)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      */
runClientApplication(SSLSocket sslSocket, boolean serverClose)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      */
createSSLEngine()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      */
createBuffers(boolean direct)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      */
runDelegatedTasks(SSLEngineResult result, SSLEngine engine)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 
isEngineClosed(SSLEngine engine)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 
log(String str, SSLEngineResult result)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 
log(String str)611     private static void log(String str) {
612         if (logging) {
613             System.out.println(str);
614         }
615     }
616 }
617