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