Print this page
Split |
Close |
Expand all |
Collapse all |
--- old/src/solaris/classes/sun/net/www/protocol/http/NTLMAuthentication.java
+++ new/src/solaris/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java
1 1 /*
2 2 * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved.
3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 4 *
5 5 * This code is free software; you can redistribute it and/or modify it
6 6 * under the terms of the GNU General Public License version 2 only, as
7 7 * published by the Free Software Foundation. Sun designates this
8 8 * particular file as subject to the "Classpath" exception as provided
9 9 * by Sun in the LICENSE file that accompanied this code.
10 10 *
11 11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 14 * version 2 for more details (a copy is included in the LICENSE file that
15 15 * accompanied this code).
↓ open down ↓ |
15 lines elided |
↑ open up ↑ |
16 16 *
17 17 * You should have received a copy of the GNU General Public License version
18 18 * 2 along with this work; if not, write to the Free Software Foundation,
19 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 20 *
21 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 23 * have any questions.
24 24 */
25 25
26 -package sun.net.www.protocol.http;
26 +package sun.net.www.protocol.http.ntlm;
27 27
28 28 import java.io.IOException;
29 29 import java.io.UnsupportedEncodingException;
30 30 import java.net.InetAddress;
31 31 import java.net.PasswordAuthentication;
32 32 import java.net.UnknownHostException;
33 33 import java.net.URL;
34 34 import java.security.GeneralSecurityException;
35 35 import java.security.MessageDigest;
36 36 import java.security.NoSuchAlgorithmException;
37 37 import javax.crypto.Cipher;
38 38 import javax.crypto.NoSuchPaddingException;
39 39 import javax.crypto.SecretKey;
40 40 import javax.crypto.SecretKeyFactory;
41 41 import javax.crypto.spec.DESKeySpec;
42 42
43 43 import sun.net.www.HeaderParser;
44 +import sun.net.www.protocol.http.AuthenticationInfo;
45 +import sun.net.www.protocol.http.AuthScheme;
46 +import sun.net.www.protocol.http.HttpURLConnection;
44 47
45 48 /**
46 49 * NTLMAuthentication:
47 50 *
48 51 * @author Michael McMahon
49 52 */
50 53
51 54 /*
52 55 * NTLM authentication is nominally based on the framework defined in RFC2617,
53 56 * but differs from the standard (Basic & Digest) schemes as follows:
54 57 *
55 58 * 1. A complete authentication requires three request/response transactions
56 59 * as shown below:
57 60 * REQ ------------------------------->
58 61 * <---- 401 (signalling NTLM) --------
59 62 *
60 63 * REQ (with type1 NTLM msg) --------->
↓ open down ↓ |
7 lines elided |
↑ open up ↑ |
61 64 * <---- 401 (with type 2 NTLM msg) ---
62 65 *
63 66 * REQ (with type3 NTLM msg) --------->
64 67 * <---- OK ---------------------------
65 68 *
66 69 * 2. The scope of the authentication is the TCP connection (which must be kept-alive)
67 70 * after the type2 response is received. This means that NTLM does not work end-to-end
68 71 * through a proxy, rather between client and proxy, or between client and server (with no proxy)
69 72 */
70 73
71 -class NTLMAuthentication extends AuthenticationInfo {
74 +public class NTLMAuthentication extends AuthenticationInfo {
72 75 private static final long serialVersionUID = -2403849171106437142L;
73 76
74 77 private byte[] type1;
75 78 private byte[] type3;
76 79
77 80 private SecretKeyFactory fac;
78 81 private Cipher cipher;
79 82 private MessageDigest md4;
80 83 private String hostname;
81 84 private static String defaultDomain; /* Domain to use if not specified by user */
82 85
83 86 static {
84 87 defaultDomain = java.security.AccessController.doPrivileged(
85 88 new sun.security.action.GetPropertyAction("http.auth.ntlm.domain",
86 89 "domain"));
87 90 };
88 91
89 - static boolean supportsTransparentAuth () {
92 + public static boolean supportsTransparentAuth () {
90 93 return false;
91 94 }
92 95
93 96 private void init0() {
94 97 type1 = new byte[256];
95 98 type3 = new byte[256];
96 99 System.arraycopy (new byte[] {'N','T','L','M','S','S','P',0,1}, 0, type1, 0, 9);
97 100 type1[12] = (byte) 3;
98 101 type1[13] = (byte) 0xb2;
99 102 type1[28] = (byte) 0x20;
100 103 System.arraycopy (new byte[] {'N','T','L','M','S','S','P',0,3}, 0, type3, 0, 9);
101 104 type3[12] = (byte) 0x18;
102 105 type3[14] = (byte) 0x18;
103 106 type3[20] = (byte) 0x18;
104 107 type3[22] = (byte) 0x18;
105 108 type3[32] = (byte) 0x40;
106 109 type3[60] = (byte) 1;
107 110 type3[61] = (byte) 0x82;
108 111
109 112 try {
110 113 hostname = java.security.AccessController.doPrivileged(
111 114 new java.security.PrivilegedAction<String>() {
112 115 public String run() {
113 116 String localhost;
114 117 try {
115 118 localhost = InetAddress.getLocalHost().getHostName().toUpperCase();
116 119 } catch (UnknownHostException e) {
117 120 localhost = "localhost";
118 121 }
119 122 return localhost;
120 123 }
121 124 });
122 125 int x = hostname.indexOf ('.');
123 126 if (x != -1) {
124 127 hostname = hostname.substring (0, x);
125 128 }
126 129 fac = SecretKeyFactory.getInstance ("DES");
127 130 cipher = Cipher.getInstance ("DES/ECB/NoPadding");
128 131 md4 = sun.security.provider.MD4.getInstance();
129 132 } catch (NoSuchPaddingException e) {
130 133 assert false;
131 134 } catch (NoSuchAlgorithmException e) {
132 135 assert false;
133 136 }
134 137 };
135 138
136 139 PasswordAuthentication pw;
137 140 String username;
138 141 String ntdomain;
139 142 String password;
140 143
141 144 /**
142 145 * Create a NTLMAuthentication:
143 146 * Username may be specified as domain<BACKSLASH>username in the application Authenticator.
144 147 * If this notation is not used, then the domain will be taken
145 148 * from a system property: "http.auth.ntlm.domain".
146 149 */
147 150 public NTLMAuthentication(boolean isProxy, URL url, PasswordAuthentication pw) {
148 151 super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
149 152 AuthScheme.NTLM,
150 153 url,
151 154 "");
152 155 init (pw);
153 156 }
154 157
155 158 private void init (PasswordAuthentication pw) {
156 159 this.pw = pw;
157 160 String s = pw.getUserName();
158 161 int i = s.indexOf ('\\');
159 162 if (i == -1) {
160 163 username = s;
161 164 ntdomain = defaultDomain;
162 165 } else {
163 166 ntdomain = s.substring (0, i).toUpperCase();
164 167 username = s.substring (i+1);
165 168 }
166 169 password = new String (pw.getPassword());
167 170 init0();
168 171 }
169 172
170 173 /**
171 174 * Constructor used for proxy entries
172 175 */
173 176 public NTLMAuthentication(boolean isProxy, String host, int port,
174 177 PasswordAuthentication pw) {
175 178 super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION,
↓ open down ↓ |
76 lines elided |
↑ open up ↑ |
176 179 AuthScheme.NTLM,
177 180 host,
178 181 port,
179 182 "");
180 183 init (pw);
181 184 }
182 185
183 186 /**
184 187 * @return true if this authentication supports preemptive authorization
185 188 */
186 - boolean supportsPreemptiveAuthorization() {
189 + @Override
190 + public boolean supportsPreemptiveAuthorization() {
187 191 return false;
188 192 }
189 193
190 194 /**
191 - * @return the name of the HTTP header this authentication wants set
192 - */
193 - String getHeaderName() {
194 - if (type == SERVER_AUTHENTICATION) {
195 - return "Authorization";
196 - } else {
197 - return "Proxy-authorization";
198 - }
199 - }
200 -
201 - /**
202 195 * Not supported. Must use the setHeaders() method
203 196 */
204 - String getHeaderValue(URL url, String method) {
197 + @Override
198 + public String getHeaderValue(URL url, String method) {
205 199 throw new RuntimeException ("getHeaderValue not supported");
206 200 }
207 201
208 202 /**
209 203 * Check if the header indicates that the current auth. parameters are stale.
210 204 * If so, then replace the relevant field with the new value
211 205 * and return true. Otherwise return false.
212 206 * returning true means the request can be retried with the same userid/password
213 207 * returning false means we have to go back to the user to ask for a new
214 208 * username password.
215 209 */
216 - boolean isAuthorizationStale (String header) {
210 + @Override
211 + public boolean isAuthorizationStale (String header) {
217 212 return false; /* should not be called for ntlm */
218 213 }
219 214
220 215 /**
221 216 * Set header(s) on the given connection.
222 217 * @param conn The connection to apply the header(s) to
223 218 * @param p A source of header values for this connection, not used because
224 219 * HeaderParser converts the fields to lower case, use raw instead
225 220 * @param raw The raw header field.
226 221 * @return true if all goes well, false if no headers were set.
227 222 */
228 - synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
223 + @Override
224 + public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
229 225
230 226 try {
231 227 String response;
232 228 if (raw.length() < 6) { /* NTLM<sp> */
233 229 response = buildType1Msg ();
234 230 } else {
235 231 String msg = raw.substring (5); /* skip NTLM<sp> */
236 232 response = buildType3Msg (msg);
237 233 }
238 234 conn.setAuthenticationProperty(getHeaderName(), response);
239 235 return true;
240 236 } catch (IOException e) {
241 237 return false;
242 238 } catch (GeneralSecurityException e) {
243 239 return false;
244 240 }
245 241 }
246 242
247 243 private void copybytes (byte[] dest, int destpos, String src, String enc) {
248 244 try {
249 245 byte[] x = src.getBytes(enc);
250 246 System.arraycopy (x, 0, dest, destpos, x.length);
251 247 } catch (UnsupportedEncodingException e) {
252 248 assert false;
253 249 }
254 250 }
255 251
256 252 private String buildType1Msg () {
257 253 int dlen = ntdomain.length();
258 254 type1[16]= (byte) (dlen % 256);
259 255 type1[17]= (byte) (dlen / 256);
260 256 type1[18] = type1[16];
261 257 type1[19] = type1[17];
262 258
263 259 int hlen = hostname.length();
264 260 type1[24]= (byte) (hlen % 256);
265 261 type1[25]= (byte) (hlen / 256);
266 262 type1[26] = type1[24];
267 263 type1[27] = type1[25];
268 264
269 265 copybytes (type1, 32, hostname, "ISO8859_1");
270 266 copybytes (type1, hlen+32, ntdomain, "ISO8859_1");
271 267 type1[20] = (byte) ((hlen+32) % 256);
272 268 type1[21] = (byte) ((hlen+32) / 256);
273 269
274 270 byte[] msg = new byte [32 + hlen + dlen];
275 271 System.arraycopy (type1, 0, msg, 0, 32 + hlen + dlen);
276 272 String result = "NTLM " + (new B64Encoder()).encode (msg);
277 273 return result;
278 274 }
279 275
280 276
281 277 /* Convert a 7 byte array to an 8 byte array (for a des key with parity)
282 278 * input starts at offset off
283 279 */
284 280 private byte[] makeDesKey (byte[] input, int off) {
285 281 int[] in = new int [input.length];
286 282 for (int i=0; i<in.length; i++ ) {
287 283 in[i] = input[i]<0 ? input[i]+256: input[i];
288 284 }
289 285 byte[] out = new byte[8];
290 286 out[0] = (byte)in[off+0];
291 287 out[1] = (byte)(((in[off+0] << 7) & 0xFF) | (in[off+1] >> 1));
292 288 out[2] = (byte)(((in[off+1] << 6) & 0xFF) | (in[off+2] >> 2));
293 289 out[3] = (byte)(((in[off+2] << 5) & 0xFF) | (in[off+3] >> 3));
294 290 out[4] = (byte)(((in[off+3] << 4) & 0xFF) | (in[off+4] >> 4));
295 291 out[5] = (byte)(((in[off+4] << 3) & 0xFF) | (in[off+5] >> 5));
296 292 out[6] = (byte)(((in[off+5] << 2) & 0xFF) | (in[off+6] >> 6));
297 293 out[7] = (byte)((in[off+6] << 1) & 0xFF);
298 294 return out;
299 295 }
300 296
301 297 private byte[] calcLMHash () throws GeneralSecurityException {
302 298 byte[] magic = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
303 299 byte[] pwb = password.toUpperCase ().getBytes();
304 300 byte[] pwb1 = new byte [14];
305 301 int len = password.length();
306 302 if (len > 14)
307 303 len = 14;
308 304 System.arraycopy (pwb, 0, pwb1, 0, len); /* Zero padded */
309 305
310 306 DESKeySpec dks1 = new DESKeySpec (makeDesKey (pwb1, 0));
311 307 DESKeySpec dks2 = new DESKeySpec (makeDesKey (pwb1, 7));
312 308
313 309 SecretKey key1 = fac.generateSecret (dks1);
314 310 SecretKey key2 = fac.generateSecret (dks2);
315 311 cipher.init (Cipher.ENCRYPT_MODE, key1);
316 312 byte[] out1 = cipher.doFinal (magic, 0, 8);
317 313 cipher.init (Cipher.ENCRYPT_MODE, key2);
318 314 byte[] out2 = cipher.doFinal (magic, 0, 8);
319 315
320 316 byte[] result = new byte [21];
321 317 System.arraycopy (out1, 0, result, 0, 8);
322 318 System.arraycopy (out2, 0, result, 8, 8);
323 319 return result;
324 320 }
325 321
326 322 private byte[] calcNTHash () throws GeneralSecurityException {
327 323 byte[] pw = null;
328 324 try {
329 325 pw = password.getBytes ("UnicodeLittleUnmarked");
330 326 } catch (UnsupportedEncodingException e) {
331 327 assert false;
332 328 }
333 329 byte[] out = md4.digest (pw);
334 330 byte[] result = new byte [21];
335 331 System.arraycopy (out, 0, result, 0, 16);
336 332 return result;
337 333 }
338 334
339 335 /* key is a 21 byte array. Split it into 3 7 byte chunks,
340 336 * Convert each to 8 byte DES keys, encrypt the text arg with
341 337 * each key and return the three results in a sequential []
342 338 */
343 339 private byte[] calcResponse (byte[] key, byte[] text)
344 340 throws GeneralSecurityException {
345 341 assert key.length == 21;
346 342 DESKeySpec dks1 = new DESKeySpec (makeDesKey (key, 0));
347 343 DESKeySpec dks2 = new DESKeySpec (makeDesKey (key, 7));
348 344 DESKeySpec dks3 = new DESKeySpec (makeDesKey (key, 14));
349 345 SecretKey key1 = fac.generateSecret (dks1);
350 346 SecretKey key2 = fac.generateSecret (dks2);
351 347 SecretKey key3 = fac.generateSecret (dks3);
352 348 cipher.init (Cipher.ENCRYPT_MODE, key1);
353 349 byte[] out1 = cipher.doFinal (text, 0, 8);
354 350 cipher.init (Cipher.ENCRYPT_MODE, key2);
355 351 byte[] out2 = cipher.doFinal (text, 0, 8);
356 352 cipher.init (Cipher.ENCRYPT_MODE, key3);
357 353 byte[] out3 = cipher.doFinal (text, 0, 8);
358 354 byte[] result = new byte [24];
359 355 System.arraycopy (out1, 0, result, 0, 8);
360 356 System.arraycopy (out2, 0, result, 8, 8);
361 357 System.arraycopy (out3, 0, result, 16, 8);
362 358 return result;
363 359 }
364 360
365 361 private String buildType3Msg (String challenge) throws GeneralSecurityException,
366 362 IOException {
367 363 /* First decode the type2 message to get the server nonce */
368 364 /* nonce is located at type2[24] for 8 bytes */
369 365
370 366 byte[] type2 = (new sun.misc.BASE64Decoder()).decodeBuffer (challenge);
371 367 byte[] nonce = new byte [8];
372 368 System.arraycopy (type2, 24, nonce, 0, 8);
373 369
374 370 int ulen = username.length()*2;
375 371 type3[36] = type3[38] = (byte) (ulen % 256);
376 372 type3[37] = type3[39] = (byte) (ulen / 256);
377 373 int dlen = ntdomain.length()*2;
378 374 type3[28] = type3[30] = (byte) (dlen % 256);
379 375 type3[29] = type3[31] = (byte) (dlen / 256);
380 376 int hlen = hostname.length()*2;
381 377 type3[44] = type3[46] = (byte) (hlen % 256);
382 378 type3[45] = type3[47] = (byte) (hlen / 256);
383 379
384 380 int l = 64;
385 381 copybytes (type3, l, ntdomain, "UnicodeLittleUnmarked");
386 382 type3[32] = (byte) (l % 256);
387 383 type3[33] = (byte) (l / 256);
388 384 l += dlen;
389 385 copybytes (type3, l, username, "UnicodeLittleUnmarked");
390 386 type3[40] = (byte) (l % 256);
391 387 type3[41] = (byte) (l / 256);
392 388 l += ulen;
393 389 copybytes (type3, l, hostname, "UnicodeLittleUnmarked");
394 390 type3[48] = (byte) (l % 256);
395 391 type3[49] = (byte) (l / 256);
396 392 l += hlen;
397 393
398 394 byte[] lmhash = calcLMHash();
399 395 byte[] lmresponse = calcResponse (lmhash, nonce);
400 396 byte[] nthash = calcNTHash();
401 397 byte[] ntresponse = calcResponse (nthash, nonce);
402 398 System.arraycopy (lmresponse, 0, type3, l, 24);
403 399 type3[16] = (byte) (l % 256);
404 400 type3[17] = (byte) (l / 256);
405 401 l += 24;
406 402 System.arraycopy (ntresponse, 0, type3, l, 24);
407 403 type3[24] = (byte) (l % 256);
408 404 type3[25] = (byte) (l / 256);
409 405 l += 24;
410 406 type3[56] = (byte) (l % 256);
411 407 type3[57] = (byte) (l / 256);
412 408
413 409 byte[] msg = new byte [l];
414 410 System.arraycopy (type3, 0, msg, 0, l);
415 411 String result = "NTLM " + (new B64Encoder()).encode (msg);
416 412 return result;
417 413 }
418 414
419 415 }
420 416
421 417
422 418 class B64Encoder extends sun.misc.BASE64Encoder {
423 419 /* to force it to to the entire encoding in one line */
424 420 protected int bytesPerLine () {
425 421 return 1024;
426 422 }
427 423 }
↓ open down ↓ |
189 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX