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