1 /* 2 * Copyright (c) 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 /* @test 25 * @bug 8004502 26 * @summary Sanity check that NTLM will not be selected by the http protocol 27 * handler when running on a profile that does not support NTLM 28 * @run main/othervm NoNTLM 29 */ 30 31 import java.net.*; 32 import java.io.*; 33 import sun.net.www.MessageHeader; 34 35 public class NoNTLM { 36 37 static final String CRLF = "\r\n"; 38 39 static final String OKAY = 40 "HTTP/1.1 200" + CRLF + 41 "Content-Length: 0" + CRLF + 42 "Connection: close" + CRLF + 43 CRLF; 44 45 static class Client implements Runnable { 46 private final URL url; 47 private volatile IOException ioe; 48 private volatile int respCode; 49 50 Client(int port) throws IOException { 51 this.url = new URL("http://127.0.0.1:" + port + "/foo.html"); 52 } 53 54 public void run() { 55 try { 56 HttpURLConnection uc = 57 (HttpURLConnection)url.openConnection(Proxy.NO_PROXY); 58 try { 59 uc.getInputStream(); 60 } catch (IOException x) { 61 respCode = uc.getResponseCode(); 62 throw x; 63 } 64 uc.disconnect(); 65 } catch (IOException x) { 66 if (respCode == 0) 67 respCode = -1; 68 ioe = x; 69 } 70 } 71 72 IOException ioException() { 73 return ioe; 74 } 75 76 int respCode() { 77 return respCode; 78 } 79 80 static void start(int port) throws IOException { 81 Client client = new Client(port); 82 new Thread(client).start(); 83 } 84 } 85 86 /** 87 * Return the http response with WWW-Authenticate headers for the given 88 * authentication schemes. 89 */ 90 static String authReplyFor(String... schemes) { 91 // construct the server reply 92 String reply = "HTTP/1.1 401 Unauthorized" + CRLF + 93 "Content-Length: 0"+ CRLF + 94 "Connection: close" + CRLF; 95 for (String s: schemes) { 96 switch (s) { 97 case "Basic" : 98 reply += "WWW-Authenticate: Basic realm=\"wallyworld\"" + CRLF; 99 break; 100 case "Digest" : 101 reply += "WWW-Authenticate: Digest" + 102 " realm=\"wallyworld\"" + 103 " domain=/" + 104 " nonce=\"abcdefghijklmnopqrstuvwxyz\"" + 105 " qop=\"auth\"" + CRLF; 106 break; 107 case "NTLM" : 108 reply += "WWW-Authenticate: NTLM" + CRLF; 109 break; 110 default : 111 throw new RuntimeException("Should not get here"); 112 } 113 } 114 reply += CRLF; 115 return reply; 116 } 117 118 /** 119 * Test the http protocol handler with the given authentication schemes 120 * in the WWW-Authenticate header. 121 */ 122 static void test(String... schemes) throws IOException { 123 124 // the authentication scheme that the client is expected to choose 125 String expected = null; 126 for (String s: schemes) { 127 if (expected == null) { 128 expected = s; 129 } else if (s.equals("Digest")) { 130 expected = s; 131 } 132 } 133 134 // server reply 135 String reply = authReplyFor(schemes); 136 137 System.out.println("===================================="); 138 System.out.println("Expect client to choose: " + expected); 139 System.out.println(reply); 140 141 try (ServerSocket ss = new ServerSocket(0)) { 142 Client.start(ss.getLocalPort()); 143 144 // client ---- GET ---> server 145 // client <--- 401 ---- server 146 try (Socket s = ss.accept()) { 147 new MessageHeader().parseHeader(s.getInputStream()); 148 s.getOutputStream().write(reply.getBytes("US-ASCII")); 149 } 150 151 // client ---- GET ---> server 152 // client <--- 200 ---- server 153 String auth; 154 try (Socket s = ss.accept()) { 155 MessageHeader mh = new MessageHeader(); 156 mh.parseHeader(s.getInputStream()); 157 s.getOutputStream().write(OKAY.getBytes("US-ASCII")); 158 auth = mh.findValue("Authorization"); 159 } 160 161 // check Authorization header 162 if (auth == null) 163 throw new RuntimeException("Authorization header not found"); 164 System.out.println("Server received Authorization header: " + auth); 165 String[] values = auth.split(" "); 166 if (!values[0].equals(expected)) 167 throw new RuntimeException("Unexpected value"); 168 } 169 } 170 171 /** 172 * Test the http protocol handler with one WWW-Authenticate header with 173 * the value "NTLM". 174 */ 175 static void testNTLM() throws Exception { 176 // server reply 177 String reply = authReplyFor("NTLM"); 178 179 System.out.println("===================================="); 180 System.out.println("Expect client to fail with 401 Unauthorized"); 181 System.out.println(reply); 182 183 try (ServerSocket ss = new ServerSocket(0)) { 184 Client client = new Client(ss.getLocalPort()); 185 Thread thr = new Thread(client); 186 thr.start(); 187 188 // client ---- GET ---> server 189 // client <--- 401 ---- client 190 try (Socket s = ss.accept()) { 191 new MessageHeader().parseHeader(s.getInputStream()); 192 s.getOutputStream().write(reply.getBytes("US-ASCII")); 193 } 194 195 // the client should fail with 401 196 System.out.println("Waiting for client to terminate"); 197 thr.join(); 198 IOException ioe = client.ioException(); 199 if (ioe != null) 200 System.out.println("Client failed: " + ioe); 201 int respCode = client.respCode(); 202 if (respCode != 0 && respCode != -1) 203 System.out.println("Client received HTTP response code: " + respCode); 204 if (respCode != HttpURLConnection.HTTP_UNAUTHORIZED) 205 throw new RuntimeException("Unexpected response code"); 206 } 207 } 208 209 public static void main(String[] args) throws Exception { 210 // assume NTLM is not supported when Kerberos is not available 211 try { 212 Class.forName("javax.security.auth.kerberos.KerberosPrincipal"); 213 System.out.println("Kerberos is present, assuming NTLM is supported too"); 214 return; 215 } catch (ClassNotFoundException okay) { } 216 217 // setup Authenticator 218 Authenticator.setDefault(new Authenticator() { 219 @Override 220 protected PasswordAuthentication getPasswordAuthentication() { 221 return new PasswordAuthentication("user", "pass".toCharArray()); 222 } 223 }); 224 225 // test combinations of authentication schemes 226 test("Basic"); 227 test("Digest"); 228 test("Basic", "Digest"); 229 test("Basic", "NTLM"); 230 test("Digest", "NTLM"); 231 test("Basic", "Digest", "NTLM"); 232 233 // test NTLM only, this should fail with "401 Unauthorized" 234 testNTLM(); 235 236 System.out.println(); 237 System.out.println("TEST PASSED"); 238 } 239 } 240