001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.security.ntlm;
016    
017    import com.liferay.portal.security.ntlm.msrpc.NetlogonAuthenticator;
018    import com.liferay.portal.security.ntlm.msrpc.NetrServerAuthenticate3;
019    import com.liferay.portal.security.ntlm.msrpc.NetrServerReqChallenge;
020    import com.liferay.portal.util.PropsValues;
021    
022    import java.io.IOException;
023    
024    import java.security.MessageDigest;
025    import java.security.NoSuchAlgorithmException;
026    import java.security.SecureRandom;
027    
028    import java.util.Arrays;
029    
030    import jcifs.dcerpc.DcerpcHandle;
031    
032    import jcifs.smb.NtlmPasswordAuthentication;
033    
034    import jcifs.util.DES;
035    import jcifs.util.Encdec;
036    import jcifs.util.HMACT64;
037    import jcifs.util.MD4;
038    
039    /**
040     * @author Michael C. Han
041     */
042    public class NetlogonConnection {
043    
044            public NetlogonConnection() {
045                    if (_negotiateFlags == 0) {
046                            String negotiateFlags = PropsValues.NTLM_AUTH_NEGOTIATE_FLAGS;
047    
048                            if (negotiateFlags.startsWith("0x")) {
049                                    _negotiateFlags = Integer.valueOf(
050                                            negotiateFlags.substring(2), 16);
051                            }
052                            else {
053                                    _negotiateFlags = 0x600FFFFF;
054                            }
055                    }
056            }
057    
058            public NetlogonAuthenticator computeNetlogonAuthenticator() {
059                    int timestamp = (int)System.currentTimeMillis();
060    
061                    int input = Encdec.dec_uint32le(_clientCredential, 0) + timestamp;
062    
063                    Encdec.enc_uint32le(input, _clientCredential, 0);
064    
065                    byte[] credential = computeNetlogonCredential(
066                            _clientCredential, _sessionKey);
067    
068                    return new NetlogonAuthenticator(credential, timestamp);
069            }
070    
071            public void connect(
072                            String domainController, String domainControllerName,
073                            NtlmServiceAccount ntlmServiceAccount, SecureRandom secureRandom)
074                    throws IOException, NtlmLogonException, NoSuchAlgorithmException {
075    
076                    NtlmPasswordAuthentication ntlmPasswordAuthentication =
077                            new NtlmPasswordAuthentication(
078                                    null, ntlmServiceAccount.getAccount(),
079                                    ntlmServiceAccount.getPassword());
080    
081                    String endpoint = "ncacn_np:" + domainController + "[\\PIPE\\NETLOGON]";
082    
083                    DcerpcHandle dcerpcHandle = DcerpcHandle.getHandle(
084                            endpoint, ntlmPasswordAuthentication);
085    
086                    setDcerpcHandle(dcerpcHandle);
087    
088                    dcerpcHandle.bind();
089    
090                    byte[] clientChallenge = new byte[8];
091    
092                    secureRandom.nextBytes(clientChallenge);
093    
094                    NetrServerReqChallenge netrServerReqChallenge =
095                            new NetrServerReqChallenge(
096                                    domainControllerName, ntlmServiceAccount.getComputerName(),
097                                    clientChallenge, new byte[8]);
098    
099                    dcerpcHandle.sendrecv(netrServerReqChallenge);
100    
101                    MD4 md4 = new MD4();
102    
103                    md4.update(ntlmServiceAccount.getPassword().getBytes("UTF-16LE"));
104    
105                    byte[] sessionKey = computeSessionKey(
106                            md4.digest(), clientChallenge,
107                            netrServerReqChallenge.getServerChallenge());
108    
109                    byte[] clientCredential = computeNetlogonCredential(
110                            clientChallenge, sessionKey);
111    
112                    NetrServerAuthenticate3 netrServerAuthenticate3 =
113                            new NetrServerAuthenticate3(
114                                    domainControllerName, ntlmServiceAccount.getAccountName(), 2,
115                                    ntlmServiceAccount.getComputerName(), clientCredential,
116                                    new byte[8], _negotiateFlags);
117    
118                    dcerpcHandle.sendrecv(netrServerAuthenticate3);
119    
120                    byte[] serverCredential = computeNetlogonCredential(
121                            netrServerReqChallenge.getServerChallenge(), sessionKey);
122    
123                    if (!Arrays.equals(
124                                    serverCredential,
125                                    netrServerAuthenticate3.getServerCredential())) {
126    
127                            throw new NtlmLogonException("Session key negotiation failed");
128                    }
129    
130                    _clientCredential = clientCredential;
131                    _sessionKey = sessionKey;
132            }
133    
134            public void disconnect() throws IOException {
135                    if (_dcerpcHandle != null) {
136                            _dcerpcHandle.close();
137                    }
138            }
139    
140            public byte[] getClientCredential() {
141                    return _clientCredential;
142            }
143    
144            public DcerpcHandle getDcerpcHandle() {
145                    return _dcerpcHandle;
146            }
147    
148            public byte[] getSessionKey() {
149                    return _sessionKey;
150            }
151    
152            public void setDcerpcHandle(DcerpcHandle dcerpcHandle) {
153                    _dcerpcHandle = dcerpcHandle;
154            }
155    
156            protected byte[] computeNetlogonCredential(
157                    byte[] input, byte[] sessionKey) {
158    
159                    byte[] k1 = new byte[7];
160                    byte[] k2 = new byte[7];
161    
162                    System.arraycopy(sessionKey, 0, k1, 0, 7);
163                    System.arraycopy(sessionKey, 7, k2, 0, 7);
164    
165                    DES k3 = new DES(k1);
166                    DES k4 = new DES(k2);
167    
168                    byte[] output1 = new byte[8];
169                    byte[] output2 = new byte[8];
170    
171                    k3.encrypt(input, output1);
172                    k4.encrypt(output1, output2);
173    
174                    return output2;
175            }
176    
177            protected byte[] computeSessionKey(
178                            byte[] sharedSecret, byte[] clientChallenge, byte[] serverChallenge)
179                    throws NoSuchAlgorithmException {
180    
181                    MessageDigest messageDigest = MessageDigest.getInstance("MD5");
182    
183                    byte[] zeroes = {0, 0, 0, 0};
184    
185                    messageDigest.update(zeroes, 0, 4);
186                    messageDigest.update(clientChallenge, 0, 8);
187                    messageDigest.update(serverChallenge, 0, 8);
188    
189                    HMACT64 hmact64 = new HMACT64(sharedSecret);
190    
191                    hmact64.update(messageDigest.digest());
192    
193                    return hmact64.digest();
194            }
195    
196            private static int _negotiateFlags;
197    
198            private byte[] _clientCredential;
199            private DcerpcHandle _dcerpcHandle;
200            private byte[] _sessionKey;
201    
202    }