001: /*
002: * ====================================================================
003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
004: *
005: * This software is licensed as described in the file COPYING, which
006: * you should have received as part of this distribution. The terms
007: * are also available at http://svnkit.com/license.html
008: * If newer versions of this license are posted there, you may use a
009: * newer version instead, at your option.
010: * ====================================================================
011: */
012: package org.tmatesoft.svn.core.internal.io.dav.http;
013:
014: import java.io.UnsupportedEncodingException;
015: import java.net.InetAddress;
016: import java.net.UnknownHostException;
017: import java.security.InvalidKeyException;
018: import java.security.NoSuchAlgorithmException;
019: import java.util.Iterator;
020: import java.util.Map;
021: import java.util.TreeMap;
022:
023: import javax.crypto.BadPaddingException;
024: import javax.crypto.Cipher;
025: import javax.crypto.IllegalBlockSizeException;
026: import javax.crypto.NoSuchPaddingException;
027: import javax.crypto.spec.SecretKeySpec;
028:
029: import org.tmatesoft.svn.core.SVNErrorCode;
030: import org.tmatesoft.svn.core.SVNErrorMessage;
031: import org.tmatesoft.svn.core.SVNException;
032: import org.tmatesoft.svn.core.internal.util.SVNBase64;
033: import org.tmatesoft.svn.core.internal.util.SVNFormatUtil;
034: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
035:
036: /**
037: * @version 1.1.1
038: * @author TMate Software Ltd.
039: */
040: class HTTPNTLMAuthentication extends HTTPAuthentication {
041:
042: private static final String NTLM_CASE_CONVERTION_PROPERTY = "svnkit.http.ntlm.uppercase";
043: private static final String OLD_NTLM_CASE_CONVERTION_PROPERTY = "javasvn.http.ntlm.uppercase";
044:
045: private static final String DEFAULT_CHARSET = "ASCII";
046: private static final String PROTOCOL_NAME = "NTLMSSP";
047: private static final int LM_RESPONSE_LENGTH = 24;
048: private static final int UNINITIATED = 0;
049: private static final int TYPE1 = 1;
050: private static final int TYPE3 = 3;
051: private static byte[] ourMagicBytes = { (byte) 0x4B, (byte) 0x47,
052: (byte) 0x53, (byte) 0x21, (byte) 0x40, (byte) 0x23,
053: (byte) 0x24, (byte) 0x25 };
054:
055: private static final long NEGOTIATE_UNICODE = 0x00000001L;
056: private static final long NEGOTIATE_OEM = 0x00000002L;
057: private static final long REQUEST_TARGET = 0x00000004L;
058: private static final long NEGOTIATE_SIGN = 0x00000010L;
059: private static final long NEGOTIATE_SEAL = 0x00000020L;
060: private static final long NEGOTIATE_DATAGRAM_STYLE = 0x00000040L;
061: private static final long NEGOTIATE_LAN_MANAGER_KEY = 0x00000080L;
062: private static final long NEGOTIATE_NETWARE = 0x00000100L;
063: private static final long NEGOTIATE_NTLM = 0x00000200L;
064: private static final long NEGOTIATE_DOMAIN_SUPPLIED = 0x00001000L;
065: private static final long NEGOTIATE_WORKSTATION_SUPPLIED = 0x00002000L;
066: private static final long NEGOTIATE_LOCAL_CALL = 0x00004000L;
067: private static final long NEGOTIATE_ALWAYS_SIGN = 0x00008000L;
068: private static final long TARGET_TYPE_DOMAIN = 0x00010000L;
069: private static final long TARGET_TYPE_SERVER = 0x00020000L;
070: private static final long TARGET_TYPE_SHARE = 0x00040000L;
071: private static final long NEGOTIATE_NTLM2_KEY = 0x00080000L;
072: private static final long REQUEST_INIT_RESPONSE = 0x00100000L;
073: private static final long REQUEST_ACCEPT_RESPONSE = 0x00200000L;
074: private static final long REQUEST_NON_NT_SESSION_KEY = 0x00400000L;
075: private static final long NEGOTIATE_TARGET_INFO = 0x00800000L;
076: private static final long NEGOTIATE_128 = 0x20000000L;
077: private static final long NEGOTIATE_KEY_EXCHANGE = 0x40000000L;
078: private static final long NEGOTIATE_56 = 0x80000000L;
079:
080: private static Map ourFlags = new TreeMap();
081: static {
082: ourFlags.put(new Long(NEGOTIATE_UNICODE),
083: "0x00000001 (Negotiate Unicode)");
084: ourFlags.put(new Long(NEGOTIATE_OEM),
085: "0x00000002 (Negotiate OEM)");
086: ourFlags.put(new Long(REQUEST_TARGET),
087: "0x00000004 (Request Target)");
088: ourFlags.put(new Long(0x00000008L), "0x00000008 (Unknown)");
089: ourFlags.put(new Long(NEGOTIATE_SIGN),
090: "0x00000010 (Negotiate Sign)");
091: ourFlags.put(new Long(NEGOTIATE_SEAL),
092: "0x00000020 (Negotiate Seal)");
093: ourFlags.put(new Long(NEGOTIATE_DATAGRAM_STYLE),
094: "0x00000040 (Negotiate Datagram Style)");
095: ourFlags.put(new Long(NEGOTIATE_LAN_MANAGER_KEY),
096: "0x00000080 (Negotiate Lan Manager Key)");
097: ourFlags.put(new Long(NEGOTIATE_NETWARE),
098: "0x00000100 (Negotiate Netware)");
099: ourFlags.put(new Long(NEGOTIATE_NTLM),
100: "0x00000200 (Negotiate NTLM)");
101: ourFlags.put(new Long(0x00000400L), "0x00000400 (Unknown)");
102: ourFlags.put(new Long(0x00000800L), "0x00000800 (Unknown)");
103: ourFlags.put(new Long(NEGOTIATE_DOMAIN_SUPPLIED),
104: "0x00001000 (Negotiate Domain Supplied)");
105: ourFlags.put(new Long(NEGOTIATE_WORKSTATION_SUPPLIED),
106: "0x00002000 (Negotiate Workstation Supplied)");
107: ourFlags.put(new Long(NEGOTIATE_LOCAL_CALL),
108: "0x00004000 (Negotiate Local Call)");
109: ourFlags.put(new Long(NEGOTIATE_ALWAYS_SIGN),
110: "0x00008000 (Negotiate Always Sign)");
111: ourFlags.put(new Long(TARGET_TYPE_DOMAIN),
112: "0x00010000 (Target Type Domain)");
113: ourFlags.put(new Long(TARGET_TYPE_SERVER),
114: "0x00020000 (Target Type Server)");
115: ourFlags.put(new Long(TARGET_TYPE_SHARE),
116: "0x00040000 (Target Type Share)");
117: ourFlags.put(new Long(NEGOTIATE_NTLM2_KEY),
118: "0x00080000 (Negotiate NTLM2 Key)");
119: ourFlags.put(new Long(REQUEST_INIT_RESPONSE),
120: "0x00100000 (Request Init Response)");
121: ourFlags.put(new Long(REQUEST_ACCEPT_RESPONSE),
122: "0x00200000 (Request Accept Response)");
123: ourFlags.put(new Long(REQUEST_NON_NT_SESSION_KEY),
124: "0x00400000 (Request Non-NT Session Key)");
125: ourFlags.put(new Long(NEGOTIATE_TARGET_INFO),
126: "0x00800000 (Negotiate Target Info)");
127: ourFlags.put(new Long(0x01000000L), "0x01000000 (Unknown)");
128: ourFlags.put(new Long(0x02000000L), "0x02000000 (Unknown)");
129: ourFlags.put(new Long(0x04000000L), "0x04000000 (Unknown)");
130: ourFlags.put(new Long(0x08000000L), "0x08000000 (Unknown)");
131: ourFlags.put(new Long(0x10000000L), "0x10000000 (Unknown)");
132: ourFlags.put(new Long(NEGOTIATE_128),
133: "0x20000000 (Negotiate 128)");
134: ourFlags.put(new Long(NEGOTIATE_KEY_EXCHANGE),
135: "0x40000000 (Negotiate Key Exchange)");
136: ourFlags.put(new Long(NEGOTIATE_56),
137: "0x80000000 (Negotiate 56)");
138: }
139: private static Map ourTargetInfoTypes = new TreeMap();
140: static {
141: ourTargetInfoTypes.put(new Integer(1), "Server Name");
142: ourTargetInfoTypes.put(new Integer(2), "Domain Name");
143: ourTargetInfoTypes.put(new Integer(3), "DNS Host Name");
144: ourTargetInfoTypes.put(new Integer(4), "DNS Domain Name");
145: }
146:
147: private int myState;
148: private byte[] myResponse;
149: private int myPosition;
150: private byte[] myNonce;
151: private boolean myIsNegotiateLocalCall;
152: private String myCharset;
153:
154: protected HTTPNTLMAuthentication(String charset) {
155: myState = UNINITIATED;
156: myIsNegotiateLocalCall = false;
157: myCharset = charset;
158: }
159:
160: public void setType1State() {
161: myState = TYPE1;
162: }
163:
164: public void setType3State() {
165: myState = TYPE3;
166: }
167:
168: public boolean isInType3State() {
169: return myState == TYPE3;
170: }
171:
172: private void initResponse(int bufferSize) {
173: myResponse = new byte[bufferSize];
174: myPosition = 0;
175: }
176:
177: private void addByte(byte b) {
178: myResponse[myPosition++] = b;
179: }
180:
181: private void addBytes(byte[] bytes) {
182: for (int i = 0; i < bytes.length; i++) {
183: myResponse[myPosition++] = bytes[i];
184: }
185: }
186:
187: private byte[] convertToShortValue(int num) {
188: byte[] val = new byte[2];
189: val[0] = (byte) (num & 0xff);
190: val[1] = (byte) ((num >> 8) & 0xff);
191: return val;
192: }
193:
194: private String getResponse() {
195: byte[] response;
196: if (myResponse.length > myPosition) {
197: response = new byte[myPosition];
198: for (int i = 0; i < myPosition; i++) {
199: response[i] = myResponse[i];
200: }
201: } else {
202: response = myResponse;
203: }
204:
205: return SVNBase64.byteArrayToBase64(response);
206: }
207:
208: public void parseChallenge(String challenge) throws SVNException {
209: if (challenge == null) {
210: SVNErrorMessage err = SVNErrorMessage.create(
211: SVNErrorCode.RA_DAV_REQUEST_FAILED,
212: "NTLM HTTP auth: expected challenge");
213: SVNErrorManager.error(err);
214: }
215: byte[] challengeBase64Bytes = HTTPAuthentication.getBytes(
216: challenge, DEFAULT_CHARSET);
217: byte[] resultBuffer = new byte[challengeBase64Bytes.length];
218: int resultLength = 0;
219: try {
220: resultLength = SVNBase64.base64ToByteArray(
221: new StringBuffer(new String(challengeBase64Bytes,
222: myCharset)), resultBuffer);
223: } catch (UnsupportedEncodingException e) {
224: SVNErrorMessage err = SVNErrorMessage.create(
225: SVNErrorCode.RA_DAV_REQUEST_FAILED,
226: "NTLM HTTP auth: " + e.getMessage());
227: SVNErrorManager.error(err);
228: }
229:
230: String proto = new String(resultBuffer, 0, 7);
231: byte[] typeBytes = new byte[4];
232: for (int i = 0; i < 4; i++) {
233: typeBytes[i] = resultBuffer[8 + i];
234: }
235: long type = toLong(typeBytes);
236:
237: if (!PROTOCOL_NAME.equalsIgnoreCase(proto)) {
238: SVNErrorMessage err = SVNErrorMessage.create(
239: SVNErrorCode.RA_DAV_REQUEST_FAILED,
240: "NTLM HTTP auth: incorrect signature ''(0}''",
241: proto);
242: SVNErrorManager.error(err);
243: } else if (type != 2) {
244: SVNErrorMessage err = SVNErrorMessage
245: .create(
246: SVNErrorCode.RA_DAV_REQUEST_FAILED,
247: "NTLM HTTP auth: expected type 2 message instead of ''(0, number, integer}''",
248: new Long(type));
249: SVNErrorManager.error(err);
250: }
251:
252: myNonce = new byte[8];
253: for (int i = 0; i < 8; i++) {
254: myNonce[i] = resultBuffer[i + 24];
255: }
256:
257: byte[] flagBytes = new byte[4];
258: for (int i = 0; i < 4; i++) {
259: flagBytes[i] = resultBuffer[i + 20];
260: }
261: long flags = toLong(flagBytes);
262:
263: StringBuffer log = new StringBuffer();
264: String base64DecodedMessage = new String(resultBuffer, 0,
265: resultLength);
266: log.append("NTLM auth message: " + base64DecodedMessage);
267: log.append('\n');
268: log.append("Length: " + base64DecodedMessage.length());
269: log.append('\n');
270: log.append("Signature: " + proto);
271: log.append('\n');
272: log.append("Type: " + type);
273: log.append('\n');
274: log.append("Flags: " + Long.toString(flags, 16));
275: log.append('\n');
276: for (Iterator flagsIter = ourFlags.keySet().iterator(); flagsIter
277: .hasNext();) {
278: Long curFlag = (Long) flagsIter.next();
279: if ((flags & curFlag.longValue()) != 0) {
280: log.append(ourFlags.get(curFlag));
281: log.append('\n');
282: }
283: }
284:
285: byte[] targetNameLengthBytes = new byte[2];
286: for (int i = 0; i < 2; i++) {
287: targetNameLengthBytes[i] = resultBuffer[12 + i];
288: }
289: int targetNameLength = toInt(targetNameLengthBytes);
290:
291: byte[] targetNameAllocatedBytes = new byte[2];
292: for (int i = 0; i < 2; i++) {
293: targetNameAllocatedBytes[i] = resultBuffer[14 + i];
294: }
295: int targetNameAllocated = toInt(targetNameAllocatedBytes);
296:
297: byte[] targetNameOffsetBytes = new byte[4];
298: for (int i = 0; i < 4; i++) {
299: targetNameOffsetBytes[i] = resultBuffer[16 + i];
300: }
301: long targetNameOffset = toLong(targetNameOffsetBytes);
302:
303: if (targetNameLength > 0) {
304: String targetName = new String(resultBuffer,
305: (int) targetNameOffset, targetNameAllocated);
306: log.append("Target Name: " + targetName);
307: log.append('\n');
308: }
309: log.append("Challenge: ");
310: for (int i = 0; i < myNonce.length; i++) {
311: log.append(SVNFormatUtil.getHexNumberFromByte(myNonce[i]));
312: }
313: log.append('\n');
314:
315: //check for local call
316: long contextH = -1;
317: long contextL = -1;
318: boolean containsContext = false;
319: if (targetNameOffset != 32 && resultLength >= 40) {
320: byte[] contextHBytes = new byte[4];
321: byte[] contextLBytes = new byte[4];
322: int i = 0;
323: for (i = 0; i < 4; i++) {
324: contextHBytes[i] = resultBuffer[i + 32];
325: }
326: for (; i < 8; i++) {
327: contextLBytes[i - 4] = resultBuffer[i + 32];
328: }
329:
330: contextH = toLong(contextHBytes);
331: contextL = toLong(contextLBytes);
332: if (contextL == 0) {
333: containsContext = true;
334: log.append("Context: ");
335: log.append(Long.toString(contextH, 16) + " "
336: + Long.toString(contextL, 16));
337: log.append('\n');
338: }
339:
340: if (contextH != 0 && (flags & NEGOTIATE_LOCAL_CALL) != 0) {
341: myIsNegotiateLocalCall = true;
342: } else {
343: myIsNegotiateLocalCall = false;
344: }
345: } else {
346: myIsNegotiateLocalCall = false;
347: }
348:
349: if ((flags & NEGOTIATE_TARGET_INFO) != 0) {
350: int tgtInfoSecurityBufferOffset = containsContext ? 40 : 32;
351:
352: byte[] targetInfoLengthBytes = new byte[2];
353: for (int i = 0; i < 2; i++) {
354: targetInfoLengthBytes[i] = resultBuffer[tgtInfoSecurityBufferOffset
355: + i];
356: }
357: int targetInfoLength = toInt(targetInfoLengthBytes);
358:
359: byte[] targetInfoAllocatedBytes = new byte[2];
360: for (int i = 0; i < 2; i++) {
361: targetInfoAllocatedBytes[i] = resultBuffer[tgtInfoSecurityBufferOffset
362: + 2 + i];
363: }
364: int targetInfoAllocated = toInt(targetInfoAllocatedBytes);
365:
366: byte[] targetInfoOffsetBytes = new byte[4];
367: for (int i = 0; i < 4; i++) {
368: targetInfoOffsetBytes[i] = resultBuffer[tgtInfoSecurityBufferOffset
369: + 4 + i];
370: }
371: long targetInfoOffset = toLong(targetInfoOffsetBytes);
372:
373: byte[] targetInfoTypeBytes = new byte[2];
374: byte[] subblockLengthBytes = new byte[2];
375: int read = 0;
376: while (targetInfoLength > 0 && read <= targetInfoAllocated) {
377: for (int i = 0; i < 2; i++) {
378: targetInfoTypeBytes[i] = resultBuffer[(int) targetInfoOffset
379: + i];
380: }
381: read += 2;
382: targetInfoOffset += 2;
383: int targetInfoType = toInt(targetInfoTypeBytes);
384: if (targetInfoType == 0) {
385: break;
386: }
387:
388: for (int i = 0; i < 2; i++) {
389: subblockLengthBytes[i] = resultBuffer[(int) targetInfoOffset
390: + i];
391: }
392: read += 2;
393: targetInfoOffset += 2;
394: int subblockLength = toInt(subblockLengthBytes);
395:
396: String typeDescription = (String) ourTargetInfoTypes
397: .get(new Integer(targetInfoType));
398: if (typeDescription != null) {
399: String info = new String(resultBuffer,
400: (int) targetInfoOffset, subblockLength);
401: log.append(typeDescription + ": " + info);
402: log.append('\n');
403: }
404: read += subblockLength;
405: targetInfoOffset += subblockLength;
406: }
407: }
408: log.append('\n');
409: }
410:
411: private static int toInt(byte[] num) {
412: int l = 0;
413: for (int i = 0; i < 2; i++) {
414: int b = num[i] & 0xff;
415: b = b << i * 8;
416: l |= b;
417: }
418: return l;
419: }
420:
421: private long toLong(byte[] num) {
422: long l = 0;
423: for (int i = 0; i < 4; i++) {
424: long b = num[i] & 0xff;
425: b = b << i * 8;
426: l |= b;
427: }
428: return l;
429: }
430:
431: public String authenticate() throws SVNException {
432: if (myState != TYPE1 && myState != TYPE3) {
433: SVNErrorMessage err = SVNErrorMessage
434: .create(SVNErrorCode.RA_DAV_REQUEST_FAILED,
435: "Unsupported message type in HTTP NTLM authentication");
436: SVNErrorManager.error(err);
437: }
438:
439: String login = getUserName();
440: String domain = null;
441: String username = null;
442:
443: int slashInd = login != null ? login.indexOf('\\') : -1;
444: if (slashInd != -1) {
445: domain = login.substring(0, slashInd);
446: int lastInd = slashInd + 1;
447: while (lastInd < login.length()
448: && login.charAt(lastInd) == '\\') {
449: lastInd++;
450: }
451: username = login.substring(lastInd);
452: } else {
453: domain = "";
454: username = login;
455: }
456:
457: String hostName = null;
458: try {
459: InetAddress localhost = InetAddress.getLocalHost();
460: hostName = localhost.getHostName();
461: } catch (UnknownHostException uhe) {
462: hostName = "";
463: }
464:
465: if (isUpperCase()) {
466: domain = domain.toUpperCase();
467: hostName = hostName.toUpperCase();
468: }
469:
470: byte[] protocol = HTTPAuthentication.getBytes(PROTOCOL_NAME,
471: DEFAULT_CHARSET);
472: byte[] domainBytes = HTTPAuthentication.getBytes(domain,
473: DEFAULT_CHARSET);
474: byte[] hostNameBytes = HTTPAuthentication.getBytes(hostName,
475: DEFAULT_CHARSET);
476: byte[] domLen = convertToShortValue(domainBytes.length);
477: byte[] hostLen = convertToShortValue(hostNameBytes.length);
478: StringBuffer sublog = new StringBuffer();
479: sublog.append("Signature: " + PROTOCOL_NAME);
480: sublog.append('\n');
481:
482: long flags = NEGOTIATE_OEM | REQUEST_TARGET | NEGOTIATE_NTLM
483: | NEGOTIATE_LOCAL_CALL;
484: if (domain.length() > 0) {
485: flags |= NEGOTIATE_DOMAIN_SUPPLIED;
486: }
487:
488: if (myState == TYPE1) {
489: int responseLength = 32 + domainBytes.length
490: + hostNameBytes.length;
491:
492: initResponse(responseLength);
493:
494: //NTLMSSP\0 signature (8 bytes long)
495: addBytes(protocol);
496: addByte((byte) 0);
497:
498: // Type1 - Negotiate (4 bytes long)
499: addByte((byte) 1);
500: addByte((byte) 0);
501: addByte((byte) 0);
502: addByte((byte) 0);
503: sublog.append("Type: " + 1);
504: sublog.append('\n');
505:
506: // Flags (4 bytes long): 'Negotiate OEM', 'Request Target',
507: // 'Negotiate NTLM', 'Negotiate Always Sign'
508: addByte((byte) (flags & 0xff));
509: addByte((byte) ((flags >> 8) & 0xff));
510: addByte((byte) ((flags >> 16) & 0xff));
511: addByte((byte) ((flags >> 24) & 0xff));
512: sublog.append("Flags: " + Long.toString(flags, 16));
513: sublog.append('\n');
514: for (Iterator flagsIter = ourFlags.keySet().iterator(); flagsIter
515: .hasNext();) {
516: Long curFlag = (Long) flagsIter.next();
517: if ((flags & curFlag.longValue()) != 0) {
518: sublog.append(ourFlags.get(curFlag));
519: sublog.append('\n');
520: }
521: }
522:
523: // Domain name length (2 bytes short)
524: addBytes(domLen);
525: // Allocated space for the domain name (2 bytes short)
526: addBytes(domLen);
527:
528: // Domain name offset (4 bytes long)
529: byte[] domainOffset = convertToShortValue(hostNameBytes.length + 32);
530: addBytes(domainOffset);
531: addByte((byte) 0);
532: addByte((byte) 0);
533:
534: // Host name length (2 bytes short).
535: addBytes(hostLen);
536: // Allocated space for the host name (2 bytes short)
537: addBytes(hostLen);
538:
539: // Host name offset (always 32, 4 bytes long).
540: byte[] hostOffset = convertToShortValue(32);
541: addBytes(hostOffset);
542: addByte((byte) 0);
543: addByte((byte) 0);
544:
545: // Host name
546: addBytes(hostNameBytes);
547: if (hostName.length() > 0) {
548: sublog.append("Host Name: " + hostName);
549: sublog.append('\n');
550: }
551:
552: // Domain name
553: addBytes(domainBytes);
554: if (domain.length() > 0) {
555: sublog.append("Domain: " + domain);
556: sublog.append('\n');
557: }
558: } else if (myState == TYPE3) {
559: byte[] userBytes = username.getBytes();
560: sublog.append("Type: " + 3);
561: sublog.append('\n');
562: sublog.append("Flags: " + Long.toString(flags, 16));
563: sublog.append('\n');
564: for (Iterator flagsIter = ourFlags.keySet().iterator(); flagsIter
565: .hasNext();) {
566: Long curFlag = (Long) flagsIter.next();
567: if ((flags & curFlag.longValue()) != 0) {
568: sublog.append(ourFlags.get(curFlag));
569: sublog.append('\n');
570: }
571: }
572:
573: if (!myIsNegotiateLocalCall) {
574: int responseLength = 64 + LM_RESPONSE_LENGTH
575: + domainBytes.length + hostNameBytes.length
576: + userBytes.length;
577:
578: initResponse(responseLength);
579:
580: addBytes(protocol);
581: addByte((byte) 0);
582:
583: //Type3
584: addByte((byte) 3);
585: addByte((byte) 0);
586: addByte((byte) 0);
587: addByte((byte) 0);
588:
589: byte[] lmResponseLength = convertToShortValue(24);
590: // LM Response Length
591: addBytes(lmResponseLength);
592: // LM Response allocated space
593: addBytes(lmResponseLength);
594:
595: // LM Response Offset
596: addBytes(convertToShortValue(responseLength - 24));
597: addByte((byte) 0);
598: addByte((byte) 0);
599:
600: byte[] ntlmResponseLength = convertToShortValue(0);
601: // NTLM Response Length
602: addBytes(ntlmResponseLength);
603: // NTLM Response allocated space
604: addBytes(ntlmResponseLength);
605:
606: byte[] responseLengthShortBytes = convertToShortValue(responseLength);
607: // NTLM Response Offset
608: addBytes(responseLengthShortBytes);
609: addByte((byte) 0);
610: addByte((byte) 0);
611:
612: // Domain length
613: addBytes(domLen);
614: // Domain allocated space
615: addBytes(domLen);
616:
617: // Domain Offset
618: addBytes(convertToShortValue(64));
619: addByte((byte) 0);
620: addByte((byte) 0);
621:
622: byte[] usernameLength = convertToShortValue(userBytes.length);
623: // Username Length
624: addBytes(usernameLength);
625: // Username allocated space
626: addBytes(usernameLength);
627:
628: // User offset
629: addBytes(convertToShortValue(64 + domainBytes.length));
630: addByte((byte) 0);
631: addByte((byte) 0);
632:
633: // Host name length
634: addBytes(hostLen);
635: // Host name allocated space
636: addBytes(hostLen);
637:
638: // Host offset
639: addBytes(convertToShortValue(64 + domainBytes.length
640: + userBytes.length));
641:
642: for (int i = 0; i < 6; i++) {
643: addByte((byte) 0);
644: }
645:
646: // Message length
647: addBytes(responseLengthShortBytes);
648: addByte((byte) 0);
649: addByte((byte) 0);
650:
651: // Flags
652: addByte((byte) (flags & 0xff));
653: addByte((byte) ((flags >> 8) & 0xff));
654: addByte((byte) ((flags >> 16) & 0xff));
655: addByte((byte) ((flags >> 24) & 0xff));
656:
657: addBytes(domainBytes);
658: if (domain.length() > 0) {
659: sublog.append("Domain: " + domain);
660: sublog.append('\n');
661: }
662:
663: addBytes(userBytes);
664: if (username.length() > 0) {
665: sublog.append("User Name: " + username);
666: sublog.append('\n');
667: }
668:
669: addBytes(hostNameBytes);
670: if (hostName.length() > 0) {
671: sublog.append("Host Name: " + hostName);
672: sublog.append('\n');
673: }
674:
675: String password = getPassword();
676: byte[] hash = hashPassword(password != null ? password
677: : "");
678: addBytes(hash);
679:
680: sublog.append("Hash: " + new String(hash));
681: sublog.append('\n');
682:
683: } else {
684: int responseLength = 64;
685: byte[] responseLengthShortBytes = convertToShortValue(responseLength);
686: initResponse(responseLength);
687:
688: addBytes(protocol);
689: addByte((byte) 0);
690:
691: //Type3
692: addByte((byte) 3);
693: addByte((byte) 0);
694: addByte((byte) 0);
695: addByte((byte) 0);
696:
697: // LM Response Length
698: addByte((byte) 0);
699: addByte((byte) 0);
700: // LM Response allocated space
701: addByte((byte) 0);
702: addByte((byte) 0);
703:
704: // LM Response Offset
705: addBytes(responseLengthShortBytes);
706: addByte((byte) 0);
707: addByte((byte) 0);
708:
709: // NTLM Response Length
710: addByte((byte) 0);
711: addByte((byte) 0);
712: // NTLM Response allocated space
713: addByte((byte) 0);
714: addByte((byte) 0);
715: // NTLM Response Offset
716: addBytes(responseLengthShortBytes);
717: addByte((byte) 0);
718: addByte((byte) 0);
719:
720: // Domain length
721: addByte((byte) 0);
722: addByte((byte) 0);
723: // Domain allocated space
724: addByte((byte) 0);
725: addByte((byte) 0);
726: // Domain Offset
727: addBytes(responseLengthShortBytes);
728: addByte((byte) 0);
729: addByte((byte) 0);
730:
731: // Username Length
732: addByte((byte) 0);
733: addByte((byte) 0);
734: // Username allocated space
735: addByte((byte) 0);
736: addByte((byte) 0);
737: // User offset
738: addBytes(responseLengthShortBytes);
739: addByte((byte) 0);
740: addByte((byte) 0);
741:
742: // Host name length
743: addByte((byte) 0);
744: addByte((byte) 0);
745: // Host name allocated space
746: addByte((byte) 0);
747: addByte((byte) 0);
748: // Host offset
749: addBytes(responseLengthShortBytes);
750:
751: for (int i = 0; i < 6; i++) {
752: addByte((byte) 0);
753: }
754:
755: // Message length
756: addBytes(responseLengthShortBytes);
757: addByte((byte) 0);
758: addByte((byte) 0);
759:
760: // Flags
761: addByte((byte) (flags & 0xff));
762: addByte((byte) ((flags >> 8) & 0xff));
763: addByte((byte) ((flags >> 16) & 0xff));
764: addByte((byte) ((flags >> 24) & 0xff));
765: }
766: setType1State();
767: }
768:
769: StringBuffer log = new StringBuffer();
770: String message = new String(myResponse, 0, myPosition);
771: log.append("NTLM auth message: " + message);
772: log.append('\n');
773: log.append("Length: " + message.length());
774: log.append('\n');
775: log.append(sublog);
776:
777: return "NTLM " + getResponse();
778: }
779:
780: private boolean isUpperCase() {
781: String upperCase = System.getProperty(
782: NTLM_CASE_CONVERTION_PROPERTY, System.getProperty(
783: OLD_NTLM_CASE_CONVERTION_PROPERTY, "true"));
784: return Boolean.valueOf(upperCase).booleanValue();
785: }
786:
787: private byte[] hashPassword(String password) throws SVNException {
788: byte[] passw = isUpperCase() ? password.toUpperCase()
789: .getBytes() : password.getBytes();
790: byte[] lmPw1 = new byte[7];
791: byte[] lmPw2 = new byte[7];
792:
793: int len = passw.length;
794: if (len > 7) {
795: len = 7;
796: }
797:
798: int idx;
799: for (idx = 0; idx < len; idx++) {
800: lmPw1[idx] = passw[idx];
801: }
802:
803: for (; idx < 7; idx++) {
804: lmPw1[idx] = (byte) 0;
805: }
806:
807: len = passw.length;
808: if (len > 14) {
809: len = 14;
810: }
811: for (idx = 7; idx < len; idx++) {
812: lmPw2[idx - 7] = passw[idx];
813: }
814: for (; idx < 14; idx++) {
815: lmPw2[idx - 7] = (byte) 0;
816: }
817:
818: byte[] lmHpw1;
819: lmHpw1 = encrypt(lmPw1, ourMagicBytes);
820:
821: byte[] lmHpw2 = encrypt(lmPw2, ourMagicBytes);
822:
823: byte[] lmHpw = new byte[21];
824: for (int i = 0; i < lmHpw1.length; i++) {
825: lmHpw[i] = lmHpw1[i];
826: }
827: for (int i = 0; i < lmHpw2.length; i++) {
828: lmHpw[i + 8] = lmHpw2[i];
829: }
830: for (int i = 0; i < 5; i++) {
831: lmHpw[i + 16] = (byte) 0;
832: }
833:
834: // Create the responses.
835: byte[] lmResp = new byte[24];
836: calcResp(lmHpw, lmResp);
837:
838: return lmResp;
839: }
840:
841: private void calcResp(byte[] keys, byte[] results)
842: throws SVNException {
843: byte[] keys1 = new byte[7];
844: byte[] keys2 = new byte[7];
845: byte[] keys3 = new byte[7];
846:
847: for (int i = 0; i < 7; i++) {
848: keys1[i] = keys[i];
849: }
850:
851: for (int i = 0; i < 7; i++) {
852: keys2[i] = keys[i + 7];
853: }
854:
855: for (int i = 0; i < 7; i++) {
856: keys3[i] = keys[i + 14];
857: }
858:
859: byte[] results1 = encrypt(keys1, myNonce);
860:
861: byte[] results2 = encrypt(keys2, myNonce);
862:
863: byte[] results3 = encrypt(keys3, myNonce);
864:
865: for (int i = 0; i < 8; i++) {
866: results[i] = results1[i];
867: }
868:
869: for (int i = 0; i < 8; i++) {
870: results[i + 8] = results2[i];
871: }
872:
873: for (int i = 0; i < 8; i++) {
874: results[i + 16] = results3[i];
875: }
876: }
877:
878: private byte[] encrypt(byte[] key, byte[] bytes)
879: throws SVNException {
880: Cipher ecipher = getCipher(key);
881: try {
882: byte[] enc = ecipher.doFinal(bytes);
883: return enc;
884: } catch (IllegalBlockSizeException e) {
885: SVNErrorMessage err = SVNErrorMessage.create(
886: SVNErrorCode.IO_ERROR,
887: "Invalid block size for DES encryption: {0}", e
888: .getLocalizedMessage());
889: SVNErrorManager.error(err);
890: } catch (BadPaddingException e) {
891: SVNErrorMessage err = SVNErrorMessage
892: .create(
893: SVNErrorCode.IO_ERROR,
894: "Data not padded correctly for DES encryption: {0}",
895: e.getLocalizedMessage());
896: SVNErrorManager.error(err);
897: }
898: return null;
899: }
900:
901: private Cipher getCipher(byte[] key) throws SVNException {
902: try {
903: final Cipher ecipher = Cipher
904: .getInstance("DES/ECB/NoPadding");
905: key = setupKey(key);
906: ecipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key,
907: "DES"));
908: return ecipher;
909: } catch (NoSuchAlgorithmException e) {
910: SVNErrorMessage err = SVNErrorMessage.create(
911: SVNErrorCode.IO_ERROR,
912: "DES encryption is not available: {0}", e
913: .getLocalizedMessage());
914: SVNErrorManager.error(err);
915: } catch (InvalidKeyException e) {
916: SVNErrorMessage err = SVNErrorMessage.create(
917: SVNErrorCode.IO_ERROR,
918: "Invalid key for DES encryption: {0}", e
919: .getLocalizedMessage());
920: SVNErrorManager.error(err);
921: } catch (NoSuchPaddingException e) {
922: SVNErrorMessage err = SVNErrorMessage.create(
923: SVNErrorCode.IO_ERROR,
924: "NoPadding option for DES is not available: {0}", e
925: .getLocalizedMessage());
926: SVNErrorManager.error(err);
927: }
928: return null;
929: }
930:
931: private byte[] setupKey(byte[] key56) {
932: byte[] key = new byte[8];
933: key[0] = (byte) ((key56[0] >> 1) & 0xff);
934: key[1] = (byte) ((((key56[0] & 0x01) << 6) | (((key56[1] & 0xff) >> 2) & 0xff)) & 0xff);
935: key[2] = (byte) ((((key56[1] & 0x03) << 5) | (((key56[2] & 0xff) >> 3) & 0xff)) & 0xff);
936: key[3] = (byte) ((((key56[2] & 0x07) << 4) | (((key56[3] & 0xff) >> 4) & 0xff)) & 0xff);
937: key[4] = (byte) ((((key56[3] & 0x0f) << 3) | (((key56[4] & 0xff) >> 5) & 0xff)) & 0xff);
938: key[5] = (byte) ((((key56[4] & 0x1f) << 2) | (((key56[5] & 0xff) >> 6) & 0xff)) & 0xff);
939: key[6] = (byte) ((((key56[5] & 0x3f) << 1) | (((key56[6] & 0xff) >> 7) & 0xff)) & 0xff);
940: key[7] = (byte) (key56[6] & 0x7f);
941:
942: for (int i = 0; i < key.length; i++) {
943: key[i] = (byte) (key[i] << 1);
944: }
945: return key;
946: }
947:
948: public String getAuthenticationScheme() {
949: return "NTLM";
950: }
951:
952: }
|