1 /* 2 * Copyright (c) 2005, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.net.www.protocol.http.ntlm; 27 28 import com.sun.security.ntlm.Client; 29 import com.sun.security.ntlm.NTLMException; 30 import java.io.IOException; 31 import java.net.InetAddress; 32 import java.net.PasswordAuthentication; 33 import java.net.UnknownHostException; 34 import java.net.URL; 35 import java.security.GeneralSecurityException; 36 import java.util.Base64; 37 38 import sun.net.www.HeaderParser; 39 import sun.net.www.protocol.http.AuthenticationInfo; 40 import sun.net.www.protocol.http.AuthScheme; 41 import sun.net.www.protocol.http.HttpURLConnection; 42 43 /** 44 * NTLMAuthentication: 45 * 46 * @author Michael McMahon 47 */ 48 49 /* 50 * NTLM authentication is nominally based on the framework defined in RFC2617, 51 * but differs from the standard (Basic & Digest) schemes as follows: 52 * 53 * 1. A complete authentication requires three request/response transactions 54 * as shown below: 55 * REQ -------------------------------> 56 * <---- 401 (signalling NTLM) -------- 57 * 58 * REQ (with type1 NTLM msg) ---------> 59 * <---- 401 (with type 2 NTLM msg) --- 60 * 61 * REQ (with type3 NTLM msg) ---------> 62 * <---- OK --------------------------- 63 * 64 * 2. The scope of the authentication is the TCP connection (which must be kept-alive) 65 * after the type2 response is received. This means that NTLM does not work end-to-end 66 * through a proxy, rather between client and proxy, or between client and server (with no proxy) 67 */ 68 69 public class NTLMAuthentication extends AuthenticationInfo { 70 private static final long serialVersionUID = 170L; 71 72 private static final NTLMAuthenticationCallback NTLMAuthCallback = 73 NTLMAuthenticationCallback.getNTLMAuthenticationCallback(); 74 75 private String hostname; 76 private static String defaultDomain; /* Domain to use if not specified by user */ 77 78 static { 79 defaultDomain = java.security.AccessController.doPrivileged( 80 new sun.security.action.GetPropertyAction("http.auth.ntlm.domain", "")); 81 }; 82 83 public static boolean supportsTransparentAuth () { 84 return false; 85 } 86 87 /** 88 * Returns true if the given site is trusted, i.e. we can try 89 * transparent Authentication. 90 */ 91 public static boolean isTrustedSite(URL url) { 92 return NTLMAuthCallback.isTrustedSite(url); 93 } 94 95 private void init0() { 96 97 hostname = java.security.AccessController.doPrivileged( 98 new java.security.PrivilegedAction<>() { 99 public String run() { 100 String localhost; 101 try { 102 localhost = InetAddress.getLocalHost().getHostName(); 103 } catch (UnknownHostException e) { 104 localhost = "localhost"; 105 } 106 return localhost; 107 } 108 }); 109 }; 110 111 PasswordAuthentication pw; 112 113 Client client; 114 /** 115 * Create a NTLMAuthentication: 116 * Username may be specified as domain<BACKSLASH>username in the application Authenticator. 117 * If this notation is not used, then the domain will be taken 118 * from a system property: "http.auth.ntlm.domain". 119 */ 120 public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) { 121 super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, 122 AuthScheme.NTLM, 123 url, 124 ""); 125 init (pw); 126 } 127 128 private void init (PasswordAuthentication pw) { 129 String username; 130 String ntdomain; 131 char[] password; 132 this.pw = pw; 133 String s = pw.getUserName(); 134 int i = s.indexOf ('\\'); 135 if (i == -1) { 136 username = s; 137 ntdomain = defaultDomain; 138 } else { 139 ntdomain = s.substring (0, i).toUpperCase(); 140 username = s.substring (i+1); 141 } 142 password = pw.getPassword(); 143 init0(); 144 try { 145 client = new Client(System.getProperty("ntlm.version"), hostname, 146 username, ntdomain, password); 147 } catch (NTLMException ne) { 148 try { 149 client = new Client(null, hostname, username, ntdomain, password); 150 } catch (NTLMException ne2) { 151 // Will never happen 152 throw new AssertionError("Really?"); 153 } 154 } 155 } 156 157 /** 158 * Constructor used for proxy entries 159 */ 160 public NTLMAuthentication(boolean isProxy, String host, int port, 161 PasswordAuthentication pw) { 162 super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, 163 AuthScheme.NTLM, 164 host, 165 port, 166 ""); 167 init (pw); 168 } 169 170 /** 171 * @return true if this authentication supports preemptive authorization 172 */ 173 @Override 174 public boolean supportsPreemptiveAuthorization() { 175 return false; 176 } 177 178 /** 179 * Not supported. Must use the setHeaders() method 180 */ 181 @Override 182 public String getHeaderValue(URL url, String method) { 183 throw new RuntimeException ("getHeaderValue not supported"); 184 } 185 186 /** 187 * Check if the header indicates that the current auth. parameters are stale. 188 * If so, then replace the relevant field with the new value 189 * and return true. Otherwise return false. 190 * returning true means the request can be retried with the same userid/password 191 * returning false means we have to go back to the user to ask for a new 192 * username password. 193 */ 194 @Override 195 public boolean isAuthorizationStale (String header) { 196 return false; /* should not be called for ntlm */ 197 } 198 199 /** 200 * Set header(s) on the given connection. 201 * @param conn The connection to apply the header(s) to 202 * @param p A source of header values for this connection, not used because 203 * HeaderParser converts the fields to lower case, use raw instead 204 * @param raw The raw header field. 205 * @return true if all goes well, false if no headers were set. 206 */ 207 @Override 208 public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { 209 210 try { 211 String response; 212 if (raw.length() < 6) { /* NTLM<sp> */ 213 response = buildType1Msg (); 214 } else { 215 String msg = raw.substring (5); /* skip NTLM<sp> */ 216 response = buildType3Msg (msg); 217 } 218 conn.setAuthenticationProperty(getHeaderName(), response); 219 return true; 220 } catch (IOException e) { 221 return false; 222 } catch (GeneralSecurityException e) { 223 return false; 224 } 225 } 226 227 private String buildType1Msg () { 228 byte[] msg = client.type1(); 229 String result = "NTLM " + Base64.getEncoder().encodeToString(msg); 230 return result; 231 } 232 233 private String buildType3Msg (String challenge) throws GeneralSecurityException, 234 IOException { 235 /* First decode the type2 message to get the server nonce */ 236 /* nonce is located at type2[24] for 8 bytes */ 237 238 byte[] type2 = Base64.getDecoder().decode(challenge); 239 byte[] nonce = new byte[8]; 240 new java.util.Random().nextBytes(nonce); 241 byte[] msg = client.type3(type2, nonce); 242 String result = "NTLM " + Base64.getEncoder().encodeToString(msg); 243 return result; 244 } 245 } 246