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 /** 120 * Test the http protocol handler with the given authentication schemes 121 * in the WWW-Authenticate header. 122 */ 123 static void test(String... schemes) throws IOException { 124 125 // the authentication scheme that the client is expected to choose 126 String expected = null; 127 for (String s: schemes) { 128 if (expected == null) { 129 expected = s; 130 } else if (s.equals("Digest")) { 131 expected = s; 132 } 133 } 134 135 // server reply 136 String reply = authReplyFor(schemes); 137 138 System.out.println("===================================="); 139 System.out.println("Expect client to choose: " + expected); 140 System.out.println(reply); 141 142 try (ServerSocket ss = new ServerSocket(0)) { 143 Client.start(ss.getLocalPort()); 144 145 // client ---- GET ---> server 146 // client <--- 401 ---- server 147 try (Socket s = ss.accept()) { 148 new MessageHeader().parseHeader(s.getInputStream()); 149 s.getOutputStream().write(reply.getBytes("US-ASCII")); 150 } 151 152 // client ---- GET ---> server 153 // client <--- 200 ---- server 154 String auth; 155 try (Socket s = ss.accept()) { 156 MessageHeader mh = new MessageHeader(); 157 mh.parseHeader(s.getInputStream()); 158 s.getOutputStream().write(OKAY.getBytes("US-ASCII")); 159 auth = mh.findValue("Authorization"); 160 } 161 162 // check Authorization header 163 if (auth == null) 164 throw new RuntimeException("Authorization header not found"); 165 System.out.println("Server received Authorization header: " + auth); 166 String[] values = auth.split(" "); 167 if (!values[0].equals(expected)) 168 throw new RuntimeException("Unexpected value"); 169 } 170 } 171 172 /** 173 * Test the http protocol handler with one WWW-Authenticate header with 174 * the value "NTLM". 175 */ 176 static void testNTLM() throws Exception { 177 // server reply 178 String reply = authReplyFor("NTLM"); 179 180 System.out.println("===================================="); 181 System.out.println("Expect client to fail with 401 Unauthorized"); 182 System.out.println(reply); 183 184 try (ServerSocket ss = new ServerSocket(0)) { 185 Client client = new Client(ss.getLocalPort()); 186 Thread thr = new Thread(client); 187 thr.start(); 188 189 // client ---- GET ---> server 190 // client <--- 401 ---- client 191 try (Socket s = ss.accept()) { 192 new MessageHeader().parseHeader(s.getInputStream()); 193 s.getOutputStream().write(reply.getBytes("US-ASCII")); 194 } 195 196 // the client should fail with 401 197 System.out.println("Waiting for client to terminate"); 198 thr.join(); 199 IOException ioe = client.ioException(); 200 if (ioe != null) 201 System.out.println("Client failed: " + ioe); 202 int respCode = client.respCode(); 203 if (respCode != 0 && respCode != -1) 204 System.out.println("Client received HTTP response code: " + respCode); 205 if (respCode != HttpURLConnection.HTTP_UNAUTHORIZED) 206 throw new RuntimeException("Unexpected response code"); 207 } 208 } 209 210 public static void main(String[] args) throws Exception { 211 // assume NTLM is not supported when Kerberos is not available 212 try { 213 Class.forName("javax.security.auth.kerberos.KerberosPrincipal"); 214 System.out.println("Kerberos is present, assuming NTLM is supported too"); 215 return; 216 } catch (ClassNotFoundException okay) { } 217 218 // setup Authenticator 219 Authenticator.setDefault(new Authenticator() { 220 @Override 221 protected PasswordAuthentication getPasswordAuthentication() { 222 return new PasswordAuthentication("user", "pass".toCharArray()); 223 } 224 }); 225 226 // test combinations of authentication schemes 227 test("Basic"); 228 test("Digest"); 229 test("Basic", "Digest"); 230 test("Basic", "NTLM"); 231 test("Digest", "NTLM"); 232 test("Basic", "Digest", "NTLM"); 233 234 // test NTLM only, this should fail with "401 Unauthorized" 235 testNTLM(); 236 237 System.out.println(); 238 System.out.println("TEST PASSED"); 239 } 240 } 241