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