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