001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.proxy;
021
022import static org.apache.mina.proxy.utils.ByteUtilities.asHex;
023import static org.junit.Assert.assertEquals;
024
025import java.io.PrintWriter;
026import java.io.UnsupportedEncodingException;
027import java.security.MessageDigest;
028import java.security.NoSuchAlgorithmException;
029
030import org.apache.mina.proxy.handlers.http.ntlm.NTLMResponses;
031import org.apache.mina.proxy.handlers.http.ntlm.NTLMUtilities;
032import org.apache.mina.proxy.utils.ByteUtilities;
033import org.junit.Test;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037/**
038 * NTLMTest.java - JUNIT tests of the NTLM authentication mechanism.
039 * 
040 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
041 * @since MINA 2.0.0-M3
042 */
043public class NTLMTest {
044    private final static Logger logger = LoggerFactory.getLogger(NTLMTest.class);
045
046    /**
047     * Tests bytes manipulations.
048     */
049    @Test
050    public void testEncoding() throws UnsupportedEncodingException {
051        assertEquals("d204", asHex(ByteUtilities.writeShort((short) 1234)));
052        assertEquals("d2040000", asHex(ByteUtilities.writeInt(1234)));
053        assertEquals("01000000", asHex(ByteUtilities.writeInt((short) 1)));
054        assertEquals("4e544c4d53535000", asHex(NTLMUtilities.NTLM_SIGNATURE));
055
056        assertEquals("680065006c006c006f00", asHex(ByteUtilities.getUTFStringAsByteArray("hello")));
057        assertEquals("48454c4c4f", asHex(ByteUtilities.getOEMStringAsByteArray("HELLO")));
058    }
059
060    /**
061     * Tests conversions to and from network byte order.
062     */
063    @Test
064    public void testMethods() {
065        byte[] buf = new byte[4];
066        ByteUtilities.intToNetworkByteOrder(1234, buf, 0, 4);
067        assertEquals("000004d2", asHex(buf));
068        assertEquals(1234, ByteUtilities.networkByteOrderToInt(buf, 0, 4));
069    }
070
071    /**
072     * Tests security buffers.
073     */
074    @Test
075    public void testSecurityBuffer() {
076        byte[] secBuf = new byte[8];
077        NTLMUtilities.writeSecurityBuffer((short) 1234, (short) 1234, 4321, secBuf, 0);
078        assertEquals("d204d204e1100000", asHex(secBuf));
079    }
080
081    /**
082     * Tests creating a type 1 message.
083     */
084    @Test
085    public void testType1Message() {
086        int customFlags = NTLMUtilities.FLAG_NEGOTIATE_UNICODE | NTLMUtilities.FLAG_NEGOTIATE_OEM
087                | NTLMUtilities.FLAG_NEGOTIATE_NTLM | NTLMUtilities.FLAG_REQUEST_SERVER_AUTH_REALM
088                | NTLMUtilities.FLAG_NEGOTIATE_DOMAIN_SUPPLIED | NTLMUtilities.FLAG_NEGOTIATE_WORKSTATION_SUPPLIED;
089
090        byte[] osVer = new byte[8];
091        NTLMUtilities.writeOSVersion((byte) 5, (byte) 0, (short) 2195, osVer, 0);
092
093        String msgType1 = asHex(NTLMUtilities.createType1Message("WORKSTATION", "DOMAIN", customFlags, osVer));
094        assertEquals("4e544c4d53535000010000000732000006000600330000000b000b0028000000"
095                + "050093080000000f574f524b53544154494f4e444f4d41494e", msgType1);
096
097        assertEquals("050093080000000f", asHex(osVer));
098
099        //Microsoft Windows XP [version 5.1.2600]
100        String os = System.getProperty("os.name");
101        if (os != null && os.toUpperCase().contains("WINDOWS") && "5.1".equals(System.getProperty("os.version"))) {
102            String hex = asHex(NTLMUtilities.getOsVersion());
103            assertEquals("0501", hex.substring(0, 4));
104            assertEquals(16, hex.length());
105        }
106    }
107
108    /**
109     * Tests creating a type 3 message.
110     * WARNING: Will silently fail if no MD4 digest provider is available.
111     */
112    @Test
113    public void testType3Message() throws Exception {
114        try {
115            MessageDigest.getInstance("MD4");
116        } catch (NoSuchAlgorithmException ex) {
117            logger.warn("No MD4 digest provider found !");
118            return;
119        }
120
121        int flags = 0x00000001 | 0x00000200 | 0x00010000 | 0x00800000;
122        String msg = "4e544c4d53535000020000000c000c003000000001028100"
123                + "0123456789abcdef0000000000000000620062003c000000"
124                + "44004f004d00410049004e0002000c0044004f004d004100"
125                + "49004e0001000c0053004500520056004500520004001400"
126                + "64006f006d00610069006e002e0063006f006d0003002200"
127                + "7300650072007600650072002e0064006f006d0061006900" + "6e002e0063006f006d0000000000";
128
129        byte[] challengePacket = ByteUtilities.asByteArray(msg);
130        int serverFlags = NTLMUtilities.extractFlagsFromType2Message(challengePacket);
131        assertEquals(flags, serverFlags);
132
133        NTLMUtilities.printTargetInformationBlockFromType2Message(challengePacket, serverFlags, new PrintWriter(
134                System.out, true));
135
136        byte[] osVer = new byte[8];
137        NTLMUtilities.writeOSVersion((byte) 5, (byte) 0, (short) 2195, osVer, 0);
138
139        byte[] challenge = NTLMUtilities.extractChallengeFromType2Message(challengePacket);
140        assertEquals("0123456789abcdef", asHex(challenge));
141
142        String expectedTargetInfoBlock = "02000c0044004f004d00410049004e00" + "01000c00530045005200560045005200"
143                + "0400140064006f006d00610069006e00" + "2e0063006f006d000300220073006500"
144                + "72007600650072002e0064006f006d00" + "610069006e002e0063006f006d000000" + "0000";
145
146        byte[] targetInfo = NTLMUtilities.extractTargetInfoFromType2Message(challengePacket, null);
147        assertEquals(expectedTargetInfoBlock, asHex(targetInfo));
148
149        assertEquals("DOMAIN",
150                NTLMUtilities.extractTargetNameFromType2Message(challengePacket, new Integer(serverFlags)));
151
152        serverFlags = 0x00000001 | 0x00000200;
153        String msgType3 = asHex(NTLMUtilities.createType3Message("user", "SecREt01", challenge, "DOMAIN",
154                "WORKSTATION", serverFlags, null));
155
156        String expected = "4e544c4d5353500003000000180018006a00000018001800"
157                + "820000000c000c0040000000080008004c00000016001600"
158                + "54000000000000009a0000000102000044004f004d004100"
159                + "49004e00750073006500720057004f0052004b0053005400"
160                + "4100540049004f004e00c337cd5cbd44fc9782a667af6d42"
161                + "7c6de67c20c2d3e77c5625a98c1c31e81847466b29b2df46" + "80f39958fb8c213a9cc6";
162        assertEquals(expected, msgType3);
163    }
164
165    /**
166     * Tests flags manipulations.
167     */
168    @Test
169    public void testFlags() {
170        int flags = NTLMUtilities.FLAG_NEGOTIATE_UNICODE | NTLMUtilities.FLAG_REQUEST_SERVER_AUTH_REALM
171                | NTLMUtilities.FLAG_NEGOTIATE_NTLM | NTLMUtilities.FLAG_NEGOTIATE_ALWAYS_SIGN;
172
173        int flags2 = NTLMUtilities.FLAG_NEGOTIATE_UNICODE | NTLMUtilities.FLAG_REQUEST_SERVER_AUTH_REALM
174                | NTLMUtilities.FLAG_NEGOTIATE_NTLM;
175
176        assertEquals(flags2, flags & (~NTLMUtilities.FLAG_NEGOTIATE_ALWAYS_SIGN));
177        assertEquals(flags2, flags2 & (~NTLMUtilities.FLAG_NEGOTIATE_ALWAYS_SIGN));
178        assertEquals("05820000", asHex(ByteUtilities.writeInt(flags)));
179
180        byte[] testFlags = ByteUtilities.asByteArray("7F808182");
181        assertEquals("7f808182", asHex(testFlags));
182        ByteUtilities.changeByteEndianess(testFlags, 0, 4);
183        assertEquals("807f8281", asHex(testFlags));
184        ByteUtilities.changeByteEndianess(testFlags, 0, 4);
185        ByteUtilities.changeWordEndianess(testFlags, 0, 4);
186        assertEquals("8281807f", asHex(testFlags));
187    }
188
189    /**
190     * Tests response computing.
191     * WARNING: Will silently fail if no MD4 digest provider is available.
192     */
193    @Test
194    public void testResponses() throws Exception {
195        try {
196            MessageDigest.getInstance("MD4");
197        } catch (NoSuchAlgorithmException ex) {
198            logger.warn("No MD4 digest provider found !");
199            return;
200        }
201
202        String LMResponse = "c337cd5cbd44fc9782a667af6d427c6de67c20c2d3e77c56";
203
204        assertEquals(LMResponse,
205                asHex(NTLMResponses.getLMResponse("SecREt01", ByteUtilities.asByteArray("0123456789abcdef"))));
206
207        String NTLMResponse = "25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6";
208
209        assertEquals(NTLMResponse,
210                asHex(NTLMResponses.getNTLMResponse("SecREt01", ByteUtilities.asByteArray("0123456789abcdef"))));
211
212        String LMv2Response = "d6e6152ea25d03b7c6ba6629c2d6aaf0ffffff0011223344";
213
214        assertEquals(
215                LMv2Response,
216                asHex(NTLMResponses.getLMv2Response("DOMAIN", "user", "SecREt01",
217                        ByteUtilities.asByteArray("0123456789abcdef"), ByteUtilities.asByteArray("ffffff0011223344"))));
218
219        String NTLM2Response = "10d550832d12b2ccb79d5ad1f4eed3df82aca4c3681dd455";
220
221        assertEquals(NTLM2Response, asHex(NTLMResponses.getNTLM2SessionResponse("SecREt01",
222                ByteUtilities.asByteArray("0123456789abcdef"), ByteUtilities.asByteArray("ffffff0011223344"))));
223
224        String NTLMv2Response = "cbabbca713eb795d04c97abc01ee4983" + "01010000000000000090d336b734c301"
225                + "ffffff00112233440000000002000c00" + "44004f004d00410049004e0001000c00"
226                + "53004500520056004500520004001400" + "64006f006d00610069006e002e006300"
227                + "6f006d00030022007300650072007600" + "650072002e0064006f006d0061006900"
228                + "6e002e0063006f006d00000000000000" + "0000";
229
230        String targetInformation = "02000c0044004f004d00410049004e00" + "01000c00530045005200560045005200"
231                + "0400140064006f006d00610069006e00" + "2e0063006f006d000300220073006500"
232                + "72007600650072002e0064006f006d00" + "610069006e002e0063006f006d000000" + "0000";
233
234        assertEquals(NTLMv2Response, asHex(NTLMResponses.getNTLMv2Response("DOMAIN", "user", "SecREt01",
235                ByteUtilities.asByteArray(targetInformation), ByteUtilities.asByteArray("0123456789abcdef"),
236                ByteUtilities.asByteArray("ffffff0011223344"), 1055844000000L)));
237    }
238}