1/*
2 * Copyright (c) 2010, 2016, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26//
27// SunJSSE does not support dynamic system properties, no way to re-use
28// system properties in samevm/agentvm mode.
29//
30
31/*
32 * @test
33 * @bug 4873188
34 * @summary Support TLS 1.1
35 * @run main/othervm ExportableBlockCipher
36 * @modules java.security.jgss
37 *          java.security.jgss/sun.security.krb5:+open
38 *          java.security.jgss/sun.security.krb5.internal:+open
39 *          java.security.jgss/sun.security.krb5.internal.ccache
40 *          java.security.jgss/sun.security.krb5.internal.crypto
41 *          java.security.jgss/sun.security.krb5.internal.ktab
42 *          java.base/sun.security.util
43 * @author Xuelei Fan
44 */
45
46import java.io.IOException;
47import java.io.InputStream;
48import java.io.OutputStream;
49import javax.net.ssl.SSLException;
50import javax.net.ssl.SSLHandshakeException;
51import javax.net.ssl.SSLServerSocket;
52import javax.net.ssl.SSLServerSocketFactory;
53import javax.net.ssl.SSLSocket;
54import javax.net.ssl.SSLSocketFactory;
55
56public class ExportableBlockCipher {
57
58    /*
59     * =============================================================
60     * Set the various variables needed for the tests, then
61     * specify what tests to run on each side.
62     */
63
64    /*
65     * Should we run the client or server in a separate thread?
66     * Both sides can throw exceptions, but do you have a preference
67     * as to which side should be the main thread.
68     */
69    static boolean separateServerThread = false;
70
71    /*
72     * Where do we find the keystores?
73     */
74    static String pathToStores = "../etc";
75    static String keyStoreFile = "keystore";
76    static String trustStoreFile = "truststore";
77    static String passwd = "passphrase";
78
79    /*
80     * Is the server ready to serve?
81     */
82    volatile static boolean serverReady = false;
83
84    /*
85     * Turn on SSL debugging?
86     */
87    static boolean debug = false;
88
89    /*
90     * If the client or server is doing some kind of object creation
91     * that the other side depends on, and that thread prematurely
92     * exits, you may experience a hang.  The test harness will
93     * terminate all hung threads after its timeout has expired,
94     * currently 3 minutes by default, but you might try to be
95     * smart about it....
96     */
97
98    /*
99     * Define the server side of the test.
100     *
101     * If the server prematurely exits, serverReady will be set to true
102     * to avoid infinite hangs.
103     */
104    void doServerSide() throws Exception {
105        SSLServerSocketFactory sslssf =
106            (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
107        SSLServerSocket sslServerSocket =
108            (SSLServerSocket) sslssf.createServerSocket(serverPort);
109
110        serverPort = sslServerSocket.getLocalPort();
111
112        /*
113         * Signal Client, we're ready for his connect.
114         */
115        serverReady = true;
116
117        SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
118        InputStream sslIS = sslSocket.getInputStream();
119        OutputStream sslOS = sslSocket.getOutputStream();
120
121        boolean interrupted = false;
122        try {
123            sslIS.read();
124            sslOS.write('A');
125            sslOS.flush();
126        } catch (IOException ioe) {
127            // get the expected exception
128            interrupted = true;
129        } finally {
130            sslSocket.close();
131        }
132
133        if (!interrupted) {
134            throw new SSLHandshakeException(
135                "A weak cipher suite is negotiated, " +
136                "TLSv1.1 must not negotiate the exportable cipher suites.");
137        }
138    }
139
140    /*
141     * Define the client side of the test.
142     *
143     * If the server prematurely exits, serverReady will be set to true
144     * to avoid infinite hangs.
145     */
146    void doClientSide() throws Exception {
147
148        /*
149         * Wait for server to get started.
150         */
151        while (!serverReady) {
152            Thread.sleep(50);
153        }
154
155        SSLSocketFactory sslsf =
156            (SSLSocketFactory) SSLSocketFactory.getDefault();
157        SSLSocket sslSocket = (SSLSocket)
158            sslsf.createSocket("localhost", serverPort);
159
160        // enable TLSv1.1 only
161        sslSocket.setEnabledProtocols(new String[] {"TLSv1.1"});
162
163        // enable a exportable block cipher
164        sslSocket.setEnabledCipherSuites(
165            new String[] {"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA"});
166
167        InputStream sslIS = sslSocket.getInputStream();
168        OutputStream sslOS = sslSocket.getOutputStream();
169
170        boolean interrupted = false;
171        try {
172            sslOS.write('B');
173            sslOS.flush();
174            sslIS.read();
175        } catch (SSLException ssle) {
176            // get the expected exception
177            interrupted = true;
178        } finally {
179            sslSocket.close();
180        }
181
182        if (!interrupted) {
183            throw new SSLHandshakeException(
184                "A weak cipher suite is negotiated, " +
185                "TLSv1.1 must not negotiate the exportable cipher suites.");
186        }
187    }
188
189    /*
190     * =============================================================
191     * The remainder is just support stuff
192     */
193
194    // use any free port by default
195    volatile int serverPort = 0;
196
197    volatile Exception serverException = null;
198    volatile Exception clientException = null;
199
200    public static void main(String[] args) throws Exception {
201        String keyFilename =
202            System.getProperty("test.src", ".") + "/" + pathToStores +
203                "/" + keyStoreFile;
204        String trustFilename =
205            System.getProperty("test.src", ".") + "/" + pathToStores +
206                "/" + trustStoreFile;
207
208        System.setProperty("javax.net.ssl.keyStore", keyFilename);
209        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
210        System.setProperty("javax.net.ssl.trustStore", trustFilename);
211        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
212
213        if (debug)
214            System.setProperty("javax.net.debug", "all");
215
216        /*
217         * Start the tests.
218         */
219        new ExportableBlockCipher();
220    }
221
222    Thread clientThread = null;
223    Thread serverThread = null;
224
225    /*
226     * Primary constructor, used to drive remainder of the test.
227     *
228     * Fork off the other side, then do your work.
229     */
230    ExportableBlockCipher() throws Exception {
231        try {
232            if (separateServerThread) {
233                startServer(true);
234                startClient(false);
235            } else {
236                startClient(true);
237                startServer(false);
238            }
239        } catch (Exception e) {
240            // swallow for now.  Show later
241        }
242
243        /*
244         * Wait for other side to close down.
245         */
246        if (separateServerThread) {
247            serverThread.join();
248        } else {
249            clientThread.join();
250        }
251
252        /*
253         * When we get here, the test is pretty much over.
254         * Which side threw the error?
255         */
256        Exception local;
257        Exception remote;
258        String whichRemote;
259
260        if (separateServerThread) {
261            remote = serverException;
262            local = clientException;
263            whichRemote = "server";
264        } else {
265            remote = clientException;
266            local = serverException;
267            whichRemote = "client";
268        }
269
270        /*
271         * If both failed, return the curthread's exception, but also
272         * print the remote side Exception
273         */
274        if ((local != null) && (remote != null)) {
275            System.out.println(whichRemote + " also threw:");
276            remote.printStackTrace();
277            System.out.println();
278            throw local;
279        }
280
281        if (remote != null) {
282            throw remote;
283        }
284
285        if (local != null) {
286            throw local;
287        }
288    }
289
290    void startServer(boolean newThread) throws Exception {
291        if (newThread) {
292            serverThread = new Thread() {
293                public void run() {
294                    try {
295                        doServerSide();
296                    } catch (Exception e) {
297                        /*
298                         * Our server thread just died.
299                         *
300                         * Release the client, if not active already...
301                         */
302                        System.err.println("Server died...");
303                        serverReady = true;
304                        serverException = e;
305                    }
306                }
307            };
308            serverThread.start();
309        } else {
310            try {
311                doServerSide();
312            } catch (Exception e) {
313                serverException = e;
314            } finally {
315                serverReady = true;
316            }
317        }
318    }
319
320    void startClient(boolean newThread) throws Exception {
321        if (newThread) {
322            clientThread = new Thread() {
323                public void run() {
324                    try {
325                        doClientSide();
326                    } catch (Exception e) {
327                        /*
328                         * Our client thread just died.
329                         */
330                        System.err.println("Client died...");
331                        clientException = e;
332                    }
333                }
334            };
335            clientThread.start();
336        } else {
337            try {
338                doClientSide();
339            } catch (Exception e) {
340                clientException = e;
341            }
342        }
343    }
344}
345