/* * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ // SunJSSE does not support dynamic system properties, no way to re-use // system properties in samevm/agentvm mode. /* * @test * @bug 8331682 * @summary Slow networks/Impatient clients can potentially send * unencrypted TLSv1.3 alerts that won't parse on the server. * @library /javax/net/ssl/templates /test/lib * @run main/othervm SSLEngineNoServerHelloClientShutdown */ import static jdk.test.lib.Asserts.assertEquals; import static jdk.test.lib.Asserts.fail; import static jdk.test.lib.security.SecurityUtils.inspectTlsBuffer; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLProtocolException; import javax.net.ssl.SSLSession; /** * A SSLEngine usage example which simplifies the presentation * by removing the I/O and multi-threading concerns. *
* The test creates two SSLEngines, simulating a client and server. * The "transport" layer consists two byte buffers: think of them * as directly connected pipes. *
* When this application runs, notice that several messages * (wrap/unwrap) pass before any application data is consumed or * produced. */ public class SSLEngineNoServerHelloClientShutdown extends SSLContextTemplate { protected static final String EXCEPTION_MSG = "Unexpected plaintext alert received: " + "Level: warning; Alert: user_canceled"; protected SSLEngine clientEngine; // client Engine protected ByteBuffer clientOut; // write side of clientEngine protected final ByteBuffer clientIn; // read side of clientEngine protected final SSLEngine serverEngine; // server Engine protected final ByteBuffer serverOut; // write side of serverEngine protected final ByteBuffer serverIn; // read side of serverEngine protected ByteBuffer cTOs; // "reliable" transport client->server protected final ByteBuffer sTOc; // "reliable" transport server->client protected SSLEngineNoServerHelloClientShutdown() throws Exception { serverEngine = configureServerEngine( createServerSSLContext().createSSLEngine()); clientEngine = configureClientEngine( createClientSSLContext().createSSLEngine()); // We'll assume the buffer sizes are the same between client and server. SSLSession session = clientEngine.getSession(); int appBufferMax = session.getApplicationBufferSize(); int netBufferMax = session.getPacketBufferSize(); // We'll make the input buffers a bit bigger than the max needed // size, so that unwrap()s following a successful data transfer // won't generate BUFFER_OVERFLOWS. clientIn = ByteBuffer.allocate(appBufferMax + 50); serverIn = ByteBuffer.allocate(appBufferMax + 50); cTOs = ByteBuffer.allocateDirect(netBufferMax * 2); // Make it larger so subsequent server wraps won't generate // BUFFER_OVERFLOWS sTOc = ByteBuffer.allocateDirect(netBufferMax * 2); clientOut = createClientOutputBuffer(); serverOut = createServerOutputBuffer(); } protected ByteBuffer createServerOutputBuffer() { return ByteBuffer.wrap("Hello Client, I'm Server".getBytes()); } protected ByteBuffer createClientOutputBuffer() { return ByteBuffer.wrap("Hi Server, I'm Client".getBytes()); } /* * Configure the client side engine. */ protected SSLEngine configureClientEngine(SSLEngine clientEngine) { clientEngine.setUseClientMode(true); clientEngine.setEnabledProtocols(new String[] {"TLSv1.3"}); return clientEngine; } /* * Configure the server side engine. */ protected SSLEngine configureServerEngine(SSLEngine serverEngine) { serverEngine.setUseClientMode(false); serverEngine.setNeedClientAuth(true); return serverEngine; } public static void main(String[] args) throws Exception { new SSLEngineNoServerHelloClientShutdown().runTestUserCancelled(); } // // Private methods that used to build the common part of the test. // private void runTestUserCancelled() throws Exception { SSLEngineResult clientResult; SSLEngineResult serverResult; log("================="); // Client: produce client_hello log("---Client Wrap client_hello---"); clientResult = clientEngine.wrap(clientOut, cTOs); logEngineStatus(clientEngine, clientResult); runDelegatedTasks(clientEngine); cTOs.flip(); // Server: consume client_hello log("---Server Unwrap client_hello---"); serverResult = serverEngine.unwrap(cTOs, serverIn); logEngineStatus(serverEngine, serverResult); runDelegatedTasks(serverEngine); cTOs.compact(); // Server: produce server_hello log("---Server Wrap server_hello---"); serverResult = serverEngine.wrap(serverOut, sTOc); logEngineStatus(serverEngine, serverResult); runDelegatedTasks(serverEngine); // SH packet went missing. Timeout on Client. // Server: produce other outbound messages log("---Server Wrap---"); serverResult = serverEngine.wrap(serverOut, sTOc); logEngineStatus(serverEngine, serverResult); runDelegatedTasks(serverEngine); // CCS packet went missing. Timeout on Client. // Server: produce other outbound messages log("---Server Wrap---"); serverResult = serverEngine.wrap(serverOut, sTOc); logEngineStatus(serverEngine, serverResult); runDelegatedTasks(serverEngine); // EE/etc. packet went missing. Timeout on Client. // Shutdown client log("---Client closeOutbound---"); clientEngine.closeOutbound(); // Client: produce an unencrypted user_canceled log("---Client Wrap user_canceled---"); clientResult = clientEngine.wrap(clientOut, cTOs); logEngineStatus(clientEngine, clientResult); runDelegatedTasks(clientEngine); cTOs.flip(); inspectTlsBuffer(cTOs); // Server unwrap should throw a proper exception when receiving an // unencrypted 2 byte packet user_canceled alert. log("---Server Unwrap user_canceled alert---"); try { serverEngine.unwrap(cTOs, serverIn); } catch (SSLProtocolException e) { assertEquals( GeneralSecurityException.class, e.getCause().getClass()); assertEquals(EXCEPTION_MSG, e.getCause().getMessage()); return; } fail("Server should have thrown SSLProtocolException"); } protected static void logEngineStatus(SSLEngine engine) { log("\tCurrent HS State: " + engine.getHandshakeStatus()); log("\tisInboundDone() : " + engine.isInboundDone()); log("\tisOutboundDone(): " + engine.isOutboundDone()); } protected static void logEngineStatus( SSLEngine engine, SSLEngineResult result) { log("\tResult Status : " + result.getStatus()); log("\tResult HS Status : " + result.getHandshakeStatus()); log("\tEngine HS Status : " + engine.getHandshakeStatus()); log("\tisInboundDone() : " + engine.isInboundDone()); log("\tisOutboundDone() : " + engine.isOutboundDone()); log("\tMore Result : " + result); } protected static void log(String message) { System.err.println(message); } // If the result indicates that we have outstanding tasks to do, // go ahead and run them in this thread. protected static void runDelegatedTasks(SSLEngine engine) throws Exception { if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { Runnable runnable; while ((runnable = engine.getDelegatedTask()) != null) { log(" running delegated task..."); runnable.run(); } HandshakeStatus hsStatus = engine.getHandshakeStatus(); if (hsStatus == HandshakeStatus.NEED_TASK) { throw new Exception( "handshake shouldn't need additional tasks"); } logEngineStatus(engine); } } }