1 /*
2  * Copyright (c) 2020, 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 // SunJSSE does not support dynamic system properties, no way to re-use
25 // system properties in samevm/agentvm mode.
26 
27 /*
28  * @test
29  * @bug 8233619
30  * @summary SSLEngine has not yet caused Solaris kernel to panic
31  * @run main/othervm FinishedPresent
32  */
33 import javax.net.ssl.*;
34 import javax.net.ssl.SSLEngineResult.*;
35 import java.io.*;
36 import java.security.*;
37 import java.nio.*;
38 
39 public class FinishedPresent {
40 
41     /*
42      * Enables logging of the SSLEngine operations.
43      */
44     private static final boolean logging = true;
45 
46     /*
47      * Enables the JSSE system debugging system property:
48      *
49      *     -Djavax.net.debug=all
50      *
51      * This gives a lot of low-level information about operations underway,
52      * including specific handshake messages, and might be best examined
53      * after gaining some familiarity with this application.
54      */
55     private static final boolean debug = false;
56 
57     private final SSLContext sslc;
58 
59     private SSLEngine clientEngine;     // client Engine
60     private ByteBuffer clientOut;       // write side of clientEngine
61     private ByteBuffer clientIn;        // read side of clientEngine
62 
63     private SSLEngine serverEngine;     // server Engine
64     private ByteBuffer serverOut;       // write side of serverEngine
65     private ByteBuffer serverIn;        // read side of serverEngine
66 
67     /*
68      * For data transport, this example uses local ByteBuffers.  This
69      * isn't really useful, but the purpose of this example is to show
70      * SSLEngine concepts, not how to do network transport.
71      */
72     private ByteBuffer cTOs;            // "reliable" transport client->server
73     private ByteBuffer sTOc;            // "reliable" transport server->client
74 
75     /*
76      * The following is to set up the keystores.
77      */
78     private static final String pathToStores = "../etc";
79     private static final String keyStoreFile = "keystore";
80     private static final String trustStoreFile = "truststore";
81     private static final char[] passphrase = "passphrase".toCharArray();
82 
83     private static final String keyFilename =
84             System.getProperty("test.src", ".") + "/" + pathToStores +
85                 "/" + keyStoreFile;
86     private static final String trustFilename =
87             System.getProperty("test.src", ".") + "/" + pathToStores +
88                 "/" + trustStoreFile;
89 
90     /*
91      * Main entry point for this test.
92      */
main(String args[])93     public static void main(String args[]) throws Exception {
94         if (debug) {
95             System.setProperty("javax.net.debug", "all");
96         }
97 
98         FinishedPresent test = new FinishedPresent();
99         test.runTest();
100 
101         log("Test Passed.");
102     }
103 
104     /*
105      * Create an initialized SSLContext to use for these tests.
106      */
FinishedPresent()107     public FinishedPresent() throws Exception {
108 
109         KeyStore ks = KeyStore.getInstance("JKS");
110         KeyStore ts = KeyStore.getInstance("JKS");
111 
112         ks.load(new FileInputStream(keyFilename), passphrase);
113         ts.load(new FileInputStream(trustFilename), passphrase);
114 
115         KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
116         kmf.init(ks, passphrase);
117 
118         TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
119         tmf.init(ts);
120 
121         SSLContext sslCtx = SSLContext.getInstance("TLSv1.3");
122 
123         sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
124 
125         sslc = sslCtx;
126     }
127 
128     /*
129      * Run the test.
130      *
131      * Sit in a tight loop, both engines calling wrap/unwrap regardless
132      * of whether data is available or not.  We do this until both engines
133      * report back they are closed.
134      *
135      * The main loop handles all of the I/O phases of the SSLEngine's
136      * lifetime:
137      *
138      *     initial handshaking
139      *     application data transfer
140      *     engine closing
141      *
142      * One could easily separate these phases into separate
143      * sections of code.
144      */
runTest()145     private void runTest() throws Exception {
146         boolean dataDone = false;
147 
148         createSSLEngines();
149         createBuffers();
150 
151         // results from client's last operation
152         SSLEngineResult clientResult;
153 
154         // results from server's last operation
155         SSLEngineResult serverResult;
156 
157         /*
158          * Examining the SSLEngineResults could be much more involved,
159          * and may alter the overall flow of the application.
160          *
161          * For example, if we received a BUFFER_OVERFLOW when trying
162          * to write to the output pipe, we could reallocate a larger
163          * pipe, but instead we wait for the peer to drain it.
164          */
165         Exception clientException = null;
166         Exception serverException = null;
167 
168         boolean clientFinishedPresent = false;
169         boolean serverFinishedPresent = false;
170         boolean client2ndFinishedPresent = false;
171         boolean server2ndFinishedPresent = false;
172         while (!isEngineClosed(clientEngine)
173                 || !isEngineClosed(serverEngine)) {
174 
175             log("================");
176 
177             try {
178                 clientResult = clientEngine.wrap(clientOut, cTOs);
179                 if (clientFinishedPresent) {
180                     client2ndFinishedPresent |= hasFinished(
181                         "posthandshake client wrap", clientResult);
182                 } else {
183                     clientFinishedPresent |= hasFinished(
184                         "client wrap", clientResult);
185                 }
186                 log("client wrap: ", clientResult);
187             } catch (Exception e) {
188                 clientException = e;
189                 log("Client wrap() threw: " + e.getMessage());
190             }
191             logEngineStatus(clientEngine);
192             runDelegatedTasks(clientEngine);
193 
194             log("----");
195 
196             try {
197                 serverResult = serverEngine.wrap(serverOut, sTOc);
198                 if (serverFinishedPresent) {
199                     server2ndFinishedPresent |= hasFinished(
200                         "posthandshake server wrap", serverResult);
201                 } else {
202                     serverFinishedPresent |= hasFinished(
203                         "server wrap", serverResult);
204                 }
205                 log("server wrap: ", serverResult);
206             } catch (Exception e) {
207                 serverException = e;
208                 log("Server wrap() threw: " + e.getMessage());
209             }
210             logEngineStatus(serverEngine);
211             runDelegatedTasks(serverEngine);
212 
213             cTOs.flip();
214             sTOc.flip();
215 
216             log("--------");
217 
218             try {
219                 clientResult = clientEngine.unwrap(sTOc, clientIn);
220                 if (clientFinishedPresent) {
221                     client2ndFinishedPresent |= hasFinished(
222                         "posthandshake client unwrap", clientResult);
223                 } else {
224                     clientFinishedPresent |= hasFinished(
225                         "client unwrap", clientResult);
226                 }
227                 log("client unwrap: ", clientResult);
228             } catch (Exception e) {
229                 clientException = e;
230                 log("Client unwrap() threw: " + e.getMessage());
231             }
232             logEngineStatus(clientEngine);
233             runDelegatedTasks(clientEngine);
234 
235             log("----");
236 
237             try {
238                 serverResult = serverEngine.unwrap(cTOs, serverIn);
239                 if (serverFinishedPresent) {
240                     server2ndFinishedPresent |= hasFinished(
241                         "posthandshake server unwrap", serverResult);
242                 } else {
243                     serverFinishedPresent |= hasFinished(
244                         "server unwrap", serverResult);
245                 }
246                 log("server unwrap: ", serverResult);
247             } catch (Exception e) {
248                 serverException = e;
249                 log("Server unwrap() threw: " + e.getMessage());
250             }
251             logEngineStatus(serverEngine);
252             runDelegatedTasks(serverEngine);
253 
254             cTOs.compact();
255             sTOc.compact();
256 
257             /*
258              * After we've transfered all application data between the client
259              * and server, we close the clientEngine's outbound stream.
260              * This generates a close_notify handshake message, which the
261              * server engine receives and responds by closing itself.
262              */
263             if (!dataDone && (clientOut.limit() == serverIn.position()) &&
264                     (serverOut.limit() == clientIn.position())) {
265 
266                 /*
267                  * A sanity check to ensure we got what was sent.
268                  */
269                 checkTransfer(serverOut, clientIn);
270                 checkTransfer(clientOut, serverIn);
271 
272                 log("\tClosing clientEngine's *OUTBOUND*...");
273                 clientEngine.closeOutbound();
274                 logEngineStatus(clientEngine);
275 
276                 dataDone = true;
277                 log("\tClosing serverEngine's *OUTBOUND*...");
278                 serverEngine.closeOutbound();
279                 logEngineStatus(serverEngine);
280             }
281         }
282 
283         if (!clientFinishedPresent) {
284             throw new Exception("No client FINISHED status present");
285         }
286 
287         if (!serverFinishedPresent) {
288             throw new Exception("No server FINISHED status present");
289         }
290 
291         if (!client2ndFinishedPresent) {
292             throw new Exception(
293                     "No posthandshake client FINISHED status present");
294         }
295 
296         // Note: the server side did not finish the handshake unless the
297         // posthandshake message get delivered.  This behaviro may be
298         // updated in the future.
299         //
300         // if (!server2ndFinishedPresent) {
301         //     throw new Exception(
302         //             "No posthandshake server FINISHED status present");
303         // }
304     }
305 
logEngineStatus(SSLEngine engine)306     private static void logEngineStatus(SSLEngine engine) {
307         log("\tCurrent HS State  " + engine.getHandshakeStatus().toString());
308         log("\tisInboundDone():  " + engine.isInboundDone());
309         log("\tisOutboundDone(): " + engine.isOutboundDone());
310     }
311 
hasFinished( String prefix, SSLEngineResult engineResult)312     private static boolean hasFinished(
313             String prefix, SSLEngineResult engineResult) {
314         if (engineResult.getHandshakeStatus() == HandshakeStatus.FINISHED) {
315             log(prefix + " finished present: " + engineResult);
316             return true;
317         }
318 
319         return false;
320     }
321 
322     /*
323      * Using the SSLContext created during object creation,
324      * create/configure the SSLEngines we'll use for this test.
325      */
createSSLEngines()326     private void createSSLEngines() throws Exception {
327         /*
328          * Configure the serverEngine to act as a server in the SSL/TLS
329          * handshake.  Also, require SSL client authentication.
330          */
331         serverEngine = sslc.createSSLEngine();
332         serverEngine.setUseClientMode(false);
333         serverEngine.setNeedClientAuth(true);
334 
335         // Get/set parameters if needed
336         SSLParameters paramsServer = serverEngine.getSSLParameters();
337         serverEngine.setSSLParameters(paramsServer);
338 
339         /*
340          * Similar to above, but using client mode instead.
341          */
342         clientEngine = sslc.createSSLEngine("client", 80);
343         clientEngine.setUseClientMode(true);
344 
345         // Get/set parameters if needed
346         SSLParameters paramsClient = clientEngine.getSSLParameters();
347         clientEngine.setSSLParameters(paramsClient);
348     }
349 
350     /*
351      * Create and size the buffers appropriately.
352      */
createBuffers()353     private void createBuffers() {
354 
355         /*
356          * We'll assume the buffer sizes are the same
357          * between client and server.
358          */
359         SSLSession session = clientEngine.getSession();
360         int appBufferMax = session.getApplicationBufferSize();
361         int netBufferMax = session.getPacketBufferSize();
362 
363         /*
364          * We'll make the input buffers a bit bigger than the max needed
365          * size, so that unwrap()s following a successful data transfer
366          * won't generate BUFFER_OVERFLOWS.
367          *
368          * We'll use a mix of direct and indirect ByteBuffers for
369          * tutorial purposes only.  In reality, only use direct
370          * ByteBuffers when they give a clear performance enhancement.
371          */
372         clientIn = ByteBuffer.allocate(appBufferMax + 50);
373         serverIn = ByteBuffer.allocate(appBufferMax + 50);
374 
375         cTOs = ByteBuffer.allocateDirect(netBufferMax);
376         sTOc = ByteBuffer.allocateDirect(netBufferMax);
377 
378         clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
379         serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
380     }
381 
382     /*
383      * If the result indicates that we have outstanding tasks to do,
384      * go ahead and run them in this thread.
385      */
runDelegatedTasks(SSLEngine engine)386     private static void runDelegatedTasks(SSLEngine engine) throws Exception {
387 
388         if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
389             Runnable runnable;
390             while ((runnable = engine.getDelegatedTask()) != null) {
391                 log("    running delegated task...");
392                 runnable.run();
393             }
394             HandshakeStatus hsStatus = engine.getHandshakeStatus();
395             if (hsStatus == HandshakeStatus.NEED_TASK) {
396                 throw new Exception(
397                     "handshake shouldn't need additional tasks");
398             }
399             logEngineStatus(engine);
400         }
401     }
402 
isEngineClosed(SSLEngine engine)403     private static boolean isEngineClosed(SSLEngine engine) {
404         return (engine.isOutboundDone() && engine.isInboundDone());
405     }
406 
407     /*
408      * Simple check to make sure everything came across as expected.
409      */
checkTransfer(ByteBuffer a, ByteBuffer b)410     private static void checkTransfer(ByteBuffer a, ByteBuffer b)
411             throws Exception {
412         a.flip();
413         b.flip();
414 
415         if (!a.equals(b)) {
416             throw new Exception("Data didn't transfer cleanly");
417         } else {
418             log("\tData transferred cleanly");
419         }
420 
421         a.position(a.limit());
422         b.position(b.limit());
423         a.limit(a.capacity());
424         b.limit(b.capacity());
425     }
426 
427     /*
428      * Logging code
429      */
430     private static boolean resultOnce = true;
431 
log(String str, SSLEngineResult result)432     private static void log(String str, SSLEngineResult result) {
433         if (!logging) {
434             return;
435         }
436         if (resultOnce) {
437             resultOnce = false;
438             System.err.println("The format of the SSLEngineResult is: \n" +
439                     "\t\"getStatus() / getHandshakeStatus()\" +\n" +
440                     "\t\"bytesConsumed() / bytesProduced()\"\n");
441         }
442         HandshakeStatus hsStatus = result.getHandshakeStatus();
443         log(str +
444                 result.getStatus() + "/" + hsStatus + ", " +
445                 result.bytesConsumed() + "/" + result.bytesProduced() +
446                 " bytes");
447         if (hsStatus == HandshakeStatus.FINISHED) {
448             log("\t...ready for application data");
449         }
450     }
451 
log(String str)452     private static void log(String str) {
453         if (logging) {
454             System.err.println(str);
455         }
456     }
457 }
458