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 {@literal domain<BACKSLASH>username} 117 * in the application Authenticator. 118 * If this notation is not used, then the domain will be taken 119 * from a system property: "http.auth.ntlm.domain". 120 */ 121 public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) { 122 super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, 123 AuthScheme.NTLM, 124 url, 125 ""); 126 init (pw); 127 } 128 129 private void init (PasswordAuthentication pw) { 130 String username; 131 String ntdomain; 132 char[] password; 133 this.pw = pw; 134 String s = pw.getUserName(); 135 int i = s.indexOf ('\\'); 136 if (i == -1) { 137 username = s; 138 ntdomain = defaultDomain; 139 } else { 140 ntdomain = s.substring (0, i).toUpperCase(); 141 username = s.substring (i+1); 142 } 143 password = pw.getPassword(); 144 init0(); 145 try { 146 client = new Client(System.getProperty("ntlm.version"), hostname, 147 username, ntdomain, password); 148 } catch (NTLMException ne) { 149 try { 150 client = new Client(null, hostname, username, ntdomain, password); 151 } catch (NTLMException ne2) { 152 // Will never happen 153 throw new AssertionError("Really?"); 154 } 155 } 156 } 157 158 /** 159 * Constructor used for proxy entries 160 */ 161 public NTLMAuthentication(boolean isProxy, String host, int port, 162 PasswordAuthentication pw) { 163 super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, 164 AuthScheme.NTLM, 165 host, 166 port, 167 ""); 168 init (pw); 169 } 170 171 /** 172 * @return true if this authentication supports preemptive authorization 173 */ 174 @Override 175 public boolean supportsPreemptiveAuthorization() { 176 return false; 177 } 178 179 /** 180 * Not supported. Must use the setHeaders() method 181 */ 182 @Override 183 public String getHeaderValue(URL url, String method) { 184 throw new RuntimeException ("getHeaderValue not supported"); 185 } 186 187 /** 188 * Check if the header indicates that the current auth. parameters are stale. 189 * If so, then replace the relevant field with the new value 190 * and return true. Otherwise return false. 191 * returning true means the request can be retried with the same userid/password 192 * returning false means we have to go back to the user to ask for a new 193 * username password. 194 */ 195 @Override 196 public boolean isAuthorizationStale (String header) { 197 return false; /* should not be called for ntlm */ 198 } 199 200 /** 201 * Set header(s) on the given connection. 202 * @param conn The connection to apply the header(s) to 203 * @param p A source of header values for this connection, not used because 204 * HeaderParser converts the fields to lower case, use raw instead 205 * @param raw The raw header field. 206 * @return true if all goes well, false if no headers were set. 207 */ 208 @Override 209 public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { 210 211 try { 212 String response; 213 if (raw.length() < 6) { /* NTLM<sp> */ 214 response = buildType1Msg (); 215 } else { 216 String msg = raw.substring (5); /* skip NTLM<sp> */ 217 response = buildType3Msg (msg); 218 } 219 conn.setAuthenticationProperty(getHeaderName(), response); 220 return true; 221 } catch (IOException e) { 222 return false; 223 } catch (GeneralSecurityException e) { 224 return false; 225 } 226 } 227 228 private String buildType1Msg () { 229 byte[] msg = client.type1(); 230 String result = "NTLM " + Base64.getEncoder().encodeToString(msg); 231 return result; 232 } 233 234 private String buildType3Msg (String challenge) throws GeneralSecurityException, 235 IOException { 236 /* First decode the type2 message to get the server nonce */ 237 /* nonce is located at type2[24] for 8 bytes */ 238 239 byte[] type2 = Base64.getDecoder().decode(challenge); 240 byte[] nonce = new byte[8]; 241 new java.util.Random().nextBytes(nonce); 242 byte[] msg = client.type3(type2, nonce); 243 String result = "NTLM " + Base64.getEncoder().encodeToString(msg); 244 return result; 245 } 246 } 247