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 }