1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.proxy;
21  
22  import static org.apache.mina.proxy.utils.ByteUtilities.asHex;
23  
24  import java.io.PrintWriter;
25  import java.io.UnsupportedEncodingException;
26  import java.security.MessageDigest;
27  import java.security.NoSuchAlgorithmException;
28  import java.security.Security;
29  
30  import junit.framework.TestCase;
31  
32  import org.apache.mina.proxy.handlers.http.ntlm.NTLMResponses;
33  import org.apache.mina.proxy.handlers.http.ntlm.NTLMUtilities;
34  import org.apache.mina.proxy.utils.ByteUtilities;
35  import org.apache.mina.proxy.utils.MD4Provider;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  /**
40   * NTLMTest.java - JUNIT tests of the NTLM authentication mechanism.
41   * 
42   * @author The Apache MINA Project (dev@mina.apache.org)
43   * @version $Rev: 738306 $, $Date: 2009-01-28 00:57:45 +0100 (Wed, 28 Jan 2009) $
44   * @since MINA 2.0.0-M3
45   */
46  public class NTLMTest extends TestCase {
47      private final static Logger logger = LoggerFactory
48              .getLogger(NTLMTest.class);
49  
50      static {
51          if (Security.getProvider("MINA") == null) {
52              Security.addProvider(new MD4Provider());
53          }
54      }
55      
56      /**
57       * Tests bytes manipulations.
58       */
59      public void testEncoding() throws UnsupportedEncodingException {
60          assertEquals("d204", asHex(ByteUtilities.writeShort((short) 1234)));
61          assertEquals("d2040000", asHex(ByteUtilities.writeInt(1234)));
62          assertEquals("01000000", asHex(ByteUtilities.writeInt((short) 1)));
63          assertEquals("4e544c4d53535000", asHex(NTLMUtilities.NTLM_SIGNATURE));
64  
65          assertEquals("680065006c006c006f00", asHex(ByteUtilities
66                  .getUTFStringAsByteArray("hello")));
67          assertEquals("48454c4c4f", asHex(ByteUtilities
68                  .getOEMStringAsByteArray("HELLO")));
69      }
70  
71      /**
72       * Tests conversions to and from network byte order.
73       */
74      public void testMethods() {
75          byte[] buf = new byte[4];
76          ByteUtilities.intToNetworkByteOrder(1234, buf, 0, 4);
77          assertEquals("000004d2", asHex(buf));
78          assertEquals(1234, ByteUtilities.networkByteOrderToInt(buf, 0, 4));
79      }
80  
81      /**
82       * Tests security buffers.
83       */
84      public void testSecurityBuffer() {
85          byte[] secBuf = new byte[8];
86          NTLMUtilities.writeSecurityBuffer((short) 1234, (short) 1234, 4321,
87                  secBuf, 0);
88          assertEquals("d204d204e1100000", asHex(secBuf));
89      }
90  
91      /**
92       * Tests creating a type 1 message.
93       */
94      public void testType1Message() {
95          int customFlags = NTLMUtilities.FLAG_NEGOTIATE_UNICODE
96                  | NTLMUtilities.FLAG_NEGOTIATE_OEM
97                  | NTLMUtilities.FLAG_NEGOTIATE_NTLM
98                  | NTLMUtilities.FLAG_REQUEST_SERVER_AUTH_REALM
99                  | NTLMUtilities.FLAG_NEGOTIATE_DOMAIN_SUPPLIED
100                 | NTLMUtilities.FLAG_NEGOTIATE_WORKSTATION_SUPPLIED;
101 
102         byte[] osVer = new byte[8];
103         NTLMUtilities
104                 .writeOSVersion((byte) 5, (byte) 0, (short) 2195, osVer, 0);
105 
106         String msgType1 = asHex(NTLMUtilities.createType1Message("WORKSTATION",
107                 "DOMAIN", customFlags, osVer));
108         assertEquals(
109                 "4e544c4d53535000010000000732000006000600330000000b000b0028000000"
110                         + "050093080000000f574f524b53544154494f4e444f4d41494e",
111                 msgType1);
112 
113         assertEquals("050093080000000f", asHex(osVer));
114         
115         //Microsoft Windows XP [version 5.1.2600]
116         String os = System.getProperty("os.name");
117         if (os != null && os.toUpperCase().contains("WINDOWS") && 
118                 "5.1".equals(System.getProperty("os.version"))) {
119             String hex = asHex(NTLMUtilities.getOsVersion());
120         	assertEquals("0501", hex.substring(0, 4));
121         	assertEquals(16, hex.length());
122         }
123     }
124 
125     /**
126      * Tests creating a type 3 message.
127      * WARNING: Will silently fail if no MD4 digest provider is available.
128      */
129     public void testType3Message() throws Exception {
130         try {
131             MessageDigest.getInstance("MD4");
132         } catch (NoSuchAlgorithmException ex) {
133             logger.warn("No MD4 digest provider found !");
134             return;
135         }
136 
137         int flags = 0x00000001 | 0x00000200 | 0x00010000 | 0x00800000;
138         String msg = "4e544c4d53535000020000000c000c003000000001028100"
139                 + "0123456789abcdef0000000000000000620062003c000000"
140                 + "44004f004d00410049004e0002000c0044004f004d004100"
141                 + "49004e0001000c0053004500520056004500520004001400"
142                 + "64006f006d00610069006e002e0063006f006d0003002200"
143                 + "7300650072007600650072002e0064006f006d0061006900"
144                 + "6e002e0063006f006d0000000000";
145 
146         byte[] challengePacket = ByteUtilities.asByteArray(msg);
147         int serverFlags = NTLMUtilities
148                 .extractFlagsFromType2Message(challengePacket);
149         assertEquals(flags, serverFlags);
150 
151         NTLMUtilities
152                 .printTargetInformationBlockFromType2Message(challengePacket,
153                         serverFlags, new PrintWriter(System.out, true));
154 
155         byte[] osVer = new byte[8];
156         NTLMUtilities
157                 .writeOSVersion((byte) 5, (byte) 0, (short) 2195, osVer, 0);
158 
159         byte[] challenge = NTLMUtilities
160                 .extractChallengeFromType2Message(challengePacket);
161         assertEquals("0123456789abcdef", asHex(challenge));
162 
163         String expectedTargetInfoBlock = "02000c0044004f004d00410049004e00"
164                 + "01000c00530045005200560045005200"
165                 + "0400140064006f006d00610069006e00"
166                 + "2e0063006f006d000300220073006500"
167                 + "72007600650072002e0064006f006d00"
168                 + "610069006e002e0063006f006d000000" + "0000";
169 
170         byte[] targetInfo = NTLMUtilities.extractTargetInfoFromType2Message(
171                 challengePacket, null);
172         assertEquals(expectedTargetInfoBlock, asHex(targetInfo));
173 
174         assertEquals("DOMAIN", NTLMUtilities.extractTargetNameFromType2Message(
175                 challengePacket, new Integer(serverFlags)));
176 
177         serverFlags = 0x00000001 | 0x00000200;
178         String msgType3 = asHex(NTLMUtilities.createType3Message("user",
179                 "SecREt01", challenge, "DOMAIN", "WORKSTATION", serverFlags,
180                 null));
181 
182         String expected = "4e544c4d5353500003000000180018006a00000018001800"
183                 + "820000000c000c0040000000080008004c00000016001600"
184                 + "54000000000000009a0000000102000044004f004d004100"
185                 + "49004e00750073006500720057004f0052004b0053005400"
186                 + "4100540049004f004e00c337cd5cbd44fc9782a667af6d42"
187                 + "7c6de67c20c2d3e77c5625a98c1c31e81847466b29b2df46"
188                 + "80f39958fb8c213a9cc6";
189         assertEquals(expected, msgType3);
190     }
191 
192     /**
193      * Tests flags manipulations.
194      */
195     public void testFlags() {
196         int flags = NTLMUtilities.FLAG_NEGOTIATE_UNICODE
197                 | NTLMUtilities.FLAG_REQUEST_SERVER_AUTH_REALM
198                 | NTLMUtilities.FLAG_NEGOTIATE_NTLM
199                 | NTLMUtilities.FLAG_NEGOTIATE_ALWAYS_SIGN;
200 
201         int flags2 = NTLMUtilities.FLAG_NEGOTIATE_UNICODE
202                 | NTLMUtilities.FLAG_REQUEST_SERVER_AUTH_REALM
203                 | NTLMUtilities.FLAG_NEGOTIATE_NTLM;
204 
205         assertEquals(flags2, flags
206                 & (~NTLMUtilities.FLAG_NEGOTIATE_ALWAYS_SIGN));
207         assertEquals(flags2, flags2
208                 & (~NTLMUtilities.FLAG_NEGOTIATE_ALWAYS_SIGN));
209         assertEquals("05820000", asHex(ByteUtilities.writeInt(flags)));
210 
211         byte[] testFlags = ByteUtilities.asByteArray("7F808182");
212         assertEquals("7f808182", asHex(testFlags));
213         ByteUtilities.changeByteEndianess(testFlags, 0, 4);
214         assertEquals("807f8281", asHex(testFlags));
215         ByteUtilities.changeByteEndianess(testFlags, 0, 4);
216         ByteUtilities.changeWordEndianess(testFlags, 0, 4);
217         assertEquals("8281807f", asHex(testFlags));
218     }
219 
220     /**
221      * Tests response computing.
222      * WARNING: Will silently fail if no MD4 digest provider is available.
223      */
224     public void testResponses() throws Exception {
225         try {
226             MessageDigest.getInstance("MD4");
227         } catch (NoSuchAlgorithmException ex) {
228             logger.warn("No MD4 digest provider found !");
229             return;
230         }
231 
232         String LMResponse = "c337cd5cbd44fc9782a667af6d427c6de67c20c2d3e77c56";
233 
234         assertEquals(LMResponse, asHex(NTLMResponses.getLMResponse("SecREt01",
235                 ByteUtilities.asByteArray("0123456789abcdef"))));
236 
237         String NTLMResponse = "25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6";
238 
239         assertEquals(NTLMResponse, asHex(NTLMResponses.getNTLMResponse(
240                 "SecREt01", ByteUtilities.asByteArray("0123456789abcdef"))));
241 
242         String LMv2Response = "d6e6152ea25d03b7c6ba6629c2d6aaf0ffffff0011223344";
243 
244         assertEquals(LMv2Response, asHex(NTLMResponses.getLMv2Response(
245                 "DOMAIN", "user", "SecREt01", ByteUtilities
246                         .asByteArray("0123456789abcdef"), ByteUtilities
247                         .asByteArray("ffffff0011223344"))));
248 
249         String NTLM2Response = "10d550832d12b2ccb79d5ad1f4eed3df82aca4c3681dd455";
250 
251         assertEquals(NTLM2Response, asHex(NTLMResponses
252                 .getNTLM2SessionResponse("SecREt01", ByteUtilities
253                         .asByteArray("0123456789abcdef"), ByteUtilities
254                         .asByteArray("ffffff0011223344"))));
255 
256         String NTLMv2Response = "cbabbca713eb795d04c97abc01ee4983"
257                 + "01010000000000000090d336b734c301"
258                 + "ffffff00112233440000000002000c00"
259                 + "44004f004d00410049004e0001000c00"
260                 + "53004500520056004500520004001400"
261                 + "64006f006d00610069006e002e006300"
262                 + "6f006d00030022007300650072007600"
263                 + "650072002e0064006f006d0061006900"
264                 + "6e002e0063006f006d00000000000000" + "0000";
265 
266         String targetInformation = "02000c0044004f004d00410049004e00"
267                 + "01000c00530045005200560045005200"
268                 + "0400140064006f006d00610069006e00"
269                 + "2e0063006f006d000300220073006500"
270                 + "72007600650072002e0064006f006d00"
271                 + "610069006e002e0063006f006d000000" + "0000";
272 
273         assertEquals(NTLMv2Response, asHex(NTLMResponses.getNTLMv2Response(
274                 "DOMAIN", "user", "SecREt01", ByteUtilities
275                         .asByteArray(targetInformation), ByteUtilities
276                         .asByteArray("0123456789abcdef"), ByteUtilities
277                         .asByteArray("ffffff0011223344"), 1055844000000L)));
278     }
279 }