1/*
2 * Copyright (c) 2003, 2013, 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//
25// SunJSSE does not support dynamic system properties, no way to re-use
26// system properties in samevm/agentvm mode.
27//
28
29/*
30 * @test
31 * @bug 4931274
32 * @summary closeInbound does not signal when a close_notify has not
33 *              been received.
34 * @run main/othervm CloseInboundException
35 * @author Brad Wetmore
36 */
37
38import javax.net.ssl.*;
39import javax.net.ssl.SSLEngineResult.*;
40import java.io.*;
41import java.security.*;
42import java.nio.*;
43
44public class CloseInboundException {
45
46    private SSLEngine ssle1;    // client
47    private SSLEngine ssle2;    // server
48
49    SSLEngineResult result1;    // ssle1's results from last operation
50    SSLEngineResult result2;    // ssle2's results from last operation
51
52    private static String pathToStores = "../../../../javax/net/ssl/etc";
53    private static String keyStoreFile = "keystore";
54    private static String trustStoreFile = "truststore";
55    private static String passwd = "passphrase";
56
57    private static String keyFilename =
58            System.getProperty("test.src", "./") + "/" + pathToStores +
59                "/" + keyStoreFile;
60    private static String trustFilename =
61            System.getProperty("test.src", "./") + "/" + pathToStores +
62                "/" + trustStoreFile;
63
64    private ByteBuffer appOut1;         // write side of ssle1
65    private ByteBuffer appIn1;          // read side of ssle1
66    private ByteBuffer appOut2;         // write side of ssle2
67    private ByteBuffer appIn2;          // read side of ssle2
68
69    private ByteBuffer oneToTwo;        // "reliable" transport ssle1->ssle2
70    private ByteBuffer twoToOne;        // "reliable" transport ssle2->ssle1
71
72    /*
73     * Majority of the test case is here, setup is done below.
74     */
75    private void runTest(boolean inboundClose) throws Exception {
76
77        boolean done = false;
78
79        while (!isEngineClosed(ssle1) || !isEngineClosed(ssle2)) {
80
81            System.out.println("================");
82
83            result1 = ssle1.wrap(appOut1, oneToTwo);
84            result2 = ssle2.wrap(appOut2, twoToOne);
85
86            System.out.println("wrap1 = " + result1);
87            System.out.println("oneToTwo  = " + oneToTwo);
88
89            System.out.println("wrap2 = " + result2);
90            System.out.println("twoToOne  = " + twoToOne);
91
92            runDelegatedTasks(result1, ssle1);
93            runDelegatedTasks(result2, ssle2);
94
95            oneToTwo.flip();
96            twoToOne.flip();
97
98            System.out.println("----");
99            result1 = ssle1.unwrap(twoToOne, appIn1);
100
101            if (done && inboundClose) {
102                try {
103                    result2 = ssle2.unwrap(oneToTwo, appIn2);
104                    throw new Exception("Didn't throw Exception");
105                } catch (SSLException e) {
106                    System.out.println("Caught proper exception\n" + e);
107                    return;
108                }
109            } else {
110                result2 = ssle2.unwrap(oneToTwo, appIn2);
111            }
112
113            System.out.println("unwrap1 = " + result1);
114            System.out.println("twoToOne  = " + twoToOne);
115
116            System.out.println("unwrap2 = " + result2);
117            System.out.println("oneToTwo  = " + oneToTwo);
118
119            runDelegatedTasks(result1, ssle1);
120            runDelegatedTasks(result2, ssle2);
121
122            oneToTwo.compact();
123            twoToOne.compact();
124
125            /*
126             * If we've transfered all the data between app1 and app2,
127             * we try to close and see what that gets us.
128             */
129            if (!done && (appOut1.limit() == appIn2.position()) &&
130                (appOut2.limit() == appIn1.position())) {
131
132                if (inboundClose) {
133                    try {
134                        System.out.println("Closing ssle1's *INBOUND*...");
135                        ssle1.closeInbound();
136                        throw new Exception("closeInbound didn't throw");
137                    } catch (SSLException e) {
138                        System.out.println("Caught closeInbound exc properly");
139                        checkStatus();
140                        /*
141                         * Let the message processing continue to
142                         * handle the alert.
143                         */
144                    }
145                    done = true;
146                } else {
147                    done = true;
148                    System.out.println("Closing ssle1's *OUTBOUND*...");
149                    ssle1.closeOutbound();
150                }
151            }
152        }
153    }
154
155    /*
156     * Check to see if the close generated a close_notify message,
157     * that the result status is sane, and that close again doesn't
158     * generate a new exception.
159     *
160     * We'll consume the wrapped data when we loop back around.
161     */
162    private void checkStatus() throws Exception {
163        System.out.println("\nCalling last wrap");
164        int pos = oneToTwo.position();
165
166        result1 = ssle1.wrap(appOut1, oneToTwo);
167        System.out.println("result1 = " + result1);
168
169        if ((pos >= oneToTwo.position()) ||
170                !result1.getStatus().equals(Status.CLOSED) ||
171                !result1.getHandshakeStatus().equals(
172                    HandshakeStatus.NOT_HANDSHAKING) ||
173                !ssle1.isOutboundDone() ||
174                !ssle1.isInboundDone()) {
175            throw new Exception(result1.toString());
176        }
177        System.out.println("Make sure we don't throw a second SSLException.");
178        ssle1.closeInbound();
179    }
180
181    public static void main(String args[]) throws Exception {
182
183        CloseInboundException test;
184
185        test = new CloseInboundException();
186        test.runTest(false);
187
188        test = new CloseInboundException();
189        test.runTest(true);
190        System.out.println("Test PASSED!!!");
191    }
192
193    /*
194     * **********************************************************
195     * Majority of the test case is above, below is just setup stuff
196     * **********************************************************
197     */
198
199    public CloseInboundException() throws Exception {
200
201        SSLContext sslc = getSSLContext(keyFilename, trustFilename);
202
203        ssle1 = sslc.createSSLEngine("host1", 1);
204        ssle1.setUseClientMode(true);
205
206        ssle2 = sslc.createSSLEngine("host2", 2);
207        ssle2.setUseClientMode(false);
208
209        createBuffers();
210    }
211
212    /*
213     * Create an initialized SSLContext to use for this test.
214     */
215    private SSLContext getSSLContext(String keyFile, String trustFile)
216            throws Exception {
217
218        KeyStore ks = KeyStore.getInstance("JKS");
219        KeyStore ts = KeyStore.getInstance("JKS");
220
221        char[] passphrase = "passphrase".toCharArray();
222
223        ks.load(new FileInputStream(keyFile), passphrase);
224        ts.load(new FileInputStream(trustFile), passphrase);
225
226        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
227        kmf.init(ks, passphrase);
228
229        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
230        tmf.init(ts);
231
232        SSLContext sslCtx = SSLContext.getInstance("TLS");
233
234        sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
235
236        return sslCtx;
237    }
238
239    private void createBuffers() {
240        // Size the buffers as appropriate.
241        SSLSession session = ssle1.getSession();
242        int appBufferMax = session.getApplicationBufferSize();
243        int netBufferMax = session.getPacketBufferSize();
244
245        appIn1 = ByteBuffer.allocateDirect(appBufferMax + 50);
246        appIn2 = ByteBuffer.allocateDirect(appBufferMax + 50);
247
248        oneToTwo = ByteBuffer.allocateDirect(netBufferMax);
249        twoToOne = ByteBuffer.allocateDirect(netBufferMax);
250
251        appOut1 = ByteBuffer.wrap("Hi Engine2, I'm SSLEngine1".getBytes());
252        appOut2 = ByteBuffer.wrap("Hello Engine1, I'm SSLEngine2".getBytes());
253
254        System.out.println("AppOut1 = " + appOut1);
255        System.out.println("AppOut2 = " + appOut2);
256        System.out.println();
257    }
258
259    private static void runDelegatedTasks(SSLEngineResult result,
260            SSLEngine engine) throws Exception {
261
262        if (result.getHandshakeStatus().equals(HandshakeStatus.NEED_TASK)) {
263            Runnable runnable;
264            while ((runnable = engine.getDelegatedTask()) != null) {
265                System.out.println("running delegated task...");
266                runnable.run();
267            }
268        }
269    }
270
271    private static boolean isEngineClosed(SSLEngine engine) {
272        return (engine.isOutboundDone() && engine.isInboundDone());
273    }
274
275}
276