Path: blob/master/test/jdk/sun/security/ssl/SSLEngineImpl/SSLEngineBadBufferArrayAccess.java
41152 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 /test/lib33* @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;8889import jdk.test.lib.security.SecurityUtils;9091public class SSLEngineBadBufferArrayAccess {9293/*94* Enables logging of the SSL/TLS operations.95*/96private static boolean logging = true;9798/*99* Enables the JSSE system debugging system property:100*101* -Djavax.net.debug=all102*103* This gives a lot of low-level information about operations underway,104* including specific handshake messages, and might be best examined105* after gaining some familiarity with this application.106*/107private static boolean debug = false;108private SSLContext sslc;109private SSLEngine serverEngine; // server-side SSLEngine110111private final byte[] serverMsg = "Hi there Client, I'm a Server".getBytes();112private final byte[] clientMsg = "Hello Server, I'm a Client".getBytes();113114private ByteBuffer serverOut; // write side of serverEngine115private ByteBuffer serverIn; // read side of serverEngine116117private volatile Exception clientException;118private volatile Exception serverException;119120/*121* For data transport, this example uses local ByteBuffers.122*/123private ByteBuffer cTOs; // "reliable" transport client->server124private ByteBuffer sTOc; // "reliable" transport server->client125126/*127* The following is to set up the keystores/trust material.128*/129private static final String pathToStores = "../../../../javax/net/ssl/etc";130private static final String keyStoreFile = "keystore";131private static final String trustStoreFile = "truststore";132private static final String passwd = "passphrase";133private static String keyFilename =134System.getProperty("test.src", ".") + "/" + pathToStores135+ "/" + keyStoreFile;136private static String trustFilename =137System.getProperty("test.src", ".") + "/" + pathToStores138+ "/" + trustStoreFile;139140/*141* Is the server ready to serve?142*/143private static final CountDownLatch serverCondition = new CountDownLatch(1);144145/*146* Is the client ready to handshake?147*/148private static final CountDownLatch clientCondition = new CountDownLatch(1);149150/*151* What's the server port? Use any free port by default152*/153private volatile int serverPort = 0;154155/*156* Main entry point for this test.157*/158public static void main(String args[]) throws Exception {159if (debug) {160System.setProperty("javax.net.debug", "all");161}162163// Re-enable TLSv1 and TLSv1.1 since test depends on them.164SecurityUtils.removeFromDisabledTlsAlgs("TLSv1", "TLSv1.1");165166String [] protocols = new String [] {167"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" };168169for (String protocol : protocols) {170/*171* Run the tests with direct and indirect buffers.172*/173log("Testing " + protocol + ":true");174new SSLEngineBadBufferArrayAccess(protocol).runTest(true);175176log("Testing " + protocol + ":false");177new SSLEngineBadBufferArrayAccess(protocol).runTest(false);178}179180System.out.println("Test Passed.");181}182183/*184* Create an initialized SSLContext to use for these tests.185*/186public SSLEngineBadBufferArrayAccess(String protocol) throws Exception {187188KeyStore ks = KeyStore.getInstance("JKS");189KeyStore ts = KeyStore.getInstance("JKS");190191char[] passphrase = "passphrase".toCharArray();192193try (FileInputStream fis = new FileInputStream(keyFilename)) {194ks.load(fis, passphrase);195}196197try (FileInputStream fis = new FileInputStream(trustFilename)) {198ts.load(fis, passphrase);199}200201KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");202kmf.init(ks, passphrase);203204TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");205tmf.init(ts);206207SSLContext sslCtx = SSLContext.getInstance(protocol);208209sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);210211sslc = sslCtx;212}213214/*215* Run the test.216*217* Sit in a tight loop, with the server engine calling wrap/unwrap218* regardless of whether data is available or not. We do this until219* we get the application data. Then we shutdown and go to the next one.220*221* The main loop handles all of the I/O phases of the SSLEngine's222* lifetime:223*224* initial handshaking225* application data transfer226* engine closing227*228* One could easily separate these phases into separate229* sections of code.230*/231private void runTest(boolean direct) throws Exception {232boolean serverClose = direct;233234ServerSocket serverSocket = new ServerSocket(0);235serverPort = serverSocket.getLocalPort();236237// Signal the client, the server is ready to accept connection.238serverCondition.countDown();239240Thread clientThread = runClient(serverClose);241242// Try to accept a connection in 30 seconds.243Socket socket;244try {245serverSocket.setSoTimeout(30000);246socket = (Socket) serverSocket.accept();247} catch (SocketTimeoutException ste) {248serverSocket.close();249250// Ignore the test case if no connection within 30 seconds.251System.out.println(252"No incoming client connection in 30 seconds. " +253"Ignore in server side.");254return;255}256257// handle the connection258try {259// Is it the expected client connection?260//261// Naughty test cases or third party routines may try to262// connection to this server port unintentionally. In263// order to mitigate the impact of unexpected client264// connections and avoid intermittent failure, it should265// be checked that the accepted connection is really linked266// to the expected client.267boolean clientIsReady =268clientCondition.await(30L, TimeUnit.SECONDS);269270if (clientIsReady) {271// Run the application in server side.272runServerApplication(socket, direct, serverClose);273} else { // Otherwise, ignore274// We don't actually care about plain socket connections275// for TLS communication testing generally. Just ignore276// the test if the accepted connection is not linked to277// the expected client or the client connection timeout278// in 30 seconds.279System.out.println(280"The client is not the expected one or timeout. " +281"Ignore in server side.");282}283} catch (Exception e) {284System.out.println("Server died ...");285e.printStackTrace(System.out);286serverException = e;287} finally {288socket.close();289serverSocket.close();290}291292clientThread.join();293294if (clientException != null || serverException != null) {295throw new RuntimeException("Test failed");296}297}298299/*300* Define the server side application of the test for the specified socket.301*/302void runServerApplication(Socket socket, boolean direct,303boolean serverClose) throws Exception {304305socket.setSoTimeout(500);306307createSSLEngine();308createBuffers(direct);309310boolean closed = false;311312InputStream is = socket.getInputStream();313OutputStream os = socket.getOutputStream();314315SSLEngineResult serverResult; // results from last operation316317/*318* Examining the SSLEngineResults could be much more involved,319* and may alter the overall flow of the application.320*321* For example, if we received a BUFFER_OVERFLOW when trying322* to write to the output pipe, we could reallocate a larger323* pipe, but instead we wait for the peer to drain it.324*/325byte[] inbound = new byte[8192];326byte[] outbound = new byte[8192];327328while (!isEngineClosed(serverEngine)) {329int len = 0;330331// Inbound data332log("================");333334// Read from the Client side.335try {336len = is.read(inbound);337if (len == -1) {338throw new Exception("Unexpected EOF");339}340cTOs.put(inbound, 0, len);341} catch (SocketTimeoutException ste) {342// swallow. Nothing yet, probably waiting on us.343System.out.println("Warning: " + ste);344}345346cTOs.flip();347348serverResult = serverEngine.unwrap(cTOs, serverIn);349log("server unwrap: ", serverResult);350runDelegatedTasks(serverResult, serverEngine);351cTOs.compact();352353// Outbound data354log("----");355356serverResult = serverEngine.wrap(serverOut, sTOc);357log("server wrap: ", serverResult);358runDelegatedTasks(serverResult, serverEngine);359360sTOc.flip();361362if ((len = sTOc.remaining()) != 0) {363sTOc.get(outbound, 0, len);364os.write(outbound, 0, len);365// Give the other side a chance to process366}367368sTOc.compact();369370if (!closed && (serverOut.remaining() == 0)) {371closed = true;372373/*374* We'll alternate initiatating the shutdown.375* When the server initiates, it will take one more376* loop, but tests the orderly shutdown.377*/378if (serverClose) {379serverEngine.closeOutbound();380}381}382383if (closed && isEngineClosed(serverEngine)) {384serverIn.flip();385386/*387* A sanity check to ensure we got what was sent.388*/389if (serverIn.remaining() != clientMsg.length) {390throw new Exception("Client: Data length error -" +391" IF THIS FAILS, PLEASE REPORT THIS TO THE" +392" SECURITY TEAM. WE HAVE BEEN UNABLE TO" +393" RELIABLY DUPLICATE.");394}395396for (int i = 0; i < clientMsg.length; i++) {397if (clientMsg[i] != serverIn.get()) {398throw new Exception("Client: Data content error -" +399" IF THIS FAILS, PLEASE REPORT THIS TO THE" +400" SECURITY TEAM. WE HAVE BEEN UNABLE TO" +401" RELIABLY DUPLICATE.");402}403}404serverIn.compact();405}406}407}408409/*410* Create a client thread which does simple SSLSocket operations.411* We'll write and read one data packet.412*/413private Thread runClient(final boolean serverClose)414throws Exception {415416Thread t = new Thread("ClientThread") {417418@Override419public void run() {420try {421doClientSide(serverClose);422} catch (Exception e) {423System.out.println("Client died ...");424e.printStackTrace(System.out);425clientException = e;426}427}428};429430t.start();431return t;432}433434/*435* Define the client side of the test.436*/437void doClientSide(boolean serverClose) throws Exception {438// Wait for server to get started.439//440// The server side takes care of the issue if the server cannot441// get started in 90 seconds. The client side would just ignore442// the test case if the serer is not ready.443boolean serverIsReady =444serverCondition.await(90L, TimeUnit.SECONDS);445if (!serverIsReady) {446System.out.println(447"The server is not ready yet in 90 seconds. " +448"Ignore in client side.");449return;450}451452SSLSocketFactory sslsf = sslc.getSocketFactory();453try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {454try {455sslSocket.connect(456new InetSocketAddress("localhost", serverPort), 15000);457} catch (IOException ioe) {458// The server side may be impacted by naughty test cases or459// third party routines, and cannot accept connections.460//461// Just ignore the test if the connection cannot be462// established.463System.out.println(464"Cannot make a connection in 15 seconds. " +465"Ignore in client side.");466return;467}468469// OK, here the client and server get connected.470471// Signal the server, the client is ready to communicate.472clientCondition.countDown();473474// There is still a chance in theory that the server thread may475// wait client-ready timeout and then quit. The chance should476// be really rare so we don't consider it until it becomes a477// real problem.478479// Run the application in client side.480runClientApplication(sslSocket, serverClose);481}482}483484/*485* Define the server side application of the test for the specified socket.486*/487void runClientApplication(SSLSocket sslSocket, boolean serverClose)488throws Exception {489490OutputStream os = sslSocket.getOutputStream();491InputStream is = sslSocket.getInputStream();492493// write(byte[]) goes in one shot.494os.write(clientMsg);495496byte[] inbound = new byte[2048];497int pos = 0;498499int len;500while ((len = is.read(inbound, pos, 2048 - pos)) != -1) {501pos += len;502// Let the client do the closing.503if ((pos == serverMsg.length) && !serverClose) {504sslSocket.close();505break;506}507}508509if (pos != serverMsg.length) {510throw new Exception("Client: Data length error");511}512513for (int i = 0; i < serverMsg.length; i++) {514if (inbound[i] != serverMsg[i]) {515throw new Exception("Client: Data content error");516}517}518}519520/*521* Using the SSLContext created during object creation,522* create/configure the SSLEngines we'll use for this test.523*/524private void createSSLEngine() throws Exception {525/*526* Configure the serverEngine to act as a server in the SSL/TLS527* handshake.528*/529serverEngine = sslc.createSSLEngine();530serverEngine.setUseClientMode(false);531serverEngine.getNeedClientAuth();532}533534/*535* Create and size the buffers appropriately.536*/537private void createBuffers(boolean direct) {538539SSLSession session = serverEngine.getSession();540int appBufferMax = session.getApplicationBufferSize();541int netBufferMax = session.getPacketBufferSize();542543/*544* We'll make the input buffers a bit bigger than the max needed545* size, so that unwrap()s following a successful data transfer546* won't generate BUFFER_OVERFLOWS.547*548* We'll use a mix of direct and indirect ByteBuffers for549* tutorial purposes only. In reality, only use direct550* ByteBuffers when they give a clear performance enhancement.551*/552if (direct) {553serverIn = ByteBuffer.allocateDirect(appBufferMax + 50);554cTOs = ByteBuffer.allocateDirect(netBufferMax);555sTOc = ByteBuffer.allocateDirect(netBufferMax);556} else {557serverIn = ByteBuffer.allocate(appBufferMax + 50);558cTOs = ByteBuffer.allocate(netBufferMax);559sTOc = ByteBuffer.allocate(netBufferMax);560}561562serverOut = ByteBuffer.wrap(serverMsg);563}564565/*566* If the result indicates that we have outstanding tasks to do,567* go ahead and run them in this thread.568*/569private static void runDelegatedTasks(SSLEngineResult result,570SSLEngine engine) throws Exception {571572if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {573Runnable runnable;574while ((runnable = engine.getDelegatedTask()) != null) {575log("\trunning delegated task...");576runnable.run();577}578HandshakeStatus hsStatus = engine.getHandshakeStatus();579if (hsStatus == HandshakeStatus.NEED_TASK) {580throw new Exception(581"handshake shouldn't need additional tasks");582}583log("\tnew HandshakeStatus: " + hsStatus);584}585}586587private static boolean isEngineClosed(SSLEngine engine) {588return (engine.isOutboundDone() && engine.isInboundDone());589}590591/*592* Logging code593*/594private static boolean resultOnce = true;595596private static void log(String str, SSLEngineResult result) {597if (!logging) {598return;599}600if (resultOnce) {601resultOnce = false;602System.out.println("The format of the SSLEngineResult is: \n"603+ "\t\"getStatus() / getHandshakeStatus()\" +\n"604+ "\t\"bytesConsumed() / bytesProduced()\"\n");605}606HandshakeStatus hsStatus = result.getHandshakeStatus();607log(str608+ result.getStatus() + "/" + hsStatus + ", "609+ result.bytesConsumed() + "/" + result.bytesProduced()610+ " bytes");611if (hsStatus == HandshakeStatus.FINISHED) {612log("\t...ready for application data");613}614}615616private static void log(String str) {617if (logging) {618System.out.println(str);619}620}621}622623624