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