1 /* 2 * Copyright (c) 2001, 2011, 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 * @test 26 * @bug 4474255 27 * @test 1.1 01/06/27 28 * @bug 4484246 29 * @summary When an application enables anonymous SSL cipher suite, 30 * Hostname verification is not required 31 * @run main/othervm ComHostnameVerifier 32 * 33 * SunJSSE does not support dynamic system properties, no way to re-use 34 * system properties in samevm/agentvm mode. 35 */ 36 37 import java.io.*; 38 import java.net.*; 39 import javax.net.ssl.*; 40 import javax.security.cert.*; 41 import com.sun.net.ssl.HostnameVerifier; 42 import com.sun.net.ssl.HttpsURLConnection; 43 44 /** 45 * Use com.net.net.ssl.HostnameVerifier 46 */ 47 public class ComHostnameVerifier { 48 49 /* 50 * ============================================================= 51 * Set the various variables needed for the tests, then 52 * specify what tests to run on each side. 53 */ 54 55 /* 56 * Should we run the client or server in a separate thread? 57 * Both sides can throw exceptions, but do you have a preference 58 * as to which side should be the main thread. 59 */ 60 static boolean separateServerThread = true; 61 62 /* 63 * Is the server ready to serve? 64 */ 65 volatile static boolean serverReady = false; 66 67 /* 68 * Turn on SSL debugging? 69 */ 70 static boolean debug = false; 71 72 /* 73 * If the client or server is doing some kind of object creation 74 * that the other side depends on, and that thread prematurely 75 * exits, you may experience a hang. The test harness will 76 * terminate all hung threads after its timeout has expired, 77 * currently 3 minutes by default, but you might try to be 78 * smart about it.... 79 */ 80 81 /** 82 * Returns the path to the file obtained from 83 * parsing the HTML header. 84 */ 85 private static String getPath(DataInputStream in) 86 throws IOException 87 { 88 String line = in.readLine(); 89 if (line == null) 90 return null; 91 String path = ""; 92 // extract class from GET line 93 if (line.startsWith("GET /")) { 94 line = line.substring(5, line.length()-1).trim(); 95 int index = line.indexOf(' '); 96 if (index != -1) { 97 path = line.substring(0, index); 98 } 99 } 100 101 // eat the rest of header 102 do { 103 line = in.readLine(); 104 } while ((line.length() != 0) && 105 (line.charAt(0) != '\r') && (line.charAt(0) != '\n')); 106 107 if (path.length() != 0) { 108 return path; 109 } else { 110 throw new IOException("Malformed Header"); 111 } 112 } 113 114 /** 115 * Returns an array of bytes containing the bytes for 116 * the file represented by the argument <b>path</b>. 117 * 118 * In our case, we just pretend to send something back. 119 * 120 * @return the bytes for the file 121 * @exception FileNotFoundException if the file corresponding 122 * to <b>path</b> could not be loaded. 123 */ 124 private byte[] getBytes(String path) 125 throws IOException 126 { 127 return "Hello world, I am here".getBytes(); 128 } 129 130 /* 131 * Define the server side of the test. 132 * 133 * If the server prematurely exits, serverReady will be set to true 134 * to avoid infinite hangs. 135 */ 136 void doServerSide() throws Exception { 137 138 SSLServerSocketFactory sslssf = 139 (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); 140 SSLServerSocket sslServerSocket = 141 (SSLServerSocket) sslssf.createServerSocket(serverPort); 142 serverPort = sslServerSocket.getLocalPort(); 143 144 String ciphers[]= { "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA" }; 145 sslServerSocket.setEnabledCipherSuites(ciphers); 146 147 /* 148 * Signal Client, we're ready for his connect. 149 */ 150 serverReady = true; 151 152 SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); 153 DataOutputStream out = 154 new DataOutputStream(sslSocket.getOutputStream()); 155 156 try { 157 // get path to class file from header 158 DataInputStream in = 159 new DataInputStream(sslSocket.getInputStream()); 160 String path = getPath(in); 161 // retrieve bytecodes 162 byte[] bytecodes = getBytes(path); 163 // send bytecodes in response (assumes HTTP/1.0 or later) 164 try { 165 out.writeBytes("HTTP/1.0 200 OK\r\n"); 166 out.writeBytes("Content-Length: " + bytecodes.length + "\r\n"); 167 out.writeBytes("Content-Type: text/html\r\n\r\n"); 168 out.write(bytecodes); 169 out.flush(); 170 } catch (IOException ie) { 171 ie.printStackTrace(); 172 return; 173 } 174 175 } catch (Exception e) { 176 e.printStackTrace(); 177 // write out error response 178 out.writeBytes("HTTP/1.0 400 " + e.getMessage() + "\r\n"); 179 out.writeBytes("Content-Type: text/html\r\n\r\n"); 180 out.flush(); 181 } finally { 182 // close the socket 183 System.out.println("Server closing socket"); 184 sslSocket.close(); 185 serverReady = false; 186 } 187 } 188 189 private static class ComSunHTTPSHandlerFactory implements URLStreamHandlerFactory { 190 private static String SUPPORTED_PROTOCOL = "https"; 191 192 public URLStreamHandler createURLStreamHandler(String protocol) { 193 if (!protocol.equalsIgnoreCase(SUPPORTED_PROTOCOL)) 194 return null; 195 196 return new com.sun.net.ssl.internal.www.protocol.https.Handler(); 197 } 198 } 199 200 /* 201 * Define the client side of the test. 202 * 203 * If the server prematurely exits, serverReady will be set to true 204 * to avoid infinite hangs. 205 */ 206 void doClientSide() throws Exception { 207 /* 208 * Wait for server to get started. 209 */ 210 while (!serverReady) { 211 Thread.sleep(50); 212 } 213 214 URL.setURLStreamHandlerFactory(new ComSunHTTPSHandlerFactory()); 215 216 System.setProperty("https.cipherSuites", 217 "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA"); 218 219 // use the default hostname verifier 220 221 URL url = new URL("https://" + "localhost:" + serverPort + 222 "/etc/hosts"); 223 URLConnection urlc = url.openConnection(); 224 225 if (!(urlc instanceof com.sun.net.ssl.HttpsURLConnection)) { 226 throw new Exception( 227 "URLConnection ! instanceof " + 228 "com.sun.net.ssl.HttpsURLConnection"); 229 } 230 231 BufferedReader in = null; 232 try { 233 in = new BufferedReader(new InputStreamReader( 234 urlc.getInputStream())); 235 String inputLine; 236 System.out.print("Client reading... "); 237 while ((inputLine = in.readLine()) != null) 238 System.out.println(inputLine); 239 System.out.println("Cipher Suite: " + 240 ((HttpsURLConnection)urlc).getCipherSuite()); 241 in.close(); 242 } catch (SSLException e) { 243 if (in != null) 244 in.close(); 245 throw e; 246 } 247 System.out.println("Client reports: SUCCESS"); 248 } 249 250 /* 251 * ============================================================= 252 * The remainder is just support stuff 253 */ 254 255 // use any free port by default 256 volatile int serverPort = 0; 257 258 volatile Exception serverException = null; 259 volatile Exception clientException = null; 260 261 public static void main(String[] args) throws Exception { 262 263 if (debug) 264 System.setProperty("javax.net.debug", "all"); 265 266 /* 267 * Start the tests. 268 */ 269 new ComHostnameVerifier(); 270 } 271 272 Thread clientThread = null; 273 Thread serverThread = null; 274 275 /* 276 * Primary constructor, used to drive remainder of the test. 277 * 278 * Fork off the other side, then do your work. 279 */ 280 ComHostnameVerifier() throws Exception { 281 if (separateServerThread) { 282 startServer(true); 283 startClient(false); 284 } else { 285 startClient(true); 286 startServer(false); 287 } 288 289 /* 290 * Wait for other side to close down. 291 */ 292 if (separateServerThread) { 293 serverThread.join(); 294 } else { 295 clientThread.join(); 296 } 297 298 /* 299 * When we get here, the test is pretty much over. 300 * 301 * If the main thread excepted, that propagates back 302 * immediately. If the other thread threw an exception, we 303 * should report back. 304 */ 305 if (serverException != null) { 306 System.out.print("Server Exception:"); 307 throw serverException; 308 } 309 if (clientException != null) { 310 System.out.print("Client Exception:"); 311 throw clientException; 312 } 313 } 314 315 void startServer(boolean newThread) throws Exception { 316 if (newThread) { 317 serverThread = new Thread() { 318 public void run() { 319 try { 320 doServerSide(); 321 } catch (Exception e) { 322 /* 323 * Our server thread just died. 324 * 325 * Release the client, if not active already... 326 */ 327 System.err.println("Server died..."); 328 serverReady = true; 329 serverException = e; 330 } 331 } 332 }; 333 serverThread.start(); 334 } else { 335 doServerSide(); 336 } 337 } 338 339 void startClient(boolean newThread) throws Exception { 340 if (newThread) { 341 clientThread = new Thread() { 342 public void run() { 343 try { 344 doClientSide(); 345 } catch (Exception e) { 346 /* 347 * Our client thread just died. 348 */ 349 System.err.println("Client died..."); 350 clientException = e; 351 } 352 } 353 }; 354 clientThread.start(); 355 } else { 356 doClientSide(); 357 } 358 } 359 }