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 jcifs.Config;
027:
028: import jcifs.netbios.NbtAddress;
029:
030: /**
031: * Represents an NTLMSSP Type-2 message.
032: */
033: public class Type2Message extends NtlmMessage {
034:
035: private static final int DEFAULT_FLAGS;
036:
037: private static final String DEFAULT_DOMAIN;
038:
039: private static final byte[] DEFAULT_TARGET_INFORMATION;
040:
041: private byte[] challenge;
042:
043: private String target;
044:
045: private byte[] context;
046:
047: private byte[] targetInformation;
048:
049: static {
050: DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_NTLM
051: | (Config.getBoolean("jcifs.smb.client.useUnicode",
052: true) ? NTLMSSP_NEGOTIATE_UNICODE
053: : NTLMSSP_NEGOTIATE_OEM);
054: DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain",
055: null);
056: byte[] domain = new byte[0];
057: if (DEFAULT_DOMAIN != null) {
058: try {
059: domain = DEFAULT_DOMAIN
060: .getBytes("UnicodeLittleUnmarked");
061: } catch (IOException ex) {
062: }
063: }
064: int domainLength = domain.length;
065: byte[] server = new byte[0];
066: try {
067: String host = NbtAddress.getLocalHost().getHostName();
068: if (host != null) {
069: try {
070: server = host.getBytes("UnicodeLittleUnmarked");
071: } catch (IOException ex) {
072: }
073: }
074: } catch (UnknownHostException ex) {
075: }
076: int serverLength = server.length;
077: byte[] targetInfo = new byte[(domainLength > 0 ? domainLength + 4
078: : 0)
079: + (serverLength > 0 ? serverLength + 4 : 0) + 4];
080: int offset = 0;
081: if (domainLength > 0) {
082: writeUShort(targetInfo, offset, 2);
083: offset += 2;
084: writeUShort(targetInfo, offset, domainLength);
085: offset += 2;
086: System.arraycopy(domain, 0, targetInfo, offset,
087: domainLength);
088: offset += domainLength;
089: }
090: if (serverLength > 0) {
091: writeUShort(targetInfo, offset, 1);
092: offset += 2;
093: writeUShort(targetInfo, offset, serverLength);
094: offset += 2;
095: System.arraycopy(server, 0, targetInfo, offset,
096: serverLength);
097: }
098: DEFAULT_TARGET_INFORMATION = targetInfo;
099: }
100:
101: /**
102: * Creates a Type-2 message using default values from the current
103: * environment.
104: */
105: public Type2Message() {
106: this (getDefaultFlags(), null, null);
107: }
108:
109: /**
110: * Creates a Type-2 message in response to the given Type-1 message
111: * using default values from the current environment.
112: *
113: * @param type1 The Type-1 message which this represents a response to.
114: */
115: public Type2Message(Type1Message type1) {
116: this (type1, null, null);
117: }
118:
119: /**
120: * Creates a Type-2 message in response to the given Type-1 message.
121: *
122: * @param type1 The Type-1 message which this represents a response to.
123: * @param challenge The challenge from the domain controller/server.
124: * @param target The authentication target.
125: */
126: public Type2Message(Type1Message type1, byte[] challenge,
127: String target) {
128: this (getDefaultFlags(type1), challenge, (type1 != null
129: && target == null && type1
130: .getFlag(NTLMSSP_REQUEST_TARGET)) ? getDefaultDomain()
131: : target);
132: }
133:
134: /**
135: * Creates a Type-2 message with the specified parameters.
136: *
137: * @param flags The flags to apply to this message.
138: * @param challenge The challenge from the domain controller/server.
139: * @param target The authentication target.
140: */
141: public Type2Message(int flags, byte[] challenge, String target) {
142: setFlags(flags);
143: setChallenge(challenge);
144: setTarget(target);
145: if (target != null)
146: setTargetInformation(getDefaultTargetInformation());
147: }
148:
149: /**
150: * Creates a Type-2 message using the given raw Type-2 material.
151: *
152: * @param material The raw Type-2 material used to construct this message.
153: * @throws IOException If an error occurs while parsing the material.
154: */
155: public Type2Message(byte[] material) throws IOException {
156: parse(material);
157: }
158:
159: /**
160: * Returns the challenge for this message.
161: *
162: * @return A <code>byte[]</code> containing the challenge.
163: */
164: public byte[] getChallenge() {
165: return challenge;
166: }
167:
168: /**
169: * Sets the challenge for this message.
170: *
171: * @param challenge The challenge from the domain controller/server.
172: */
173: public void setChallenge(byte[] challenge) {
174: this .challenge = challenge;
175: }
176:
177: /**
178: * Returns the authentication target.
179: *
180: * @return A <code>String</code> containing the authentication target.
181: */
182: public String getTarget() {
183: return target;
184: }
185:
186: /**
187: * Sets the authentication target.
188: *
189: * @param target The authentication target.
190: */
191: public void setTarget(String target) {
192: this .target = target;
193: }
194:
195: /**
196: * Returns the target information block.
197: *
198: * @return A <code>byte[]</code> containing the target information block.
199: * The target information block is used by the client to create an
200: * NTLMv2 response.
201: */
202: public byte[] getTargetInformation() {
203: return targetInformation;
204: }
205:
206: /**
207: * Sets the target information block.
208: * The target information block is used by the client to create
209: * an NTLMv2 response.
210: *
211: * @param targetInformation The target information block.
212: */
213: public void setTargetInformation(byte[] targetInformation) {
214: this .targetInformation = targetInformation;
215: }
216:
217: /**
218: * Returns the local security context.
219: *
220: * @return A <code>byte[]</code> containing the local security
221: * context. This is used by the client to negotiate local
222: * authentication.
223: */
224: public byte[] getContext() {
225: return context;
226: }
227:
228: /**
229: * Sets the local security context. This is used by the client
230: * to negotiate local authentication.
231: *
232: * @param context The local security context.
233: */
234: public void setContext(byte[] context) {
235: this .context = context;
236: }
237:
238: public byte[] toByteArray() {
239: try {
240: String targetName = getTarget();
241: byte[] challenge = getChallenge();
242: byte[] context = getContext();
243: byte[] targetInformation = getTargetInformation();
244: int flags = getFlags();
245: byte[] target = new byte[0];
246: if ((flags & (NTLMSSP_TARGET_TYPE_DOMAIN
247: | NTLMSSP_TARGET_TYPE_SERVER | NTLMSSP_TARGET_TYPE_SHARE)) != 0) {
248: if (targetName != null && targetName.length() != 0) {
249: target = (flags & NTLMSSP_NEGOTIATE_UNICODE) != 0 ? targetName
250: .getBytes("UnicodeLittleUnmarked")
251: : targetName.toUpperCase().getBytes(
252: getOEMEncoding());
253: } else {
254: flags &= (0xffffffff ^ (NTLMSSP_TARGET_TYPE_DOMAIN
255: | NTLMSSP_TARGET_TYPE_SERVER | NTLMSSP_TARGET_TYPE_SHARE));
256: }
257: }
258: if (targetInformation != null) {
259: flags ^= NTLMSSP_NEGOTIATE_TARGET_INFO;
260: // empty context is needed for padding when t.i. is supplied.
261: if (context == null)
262: context = new byte[8];
263: }
264: int data = 32;
265: if (context != null)
266: data += 8;
267: if (targetInformation != null)
268: data += 8;
269: byte[] type2 = new byte[data
270: + target.length
271: + (targetInformation != null ? targetInformation.length
272: : 0)];
273: System.arraycopy(NTLMSSP_SIGNATURE, 0, type2, 0, 8);
274: writeULong(type2, 8, 2);
275: writeSecurityBuffer(type2, 12, data, target);
276: writeULong(type2, 20, flags);
277: System.arraycopy(challenge != null ? challenge
278: : new byte[8], 0, type2, 24, 8);
279: if (context != null)
280: System.arraycopy(context, 0, type2, 32, 8);
281: if (targetInformation != null) {
282: writeSecurityBuffer(type2, 40, data + target.length,
283: targetInformation);
284: }
285: return type2;
286: } catch (IOException ex) {
287: throw new IllegalStateException(ex.getMessage());
288: }
289: }
290:
291: public String toString() {
292: String target = getTarget();
293: byte[] challenge = getChallenge();
294: byte[] context = getContext();
295: byte[] targetInformation = getTargetInformation();
296: int flags = getFlags();
297: StringBuffer buffer = new StringBuffer();
298: if (target != null) {
299: buffer.append("target: ").append(target);
300: }
301: if (challenge != null) {
302: if (buffer.length() > 0)
303: buffer.append("; ");
304: buffer.append("challenge: ");
305: buffer.append("0x");
306: for (int i = 0; i < challenge.length; i++) {
307: buffer.append(Integer
308: .toHexString((challenge[i] >> 4) & 0x0f));
309: buffer.append(Integer.toHexString(challenge[i] & 0x0f));
310: }
311: }
312: if (context != null) {
313: if (buffer.length() > 0)
314: buffer.append("; ");
315: buffer.append("context: ");
316: buffer.append("0x");
317: for (int i = 0; i < context.length; i++) {
318: buffer.append(Integer
319: .toHexString((context[i] >> 4) & 0x0f));
320: buffer.append(Integer.toHexString(context[i] & 0x0f));
321: }
322: }
323: if (targetInformation != null) {
324: if (buffer.length() > 0)
325: buffer.append("; ");
326: buffer.append("targetInformation: ");
327: buffer.append("0x");
328: for (int i = 0; i < targetInformation.length; i++) {
329: buffer
330: .append(Integer
331: .toHexString((targetInformation[i] >> 4) & 0x0f));
332: buffer.append(Integer
333: .toHexString(targetInformation[i] & 0x0f));
334: }
335: }
336: if (flags != 0) {
337: if (buffer.length() > 0)
338: buffer.append("; ");
339: buffer.append("flags: ");
340: buffer.append("0x");
341: buffer.append(Integer.toHexString((flags >> 28) & 0x0f));
342: buffer.append(Integer.toHexString((flags >> 24) & 0x0f));
343: buffer.append(Integer.toHexString((flags >> 20) & 0x0f));
344: buffer.append(Integer.toHexString((flags >> 16) & 0x0f));
345: buffer.append(Integer.toHexString((flags >> 12) & 0x0f));
346: buffer.append(Integer.toHexString((flags >> 8) & 0x0f));
347: buffer.append(Integer.toHexString((flags >> 4) & 0x0f));
348: buffer.append(Integer.toHexString(flags & 0x0f));
349: }
350: return buffer.toString();
351: }
352:
353: /**
354: * Returns the default flags for a generic Type-2 message in the
355: * current environment.
356: *
357: * @return An <code>int</code> containing the default flags.
358: */
359: public static int getDefaultFlags() {
360: return DEFAULT_FLAGS;
361: }
362:
363: /**
364: * Returns the default flags for a Type-2 message created in response
365: * to the given Type-1 message in the current environment.
366: *
367: * @return An <code>int</code> containing the default flags.
368: */
369: public static int getDefaultFlags(Type1Message type1) {
370: if (type1 == null)
371: return DEFAULT_FLAGS;
372: int flags = NTLMSSP_NEGOTIATE_NTLM;
373: int type1Flags = type1.getFlags();
374: flags |= ((type1Flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ? NTLMSSP_NEGOTIATE_UNICODE
375: : NTLMSSP_NEGOTIATE_OEM;
376: if ((type1Flags & NTLMSSP_REQUEST_TARGET) != 0) {
377: String domain = getDefaultDomain();
378: if (domain != null) {
379: flags |= NTLMSSP_REQUEST_TARGET
380: | NTLMSSP_TARGET_TYPE_DOMAIN;
381: }
382: }
383: return flags;
384: }
385:
386: /**
387: * Returns the default domain from the current environment.
388: *
389: * @return A <code>String</code> containing the domain.
390: */
391: public static String getDefaultDomain() {
392: return DEFAULT_DOMAIN;
393: }
394:
395: public static byte[] getDefaultTargetInformation() {
396: return DEFAULT_TARGET_INFORMATION;
397: }
398:
399: private void parse(byte[] material) throws IOException {
400: for (int i = 0; i < 8; i++) {
401: if (material[i] != NTLMSSP_SIGNATURE[i]) {
402: throw new IOException("Not an NTLMSSP message.");
403: }
404: }
405: if (readULong(material, 8) != 2) {
406: throw new IOException("Not a Type 2 message.");
407: }
408: int flags = readULong(material, 20);
409: setFlags(flags);
410: String target = null;
411: byte[] bytes = readSecurityBuffer(material, 12);
412: if (bytes.length != 0) {
413: target = new String(
414: bytes,
415: ((flags & NTLMSSP_NEGOTIATE_UNICODE) != 0) ? "UnicodeLittleUnmarked"
416: : getOEMEncoding());
417: }
418: setTarget(target);
419: for (int i = 24; i < 32; i++) {
420: if (material[i] != 0) {
421: byte[] challenge = new byte[8];
422: System.arraycopy(material, 24, challenge, 0, 8);
423: setChallenge(challenge);
424: break;
425: }
426: }
427: int offset = readULong(material, 16); // offset of targetname start
428: if (offset == 32 || material.length == 32)
429: return;
430: for (int i = 32; i < 40; i++) {
431: if (material[i] != 0) {
432: byte[] context = new byte[8];
433: System.arraycopy(material, 32, context, 0, 8);
434: setContext(context);
435: break;
436: }
437: }
438: if (offset == 40 || material.length == 40)
439: return;
440: bytes = readSecurityBuffer(material, 40);
441: if (bytes.length != 0)
442: setTargetInformation(bytes);
443: }
444:
445: }
|