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