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