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