Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/sun/security/ssl/SSLEngineImpl/SSLEngineBadBufferArrayAccess.java
38853 views
/*1* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223//24// SunJSSE does not support dynamic system properties, no way to re-use25// system properties in samevm/agentvm mode.26//2728/*29* @test30* @bug 703183031* @summary bad_record_mac failure on TLSv1.2 enabled connection with SSLEngine32* @library /lib/security33* @run main/othervm SSLEngineBadBufferArrayAccess34*/3536/**37* A SSLSocket/SSLEngine interop test case. This is not the way to38* 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 code41* 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 code45* does the translation between byte [] and ByteBuffers that the SSLEngine46* can use. The "transport" layer consists of a Socket Input/OutputStream47* and two byte buffers for the SSLEngines: think of them48* as directly connected pipes.49*50* Again, this is a *very* simple example: real code will be much more51* involved. For example, different threading and I/O models could be52* used, transport mechanisms could close unexpectedly, and so on.53*54* When this application runs, notice that several messages55* (wrap/unwrap) pass before any application data is consumed or56* produced. (For more information, please see the SSL/TLS57* 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 message61* ====== ====== =======62* write() ... ClientHello63* ... unwrap() ClientHello64* ... wrap() ServerHello/Certificate65* read() ... ServerHello/Certificate66* write() ... ClientKeyExchange67* write() ... ChangeCipherSpec68* write() ... Finished69* ... unwrap() ClientKeyExchange70* ... unwrap() ChangeCipherSpec71* ... unwrap() Finished72* ... wrap() ChangeCipherSpec73* ... wrap() Finished74* read() ... ChangeCipherSpec75* read() ... Finished76*77* This particular bug had a problem where byte buffers backed by an78* array didn't offset correctly, and we got bad MAC errors.79*/80import javax.net.ssl.*;81import javax.net.ssl.SSLEngineResult.*;82import java.io.*;83import java.net.*;84import java.security.*;85import java.nio.*;86import java.util.concurrent.CountDownLatch;87import java.util.concurrent.TimeUnit;8889public class SSLEngineBadBufferArrayAccess {9091/*92* Enables logging of the SSL/TLS operations.93*/94private static boolean logging = true;9596/*97* Enables the JSSE system debugging system property:98*99* -Djavax.net.debug=all100*101* This gives a lot of low-level information about operations underway,102* including specific handshake messages, and might be best examined103* after gaining some familiarity with this application.104*/105private static boolean debug = false;106private SSLContext sslc;107private SSLEngine serverEngine; // server-side SSLEngine108109private final byte[] serverMsg = "Hi there Client, I'm a Server".getBytes();110private final byte[] clientMsg = "Hello Server, I'm a Client".getBytes();111112private ByteBuffer serverOut; // write side of serverEngine113private ByteBuffer serverIn; // read side of serverEngine114115private volatile Exception clientException;116private volatile Exception serverException;117118/*119* For data transport, this example uses local ByteBuffers.120*/121private ByteBuffer cTOs; // "reliable" transport client->server122private ByteBuffer sTOc; // "reliable" transport server->client123124/*125* The following is to set up the keystores/trust material.126*/127private static final String pathToStores = "../../../../javax/net/ssl/etc";128private static final String keyStoreFile = "keystore";129private static final String trustStoreFile = "truststore";130private static final String passwd = "passphrase";131private static String keyFilename =132System.getProperty("test.src", ".") + "/" + pathToStores133+ "/" + keyStoreFile;134private static String trustFilename =135System.getProperty("test.src", ".") + "/" + pathToStores136+ "/" + trustStoreFile;137138/*139* Is the server ready to serve?140*/141private static final CountDownLatch serverCondition = new CountDownLatch(1);142143/*144* Is the client ready to handshake?145*/146private static final CountDownLatch clientCondition = new CountDownLatch(1);147148/*149* What's the server port? Use any free port by default150*/151private volatile int serverPort = 0;152153/*154* Main entry point for this test.155*/156public static void main(String args[]) throws Exception {157if (debug) {158System.setProperty("javax.net.debug", "all");159}160161// Re-enable TLSv1 and TLSv1.1 since test depends on them.162SecurityUtils.removeFromDisabledTlsAlgs("TLSv1", "TLSv1.1");163164String [] protocols = new String [] {165"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" };166167for (String protocol : protocols) {168/*169* Run the tests with direct and indirect buffers.170*/171log("Testing " + protocol + ":true");172new SSLEngineBadBufferArrayAccess(protocol).runTest(true);173174log("Testing " + protocol + ":false");175new SSLEngineBadBufferArrayAccess(protocol).runTest(false);176}177178System.out.println("Test Passed.");179}180181/*182* Create an initialized SSLContext to use for these tests.183*/184public SSLEngineBadBufferArrayAccess(String protocol) throws Exception {185186KeyStore ks = KeyStore.getInstance("JKS");187KeyStore ts = KeyStore.getInstance("JKS");188189char[] passphrase = "passphrase".toCharArray();190191try (FileInputStream fis = new FileInputStream(keyFilename)) {192ks.load(fis, passphrase);193}194195try (FileInputStream fis = new FileInputStream(trustFilename)) {196ts.load(fis, passphrase);197}198199KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");200kmf.init(ks, passphrase);201202TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");203tmf.init(ts);204205SSLContext sslCtx = SSLContext.getInstance(protocol);206207sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);208209sslc = sslCtx;210}211212/*213* Run the test.214*215* Sit in a tight loop, with the server engine calling wrap/unwrap216* regardless of whether data is available or not. We do this until217* we get the application data. Then we shutdown and go to the next one.218*219* The main loop handles all of the I/O phases of the SSLEngine's220* lifetime:221*222* initial handshaking223* application data transfer224* engine closing225*226* One could easily separate these phases into separate227* sections of code.228*/229private void runTest(boolean direct) throws Exception {230boolean serverClose = direct;231232ServerSocket serverSocket = new ServerSocket(0);233serverPort = serverSocket.getLocalPort();234235// Signal the client, the server is ready to accept connection.236serverCondition.countDown();237238Thread clientThread = runClient(serverClose);239240// Try to accept a connection in 30 seconds.241Socket socket;242try {243serverSocket.setSoTimeout(30000);244socket = (Socket) serverSocket.accept();245} catch (SocketTimeoutException ste) {246serverSocket.close();247248// Ignore the test case if no connection within 30 seconds.249System.out.println(250"No incoming client connection in 30 seconds. " +251"Ignore in server side.");252return;253}254255// handle the connection256try {257// Is it the expected client connection?258//259// Naughty test cases or third party routines may try to260// connection to this server port unintentionally. In261// order to mitigate the impact of unexpected client262// connections and avoid intermittent failure, it should263// be checked that the accepted connection is really linked264// to the expected client.265boolean clientIsReady =266clientCondition.await(30L, TimeUnit.SECONDS);267268if (clientIsReady) {269// Run the application in server side.270runServerApplication(socket, direct, serverClose);271} else { // Otherwise, ignore272// We don't actually care about plain socket connections273// for TLS communication testing generally. Just ignore274// the test if the accepted connection is not linked to275// the expected client or the client connection timeout276// in 30 seconds.277System.out.println(278"The client is not the expected one or timeout. " +279"Ignore in server side.");280}281} catch (Exception e) {282System.out.println("Server died ...");283e.printStackTrace(System.out);284serverException = e;285} finally {286socket.close();287serverSocket.close();288}289290clientThread.join();291292if (clientException != null || serverException != null) {293throw new RuntimeException("Test failed");294}295}296297/*298* Define the server side application of the test for the specified socket.299*/300void runServerApplication(Socket socket, boolean direct,301boolean serverClose) throws Exception {302303socket.setSoTimeout(500);304305createSSLEngine();306createBuffers(direct);307308boolean closed = false;309310InputStream is = socket.getInputStream();311OutputStream os = socket.getOutputStream();312313SSLEngineResult serverResult; // results from last operation314315/*316* Examining the SSLEngineResults could be much more involved,317* and may alter the overall flow of the application.318*319* For example, if we received a BUFFER_OVERFLOW when trying320* to write to the output pipe, we could reallocate a larger321* pipe, but instead we wait for the peer to drain it.322*/323byte[] inbound = new byte[8192];324byte[] outbound = new byte[8192];325326while (!isEngineClosed(serverEngine)) {327int len = 0;328329// Inbound data330log("================");331332// Read from the Client side.333try {334len = is.read(inbound);335if (len == -1) {336throw new Exception("Unexpected EOF");337}338cTOs.put(inbound, 0, len);339} catch (SocketTimeoutException ste) {340// swallow. Nothing yet, probably waiting on us.341System.out.println("Warning: " + ste);342}343344cTOs.flip();345346serverResult = serverEngine.unwrap(cTOs, serverIn);347log("server unwrap: ", serverResult);348runDelegatedTasks(serverResult, serverEngine);349cTOs.compact();350351// Outbound data352log("----");353354serverResult = serverEngine.wrap(serverOut, sTOc);355log("server wrap: ", serverResult);356runDelegatedTasks(serverResult, serverEngine);357358sTOc.flip();359360if ((len = sTOc.remaining()) != 0) {361sTOc.get(outbound, 0, len);362os.write(outbound, 0, len);363// Give the other side a chance to process364}365366sTOc.compact();367368if (!closed && (serverOut.remaining() == 0)) {369closed = true;370371/*372* We'll alternate initiatating the shutdown.373* When the server initiates, it will take one more374* loop, but tests the orderly shutdown.375*/376if (serverClose) {377serverEngine.closeOutbound();378}379}380381if (closed && isEngineClosed(serverEngine)) {382serverIn.flip();383384/*385* A sanity check to ensure we got what was sent.386*/387if (serverIn.remaining() != clientMsg.length) {388throw new Exception("Client: Data length error -" +389" IF THIS FAILS, PLEASE REPORT THIS TO THE" +390" SECURITY TEAM. WE HAVE BEEN UNABLE TO" +391" RELIABLY DUPLICATE.");392}393394for (int i = 0; i < clientMsg.length; i++) {395if (clientMsg[i] != serverIn.get()) {396throw new Exception("Client: Data content error -" +397" IF THIS FAILS, PLEASE REPORT THIS TO THE" +398" SECURITY TEAM. WE HAVE BEEN UNABLE TO" +399" RELIABLY DUPLICATE.");400}401}402serverIn.compact();403}404}405}406407/*408* Create a client thread which does simple SSLSocket operations.409* We'll write and read one data packet.410*/411private Thread runClient(final boolean serverClose)412throws Exception {413414Thread t = new Thread("ClientThread") {415416@Override417public void run() {418try {419doClientSide(serverClose);420} catch (Exception e) {421System.out.println("Client died ...");422e.printStackTrace(System.out);423clientException = e;424}425}426};427428t.start();429return t;430}431432/*433* Define the client side of the test.434*/435void doClientSide(boolean serverClose) throws Exception {436// Wait for server to get started.437//438// The server side takes care of the issue if the server cannot439// get started in 90 seconds. The client side would just ignore440// the test case if the serer is not ready.441boolean serverIsReady =442serverCondition.await(90L, TimeUnit.SECONDS);443if (!serverIsReady) {444System.out.println(445"The server is not ready yet in 90 seconds. " +446"Ignore in client side.");447return;448}449450SSLSocketFactory sslsf = sslc.getSocketFactory();451try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {452try {453sslSocket.connect(454new InetSocketAddress("localhost", serverPort), 15000);455} catch (IOException ioe) {456// The server side may be impacted by naughty test cases or457// third party routines, and cannot accept connections.458//459// Just ignore the test if the connection cannot be460// established.461System.out.println(462"Cannot make a connection in 15 seconds. " +463"Ignore in client side.");464return;465}466467// OK, here the client and server get connected.468469// Signal the server, the client is ready to communicate.470clientCondition.countDown();471472// There is still a chance in theory that the server thread may473// wait client-ready timeout and then quit. The chance should474// be really rare so we don't consider it until it becomes a475// real problem.476477// Run the application in client side.478runClientApplication(sslSocket, serverClose);479}480}481482/*483* Define the server side application of the test for the specified socket.484*/485void runClientApplication(SSLSocket sslSocket, boolean serverClose)486throws Exception {487488OutputStream os = sslSocket.getOutputStream();489InputStream is = sslSocket.getInputStream();490491// write(byte[]) goes in one shot.492os.write(clientMsg);493494byte[] inbound = new byte[2048];495int pos = 0;496497int len;498while ((len = is.read(inbound, pos, 2048 - pos)) != -1) {499pos += len;500// Let the client do the closing.501if ((pos == serverMsg.length) && !serverClose) {502sslSocket.close();503break;504}505}506507if (pos != serverMsg.length) {508throw new Exception("Client: Data length error");509}510511for (int i = 0; i < serverMsg.length; i++) {512if (inbound[i] != serverMsg[i]) {513throw new Exception("Client: Data content error");514}515}516}517518/*519* Using the SSLContext created during object creation,520* create/configure the SSLEngines we'll use for this test.521*/522private void createSSLEngine() throws Exception {523/*524* Configure the serverEngine to act as a server in the SSL/TLS525* handshake.526*/527serverEngine = sslc.createSSLEngine();528serverEngine.setUseClientMode(false);529serverEngine.getNeedClientAuth();530}531532/*533* Create and size the buffers appropriately.534*/535private void createBuffers(boolean direct) {536537SSLSession session = serverEngine.getSession();538int appBufferMax = session.getApplicationBufferSize();539int netBufferMax = session.getPacketBufferSize();540541/*542* We'll make the input buffers a bit bigger than the max needed543* size, so that unwrap()s following a successful data transfer544* won't generate BUFFER_OVERFLOWS.545*546* We'll use a mix of direct and indirect ByteBuffers for547* tutorial purposes only. In reality, only use direct548* ByteBuffers when they give a clear performance enhancement.549*/550if (direct) {551serverIn = ByteBuffer.allocateDirect(appBufferMax + 50);552cTOs = ByteBuffer.allocateDirect(netBufferMax);553sTOc = ByteBuffer.allocateDirect(netBufferMax);554} else {555serverIn = ByteBuffer.allocate(appBufferMax + 50);556cTOs = ByteBuffer.allocate(netBufferMax);557sTOc = ByteBuffer.allocate(netBufferMax);558}559560serverOut = ByteBuffer.wrap(serverMsg);561}562563/*564* If the result indicates that we have outstanding tasks to do,565* go ahead and run them in this thread.566*/567private static void runDelegatedTasks(SSLEngineResult result,568SSLEngine engine) throws Exception {569570if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {571Runnable runnable;572while ((runnable = engine.getDelegatedTask()) != null) {573log("\trunning delegated task...");574runnable.run();575}576HandshakeStatus hsStatus = engine.getHandshakeStatus();577if (hsStatus == HandshakeStatus.NEED_TASK) {578throw new Exception(579"handshake shouldn't need additional tasks");580}581log("\tnew HandshakeStatus: " + hsStatus);582}583}584585private static boolean isEngineClosed(SSLEngine engine) {586return (engine.isOutboundDone() && engine.isInboundDone());587}588589/*590* Logging code591*/592private static boolean resultOnce = true;593594private static void log(String str, SSLEngineResult result) {595if (!logging) {596return;597}598if (resultOnce) {599resultOnce = false;600System.out.println("The format of the SSLEngineResult is: \n"601+ "\t\"getStatus() / getHandshakeStatus()\" +\n"602+ "\t\"bytesConsumed() / bytesProduced()\"\n");603}604HandshakeStatus hsStatus = result.getHandshakeStatus();605log(str606+ result.getStatus() + "/" + hsStatus + ", "607+ result.bytesConsumed() + "/" + result.bytesProduced()608+ " bytes");609if (hsStatus == HandshakeStatus.FINISHED) {610log("\t...ready for application data");611}612}613614private static void log(String str) {615if (logging) {616System.out.println(str);617}618}619}620621622