001: /* jcifs smb client library in Java
002: * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
003: * "Eric Glass" <jcifs at samba dot org>
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package jcifs.ntlmssp;
021:
022: import java.io.IOException;
023:
024: import java.net.UnknownHostException;
025:
026: import java.security.SecureRandom;
027:
028: import jcifs.Config;
029:
030: import jcifs.netbios.NbtAddress;
031:
032: import jcifs.smb.NtlmPasswordAuthentication;
033:
034: /**
035: * Represents an NTLMSSP Type-3 message.
036: */
037: public class Type3Message extends NtlmMessage {
038:
039: private static final int DEFAULT_FLAGS;
040:
041: private static final String DEFAULT_DOMAIN;
042:
043: private static final String DEFAULT_USER;
044:
045: private static final String DEFAULT_PASSWORD;
046:
047: private static final String DEFAULT_WORKSTATION;
048:
049: private static final int LM_COMPATIBILITY;
050:
051: private static final SecureRandom RANDOM = new SecureRandom();
052:
053: private byte[] lmResponse;
054:
055: private byte[] ntResponse;
056:
057: private String domain;
058:
059: private String user;
060:
061: private String workstation;
062:
063: private byte[] sessionKey;
064:
065: static {
066: DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM
067: | (Config.getBoolean("jcifs.smb.client.useUnicode",
068: true) ? NTLMSSP_NEGOTIATE_UNICODE
069: : NTLMSSP_NEGOTIATE_OEM);
070: DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain",
071: null);
072: DEFAULT_USER = Config.getProperty("jcifs.smb.client.username",
073: null);
074: DEFAULT_PASSWORD = Config.getProperty(
075: "jcifs.smb.client.password", null);
076: String defaultWorkstation = null;
077: try {
078: defaultWorkstation = NbtAddress.getLocalHost()
079: .getHostName();
080: } catch (UnknownHostException ex) {
081: }
082: DEFAULT_WORKSTATION = defaultWorkstation;
083: LM_COMPATIBILITY = Config
084: .getInt("jcifs.smb.lmCompatibility", 0);
085: }
086:
087: /**
088: * Creates a Type-3 message using default values from the current
089: * environment.
090: */
091: public Type3Message() {
092: setFlags(getDefaultFlags());
093: setDomain(getDefaultDomain());
094: setUser(getDefaultUser());
095: setWorkstation(getDefaultWorkstation());
096: }
097:
098: /**
099: * Creates a Type-3 message in response to the given Type-2 message
100: * using default values from the current environment.
101: *
102: * @param type2 The Type-2 message which this represents a response to.
103: */
104: public Type3Message(Type2Message type2) {
105: setFlags(getDefaultFlags(type2));
106: setWorkstation(getDefaultWorkstation());
107: String domain = getDefaultDomain();
108: setDomain(domain);
109: String user = getDefaultUser();
110: setUser(user);
111: String password = getDefaultPassword();
112: switch (LM_COMPATIBILITY) {
113: case 0:
114: case 1:
115: setLMResponse(getLMResponse(type2, password));
116: setNTResponse(getNTResponse(type2, password));
117: break;
118: case 2:
119: byte[] nt = getNTResponse(type2, password);
120: setLMResponse(nt);
121: setNTResponse(nt);
122: break;
123: case 3:
124: case 4:
125: case 5:
126: byte[] clientChallenge = new byte[8];
127: RANDOM.nextBytes(clientChallenge);
128: setLMResponse(getLMv2Response(type2, domain, user,
129: password, clientChallenge));
130: /*
131: setNTResponse(getNTLMv2Response(type2, domain, user, password,
132: clientChallenge));
133: */
134: break;
135: default:
136: setLMResponse(getLMResponse(type2, password));
137: setNTResponse(getNTResponse(type2, password));
138: }
139: }
140:
141: /**
142: * Creates a Type-3 message in response to the given Type-2 message.
143: *
144: * @param type2 The Type-2 message which this represents a response to.
145: * @param password The password to use when constructing the response.
146: * @param domain The domain in which the user has an account.
147: * @param user The username for the authenticating user.
148: * @param workstation The workstation from which authentication is
149: * taking place.
150: */
151: public Type3Message(Type2Message type2, String password,
152: String domain, String user, String workstation) {
153: setFlags(getDefaultFlags(type2));
154: setDomain(domain);
155: setUser(user);
156: setWorkstation(workstation);
157: switch (LM_COMPATIBILITY) {
158: case 0:
159: case 1:
160: setLMResponse(getLMResponse(type2, password));
161: setNTResponse(getNTResponse(type2, password));
162: break;
163: case 2:
164: byte[] nt = getNTResponse(type2, password);
165: setLMResponse(nt);
166: setNTResponse(nt);
167: break;
168: case 3:
169: case 4:
170: case 5:
171: byte[] clientChallenge = new byte[8];
172: RANDOM.nextBytes(clientChallenge);
173: setLMResponse(getLMv2Response(type2, domain, user,
174: password, clientChallenge));
175: /*
176: setNTResponse(getNTLMv2Response(type2, domain, user, password,
177: clientChallenge));
178: */
179: break;
180: default:
181: setLMResponse(getLMResponse(type2, password));
182: setNTResponse(getNTResponse(type2, password));
183: }
184: }
185:
186: /**
187: * Creates a Type-3 message with the specified parameters.
188: *
189: * @param flags The flags to apply to this message.
190: * @param lmResponse The LanManager/LMv2 response.
191: * @param ntResponse The NT/NTLMv2 response.
192: * @param domain The domain in which the user has an account.
193: * @param user The username for the authenticating user.
194: * @param workstation The workstation from which authentication is
195: * taking place.
196: */
197: public Type3Message(int flags, byte[] lmResponse,
198: byte[] ntResponse, String domain, String user,
199: String workstation) {
200: setFlags(flags);
201: setLMResponse(lmResponse);
202: setNTResponse(ntResponse);
203: setDomain(domain);
204: setUser(user);
205: setWorkstation(workstation);
206: }
207:
208: /**
209: * Creates a Type-3 message using the given raw Type-3 material.
210: *
211: * @param material The raw Type-3 material used to construct this message.
212: * @throws IOException If an error occurs while parsing the material.
213: */
214: public Type3Message(byte[] material) throws IOException {
215: parse(material);
216: }
217:
218: /**
219: * Returns the LanManager/LMv2 response.
220: *
221: * @return A <code>byte[]</code> containing the LanManager response.
222: */
223: public byte[] getLMResponse() {
224: return lmResponse;
225: }
226:
227: /**
228: * Sets the LanManager/LMv2 response for this message.
229: *
230: * @param lmResponse The LanManager response.
231: */
232: public void setLMResponse(byte[] lmResponse) {
233: this .lmResponse = lmResponse;
234: }
235:
236: /**
237: * Returns the NT/NTLMv2 response.
238: *
239: * @return A <code>byte[]</code> containing the NT/NTLMv2 response.
240: */
241: public byte[] getNTResponse() {
242: return ntResponse;
243: }
244:
245: /**
246: * Sets the NT/NTLMv2 response for this message.
247: *
248: * @param ntResponse The NT/NTLMv2 response.
249: */
250: public void setNTResponse(byte[] ntResponse) {
251: this .ntResponse = ntResponse;
252: }
253:
254: /**
255: * Returns the domain in which the user has an account.
256: *
257: * @return A <code>String</code> containing the domain for the user.
258: */
259: public String getDomain() {
260: return domain;
261: }
262:
263: /**
264: * Sets the domain for this message.
265: *
266: * @param domain The domain.
267: */
268: public void setDomain(String domain) {
269: this .domain = domain;
270: }
271:
272: /**
273: * Returns the username for the authenticating user.
274: *
275: * @return A <code>String</code> containing the user for this message.
276: */
277: public String getUser() {
278: return user;
279: }
280:
281: /**
282: * Sets the user for this message.
283: *
284: * @param user The user.
285: */
286: public void setUser(String user) {
287: this .user = user;
288: }
289:
290: /**
291: * Returns the workstation from which authentication is being performed.
292: *
293: * @return A <code>String</code> containing the workstation.
294: */
295: public String getWorkstation() {
296: return workstation;
297: }
298:
299: /**
300: * Sets the workstation for this message.
301: *
302: * @param workstation The workstation.
303: */
304: public void setWorkstation(String workstation) {
305: this .workstation = workstation;
306: }
307:
308: /**
309: * Returns the session key.
310: *
311: * @return A <code>byte[]</code> containing the session key.
312: */
313: public byte[] getSessionKey() {
314: return sessionKey;
315: }
316:
317: /**
318: * Sets the session key.
319: *
320: * @param sessionKey The session key.
321: */
322: public void setSessionKey(byte[] sessionKey) {
323: this .sessionKey = sessionKey;
324: }
325:
326: public byte[] toByteArray() {
327: try {
328: int flags = getFlags();
329: boolean unicode = (flags & NTLMSSP_NEGOTIATE_UNICODE) != 0;
330: String oem = unicode ? null : getOEMEncoding();
331: String domainName = getDomain();
332: byte[] domain = null;
333: if (domainName != null && domainName.length() != 0) {
334: domain = unicode ? domainName.toUpperCase().getBytes(
335: "UnicodeLittleUnmarked") : domainName
336: .toUpperCase().getBytes(oem);
337: }
338: int domainLength = (domain != null) ? domain.length : 0;
339: String userName = getUser();
340: byte[] user = null;
341: if (userName != null && userName.length() != 0) {
342: user = unicode ? userName
343: .getBytes("UnicodeLittleUnmarked") : userName
344: .toUpperCase().getBytes(oem);
345: }
346: int userLength = (user != null) ? user.length : 0;
347: String workstationName = getWorkstation();
348: byte[] workstation = null;
349: if (workstationName != null
350: && workstationName.length() != 0) {
351: workstation = unicode ? workstationName
352: .getBytes("UnicodeLittleUnmarked")
353: : workstationName.toUpperCase().getBytes(oem);
354: }
355: int workstationLength = (workstation != null) ? workstation.length
356: : 0;
357: byte[] lmResponse = getLMResponse();
358: int lmLength = (lmResponse != null) ? lmResponse.length : 0;
359: byte[] ntResponse = getNTResponse();
360: int ntLength = (ntResponse != null) ? ntResponse.length : 0;
361: byte[] sessionKey = getSessionKey();
362: int keyLength = (sessionKey != null) ? sessionKey.length
363: : 0;
364: byte[] type3 = new byte[64 + domainLength + userLength
365: + workstationLength + lmLength + ntLength
366: + keyLength];
367: System.arraycopy(NTLMSSP_SIGNATURE, 0, type3, 0, 8);
368: writeULong(type3, 8, 3);
369: int offset = 64;
370: writeSecurityBuffer(type3, 12, offset, lmResponse);
371: offset += lmLength;
372: writeSecurityBuffer(type3, 20, offset, ntResponse);
373: offset += ntLength;
374: writeSecurityBuffer(type3, 28, offset, domain);
375: offset += domainLength;
376: writeSecurityBuffer(type3, 36, offset, user);
377: offset += userLength;
378: writeSecurityBuffer(type3, 44, offset, workstation);
379: offset += workstationLength;
380: writeSecurityBuffer(type3, 52, offset, sessionKey);
381: writeULong(type3, 60, flags);
382: return type3;
383: } catch (IOException ex) {
384: throw new IllegalStateException(ex.getMessage());
385: }
386: }
387:
388: public String toString() {
389: String user = getUser();
390: String domain = getDomain();
391: String workstation = getWorkstation();
392: byte[] lmResponse = getLMResponse();
393: byte[] ntResponse = getNTResponse();
394: byte[] sessionKey = getSessionKey();
395: int flags = getFlags();
396: StringBuffer buffer = new StringBuffer();
397: if (domain != null) {
398: buffer.append("domain: ").append(domain);
399: }
400: if (user != null) {
401: if (buffer.length() > 0)
402: buffer.append("; ");
403: buffer.append("user: ").append(user);
404: }
405: if (workstation != null) {
406: if (buffer.length() > 0)
407: buffer.append("; ");
408: buffer.append("workstation: ").append(workstation);
409: }
410: if (lmResponse != null) {
411: if (buffer.length() > 0)
412: buffer.append("; ");
413: buffer.append("lmResponse: ");
414: buffer.append("0x");
415: for (int i = 0; i < lmResponse.length; i++) {
416: buffer.append(Integer
417: .toHexString((lmResponse[i] >> 4) & 0x0f));
418: buffer
419: .append(Integer
420: .toHexString(lmResponse[i] & 0x0f));
421: }
422: }
423: if (ntResponse != null) {
424: if (buffer.length() > 0)
425: buffer.append("; ");
426: buffer.append("ntResponse: ");
427: buffer.append("0x");
428: for (int i = 0; i < ntResponse.length; i++) {
429: buffer.append(Integer
430: .toHexString((ntResponse[i] >> 4) & 0x0f));
431: buffer
432: .append(Integer
433: .toHexString(ntResponse[i] & 0x0f));
434: }
435: }
436: if (sessionKey != null) {
437: if (buffer.length() > 0)
438: buffer.append("; ");
439: buffer.append("sessionKey: ");
440: buffer.append("0x");
441: for (int i = 0; i < sessionKey.length; i++) {
442: buffer.append(Integer
443: .toHexString((sessionKey[i] >> 4) & 0x0f));
444: buffer
445: .append(Integer
446: .toHexString(sessionKey[i] & 0x0f));
447: }
448: }
449: if (flags != 0) {
450: if (buffer.length() > 0)
451: buffer.append("; ");
452: buffer.append("flags: ");
453: buffer.append("0x");
454: buffer.append(Integer.toHexString((flags >> 28) & 0x0f));
455: buffer.append(Integer.toHexString((flags >> 24) & 0x0f));
456: buffer.append(Integer.toHexString((flags >> 20) & 0x0f));
457: buffer.append(Integer.toHexString((flags >> 16) & 0x0f));
458: buffer.append(Integer.toHexString((flags >> 12) & 0x0f));
459: buffer.append(Integer.toHexString((flags >> 8) & 0x0f));
460: buffer.append(Integer.toHexString((flags >> 4) & 0x0f));
461: buffer.append(Integer.toHexString(flags & 0x0f));
462: }
463: return buffer.toString();
464: }
465:
466: /**
467: * Returns the default flags for a generic Type-3 message in the
468: * current environment.
469: *
470: * @return An <code>int</code> containing the default flags.
471: */
472: public static int getDefaultFlags() {
473: return DEFAULT_FLAGS;
474: }
475:
476: /**
477: * Returns the default flags for a Type-3 message created in response
478: * to the given Type-2 message in the current environment.
479: *
480: * @return An <code>int</code> containing the default flags.
481: */
482: public static int getDefaultFlags(Type2Message type2) {
483: if (type2 == null)
484: return DEFAULT_FLAGS;
485: int flags = NTLMSSP_NEGOTIATE_NTLM;
486: flags |= ((type2.getFlags() & NTLMSSP_NEGOTIATE_UNICODE) != 0) ? NTLMSSP_NEGOTIATE_UNICODE
487: : NTLMSSP_NEGOTIATE_OEM;
488: return flags;
489: }
490:
491: /**
492: * Constructs the LanManager response to the given Type-2 message using
493: * the supplied password.
494: *
495: * @param type2 The Type-2 message.
496: * @param password The password.
497: * @return A <code>byte[]</code> containing the LanManager response.
498: */
499: public static byte[] getLMResponse(Type2Message type2,
500: String password) {
501: if (type2 == null || password == null)
502: return null;
503: return NtlmPasswordAuthentication.getPreNTLMResponse(password,
504: type2.getChallenge());
505: }
506:
507: public static byte[] getLMv2Response(Type2Message type2,
508: String domain, String user, String password,
509: byte[] clientChallenge) {
510: if (type2 == null || domain == null || user == null
511: || password == null || clientChallenge == null) {
512: return null;
513: }
514: return NtlmPasswordAuthentication.getLMv2Response(domain, user,
515: password, type2.getChallenge(), clientChallenge);
516: }
517:
518: /**
519: * Constructs the NT response to the given Type-2 message using
520: * the supplied password.
521: *
522: * @param type2 The Type-2 message.
523: * @param password The password.
524: * @return A <code>byte[]</code> containing the NT response.
525: */
526: public static byte[] getNTResponse(Type2Message type2,
527: String password) {
528: if (type2 == null || password == null)
529: return null;
530: return NtlmPasswordAuthentication.getNTLMResponse(password,
531: type2.getChallenge());
532: }
533:
534: /**
535: * Returns the default domain from the current environment.
536: *
537: * @return The default domain.
538: */
539: public static String getDefaultDomain() {
540: return DEFAULT_DOMAIN;
541: }
542:
543: /**
544: * Returns the default user from the current environment.
545: *
546: * @return The default user.
547: */
548: public static String getDefaultUser() {
549: return DEFAULT_USER;
550: }
551:
552: /**
553: * Returns the default password from the current environment.
554: *
555: * @return The default password.
556: */
557: public static String getDefaultPassword() {
558: return DEFAULT_PASSWORD;
559: }
560:
561: /**
562: * Returns the default workstation from the current environment.
563: *
564: * @return The default workstation.
565: */
566: public static String getDefaultWorkstation() {
567: return DEFAULT_WORKSTATION;
568: }
569:
570: private void parse(byte[] material) throws IOException {
571: for (int i = 0; i < 8; i++) {
572: if (material[i] != NTLMSSP_SIGNATURE[i]) {
573: throw new IOException("Not an NTLMSSP message.");
574: }
575: }
576: if (readULong(material, 8) != 3) {
577: throw new IOException("Not a Type 3 message.");
578: }
579: byte[] lmResponse = readSecurityBuffer(material, 12);
580: int lmResponseOffset = readULong(material, 16);
581: byte[] ntResponse = readSecurityBuffer(material, 20);
582: int ntResponseOffset = readULong(material, 24);
583: byte[] domain = readSecurityBuffer(material, 28);
584: int domainOffset = readULong(material, 32);
585: byte[] user = readSecurityBuffer(material, 36);
586: int userOffset = readULong(material, 40);
587: byte[] workstation = readSecurityBuffer(material, 44);
588: int workstationOffset = readULong(material, 48);
589: int flags;
590: String charset;
591: if (lmResponseOffset == 52 || ntResponseOffset == 52
592: || domainOffset == 52 || userOffset == 52
593: || workstationOffset == 52) {
594: flags = NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_OEM;
595: charset = getOEMEncoding();
596: } else {
597: setSessionKey(readSecurityBuffer(material, 52));
598: flags = readULong(material, 60);
599: charset = ((flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ? "UnicodeLittleUnmarked"
600: : getOEMEncoding();
601: }
602: setFlags(flags);
603: setLMResponse(lmResponse);
604: // NTLMv2 issues w/cross-domain authentication; leave NT empty if >= 3
605: if (LM_COMPATIBILITY < 3)
606: setNTResponse(ntResponse);
607: setDomain(new String(domain, charset));
608: setUser(new String(user, charset));
609: setWorkstation(new String(workstation, charset));
610: }
611:
612: }
|