1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.proxy.handlers.http.ntlm;
21
22 import java.io.BufferedReader;
23 import java.io.ByteArrayOutputStream;
24 import java.io.IOException;
25 import java.io.InputStreamReader;
26 import java.io.PrintWriter;
27 import java.io.UnsupportedEncodingException;
28 import java.util.StringTokenizer;
29
30 import org.apache.mina.proxy.utils.ByteUtilities;
31
32
33
34
35
36
37
38
39 public class NTLMUtilities implements NTLMConstants {
40
41
42
43 public final static byte[] writeSecurityBuffer(short length,
44 int bufferOffset) {
45 byte[] b = new byte[8];
46 writeSecurityBuffer(length, length, bufferOffset, b, 0);
47 return b;
48 }
49
50
51
52
53
54
55
56
57
58
59
60
61
62 public final static void writeSecurityBuffer(short length, short allocated,
63 int bufferOffset, byte[] b, int offset) {
64 ByteUtilities.writeShort(length, b, offset);
65 ByteUtilities.writeShort(allocated, b, offset + 2);
66 ByteUtilities.writeInt(bufferOffset, b, offset + 4);
67 }
68
69 public final static void writeOSVersion(byte majorVersion,
70 byte minorVersion, short buildNumber, byte[] b, int offset) {
71 b[offset] = majorVersion;
72 b[offset + 1] = minorVersion;
73 b[offset + 2] = (byte) buildNumber;
74 b[offset + 3] = (byte) (buildNumber >> 8);
75 b[offset + 4] = 0;
76 b[offset + 5] = 0;
77 b[offset + 6] = 0;
78 b[offset + 7] = 0x0F;
79 }
80
81
82
83
84 public final static byte[] getOsVersion() {
85 String os = System.getProperty("os.name");
86 if (os == null || !os.toUpperCase().contains("WINDOWS")) {
87 return DEFAULT_OS_VERSION;
88 } else {
89 byte[] osVer = new byte[8];
90 try {
91 Process pr = Runtime.getRuntime().exec("cmd /C ver");
92 BufferedReader reader = new BufferedReader(
93 new InputStreamReader(pr.getInputStream()));
94 pr.waitFor();
95
96 String line;
97 do {
98 line = reader.readLine();
99 } while ((line != null) && (line.length() != 0));
100
101 int pos = line.toLowerCase().indexOf("version");
102
103 if (pos == -1) {
104 throw new NullPointerException();
105 }
106
107 pos += 8;
108 line = line.substring(pos, line.indexOf(']'));
109 StringTokenizer tk = new StringTokenizer(line, ".");
110 if (tk.countTokens() != 3) {
111 throw new NullPointerException();
112 }
113
114 writeOSVersion(Byte.parseByte(tk.nextToken()), Byte
115 .parseByte(tk.nextToken()), Short.parseShort(tk
116 .nextToken()), osVer, 0);
117 } catch (Exception ex) {
118 try {
119 String version = System.getProperty("os.version");
120 writeOSVersion(Byte.parseByte(version.substring(0, 1)),
121 Byte.parseByte(version.substring(2, 3)), (short) 0,
122 osVer, 0);
123 } catch (Exception ex2) {
124 return DEFAULT_OS_VERSION;
125 }
126 }
127 return osVer;
128 }
129 }
130
131
132
133
134
135
136
137
138
139
140
141
142 public final static byte[] createType1Message(String workStation,
143 String domain, Integer customFlags, byte[] osVersion) {
144 byte[] msg = null;
145
146 if (osVersion != null && osVersion.length != 8) {
147 throw new IllegalArgumentException(
148 "osVersion parameter should be a 8 byte wide array");
149 }
150
151 if (workStation == null || domain == null) {
152 throw new NullPointerException(
153 "workStation and domain must be non null");
154 }
155
156 int flags = customFlags != null ? customFlags
157 | FLAG_NEGOTIATE_WORKSTATION_SUPPLIED
158 | FLAG_NEGOTIATE_DOMAIN_SUPPLIED : DEFAULT_FLAGS;
159
160 ByteArrayOutputStream baos = new ByteArrayOutputStream();
161
162 try {
163 baos.write(NTLM_SIGNATURE);
164 baos.write(ByteUtilities.writeInt(MESSAGE_TYPE_1));
165 baos.write(ByteUtilities.writeInt(flags));
166
167 byte[] domainData = ByteUtilities.getOEMStringAsByteArray(domain);
168 byte[] workStationData = ByteUtilities
169 .getOEMStringAsByteArray(workStation);
170
171 int pos = (osVersion != null) ? 40 : 32;
172 baos.write(writeSecurityBuffer((short) domainData.length, pos
173 + workStationData.length));
174 baos
175 .write(writeSecurityBuffer((short) workStationData.length,
176 pos));
177
178 if (osVersion != null) {
179 baos.write(osVersion);
180 }
181
182
183 baos.write(workStationData);
184 baos.write(domainData);
185
186 msg = baos.toByteArray();
187 baos.close();
188 } catch (IOException e) {
189 return null;
190 }
191
192 return msg;
193 }
194
195
196
197
198
199
200
201
202
203
204
205 public final static int writeSecurityBufferAndUpdatePointer(
206 ByteArrayOutputStream baos, short len, int pointer)
207 throws IOException {
208 baos.write(writeSecurityBuffer(len, pointer));
209 return pointer + len;
210 }
211
212 public final static byte[] extractChallengeFromType2Message(byte[] msg) {
213 byte[] challenge = new byte[8];
214 System.arraycopy(msg, 24, challenge, 0, 8);
215 return challenge;
216 }
217
218 public final static int extractFlagsFromType2Message(byte[] msg) {
219 byte[] flagsBytes = new byte[4];
220
221 System.arraycopy(msg, 20, flagsBytes, 0, 4);
222 ByteUtilities.changeWordEndianess(flagsBytes, 0, 4);
223
224 return ByteUtilities.makeIntFromByte4(flagsBytes);
225 }
226
227 public final static String extractTargetNameFromType2Message(byte[] msg,
228 Integer msgFlags) throws UnsupportedEncodingException {
229 byte[] targetName = null;
230
231
232 byte[] securityBuffer = new byte[8];
233
234 System.arraycopy(msg, 12, securityBuffer, 0, 8);
235 ByteUtilities.changeWordEndianess(securityBuffer, 0, 8);
236 int length = ByteUtilities.makeIntFromByte2(securityBuffer);
237 int offset = ByteUtilities.makeIntFromByte4(securityBuffer, 4);
238
239 targetName = new byte[length];
240 System.arraycopy(msg, offset, targetName, 0, length);
241
242 int flags = msgFlags == null ? extractFlagsFromType2Message(msg)
243 : msgFlags;
244 if (ByteUtilities.isFlagSet(flags, FLAG_NEGOTIATE_UNICODE)) {
245 return new String(targetName, "UTF-16LE");
246 } else {
247 return new String(targetName, "ASCII");
248 }
249 }
250
251 public final static byte[] extractTargetInfoFromType2Message(byte[] msg,
252 Integer msgFlags) {
253 int flags = msgFlags == null ? extractFlagsFromType2Message(msg)
254 : msgFlags;
255 byte[] targetInformationBlock = null;
256
257 if (!ByteUtilities.isFlagSet(flags, FLAG_NEGOTIATE_TARGET_INFO))
258 return null;
259
260 int pos = 40;
261
262
263 byte[] securityBuffer = new byte[8];
264
265 System.arraycopy(msg, pos, securityBuffer, 0, 8);
266 ByteUtilities.changeWordEndianess(securityBuffer, 0, 8);
267 int length = ByteUtilities.makeIntFromByte2(securityBuffer);
268 int offset = ByteUtilities.makeIntFromByte4(securityBuffer, 4);
269
270 targetInformationBlock = new byte[length];
271 System.arraycopy(msg, offset, targetInformationBlock, 0, length);
272
273 return targetInformationBlock;
274 }
275
276 public final static void printTargetInformationBlockFromType2Message(
277 byte[] msg, Integer msgFlags, PrintWriter out)
278 throws UnsupportedEncodingException {
279 int flags = msgFlags == null ? extractFlagsFromType2Message(msg)
280 : msgFlags;
281
282 byte[] infoBlock = extractTargetInfoFromType2Message(msg, flags);
283 if (infoBlock == null) {
284 out.println("No target information block found !");
285 } else {
286 int pos = 0;
287 while (infoBlock[pos] != 0) {
288 out.print("---\nType " + infoBlock[pos] + ": ");
289 switch (infoBlock[pos]) {
290 case 1:
291 out.println("Server name");
292 break;
293 case 2:
294 out.println("Domain name");
295 break;
296 case 3:
297 out.println("Fully qualified DNS hostname");
298 break;
299 case 4:
300 out.println("DNS domain name");
301 break;
302 case 5:
303 out.println("Parent DNS domain name");
304 break;
305 }
306 byte[] len = new byte[2];
307 System.arraycopy(infoBlock, pos + 2, len, 0, 2);
308 ByteUtilities.changeByteEndianess(len, 0, 2);
309
310 int length = ByteUtilities.makeIntFromByte2(len, 0);
311 out.println("Length: " + length + " bytes");
312 out.print("Data: ");
313 if (ByteUtilities.isFlagSet(flags, FLAG_NEGOTIATE_UNICODE)) {
314 out.println(new String(infoBlock, pos + 4, length,
315 "UTF-16LE"));
316 } else {
317 out
318 .println(new String(infoBlock, pos + 4, length,
319 "ASCII"));
320 }
321 pos += 4 + length;
322 out.flush();
323 }
324 }
325 }
326
327
328
329
330 public final static byte[] createType3Message(String user, String password,
331 byte[] challenge, String target, String workstation,
332 Integer serverFlags, byte[] osVersion) {
333 byte[] msg = null;
334
335 if (challenge == null || challenge.length != 8) {
336 throw new IllegalArgumentException(
337 "challenge[] should be a 8 byte wide array");
338 }
339
340 if (osVersion != null && osVersion.length != 8) {
341 throw new IllegalArgumentException(
342 "osVersion should be a 8 byte wide array");
343 }
344
345
346
347
348
349 int flags = serverFlags != null ? serverFlags : DEFAULT_FLAGS;
350
351 ByteArrayOutputStream baos = new ByteArrayOutputStream();
352
353 try {
354 baos.write(NTLM_SIGNATURE);
355 baos.write(ByteUtilities.writeInt(MESSAGE_TYPE_3));
356
357 byte[] dataLMResponse = NTLMResponses.getLMResponse(password,
358 challenge);
359 byte[] dataNTLMResponse = NTLMResponses.getNTLMResponse(password,
360 challenge);
361
362 boolean useUnicode = ByteUtilities.isFlagSet(flags,
363 FLAG_NEGOTIATE_UNICODE);
364 byte[] targetName = ByteUtilities.encodeString(target, useUnicode);
365 byte[] userName = ByteUtilities.encodeString(user, useUnicode);
366 byte[] workstationName = ByteUtilities.encodeString(workstation,
367 useUnicode);
368
369 int pos = osVersion != null ? 72 : 64;
370 int responsePos = pos + targetName.length + userName.length
371 + workstationName.length;
372 responsePos = writeSecurityBufferAndUpdatePointer(baos,
373 (short) dataLMResponse.length, responsePos);
374 writeSecurityBufferAndUpdatePointer(baos,
375 (short) dataNTLMResponse.length, responsePos);
376 pos = writeSecurityBufferAndUpdatePointer(baos,
377 (short) targetName.length, pos);
378 pos = writeSecurityBufferAndUpdatePointer(baos,
379 (short) userName.length, pos);
380 writeSecurityBufferAndUpdatePointer(baos,
381 (short) workstationName.length, pos);
382
383
384
385
386
387
388
389
390
391
392
393
394 baos.write(new byte[] { 0, 0, 0, 0, (byte) 0x9a, 0, 0, 0 });
395 baos.write(ByteUtilities.writeInt(flags));
396
397 if (osVersion != null) {
398 baos.write(osVersion);
399 }
400
401
402
403
404 baos.write(targetName);
405 baos.write(userName);
406 baos.write(workstationName);
407
408 baos.write(dataLMResponse);
409 baos.write(dataNTLMResponse);
410
411 msg = baos.toByteArray();
412 baos.close();
413 } catch (Exception e) {
414 e.printStackTrace();
415 return null;
416 }
417
418 return msg;
419 }
420 }