001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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    
021    import java.io.IOException;
022    
023    import java.security.MessageDigest;
024    import java.security.NoSuchAlgorithmException;
025    import java.security.SecureRandom;
026    
027    import java.util.Arrays;
028    
029    import jcifs.dcerpc.DcerpcHandle;
030    
031    import jcifs.smb.NtlmPasswordAuthentication;
032    
033    import jcifs.util.DES;
034    import jcifs.util.Encdec;
035    import jcifs.util.HMACT64;
036    import jcifs.util.MD4;
037    
038    /**
039     * @author Michael C. Han
040     */
041    public class NetlogonConnection {
042    
043            public NetlogonAuthenticator computeNetlogonAuthenticator() {
044                    int timestamp = (int)System.currentTimeMillis();
045    
046                    int input = Encdec.dec_uint32le(_clientCredential, 0) + timestamp;
047    
048                    Encdec.enc_uint32le(input, _clientCredential, 0);
049    
050                    byte[] credential = computeNetlogonCredential(
051                            _clientCredential, _sessionKey);
052    
053                    return new NetlogonAuthenticator(credential, timestamp);
054            }
055    
056            public void connect(
057                            String domainController, String domainControllerName,
058                            NtlmServiceAccount ntlmServiceAccount, SecureRandom secureRandom)
059                    throws IOException, NtlmLogonException, NoSuchAlgorithmException {
060    
061                    NtlmPasswordAuthentication ntlmPasswordAuthentication =
062                            new NtlmPasswordAuthentication(
063                                    null, ntlmServiceAccount.getAccount(),
064                                    ntlmServiceAccount.getPassword());
065    
066                    String endpoint = "ncacn_np:" + domainController + "[\\PIPE\\NETLOGON]";
067    
068                    DcerpcHandle dcerpcHandle = DcerpcHandle.getHandle(
069                            endpoint, ntlmPasswordAuthentication);
070    
071                    setDcerpcHandle(dcerpcHandle);
072    
073                    dcerpcHandle.bind();
074    
075                    byte[] clientChallenge = new byte[8];
076    
077                    secureRandom.nextBytes(clientChallenge);
078    
079                    NetrServerReqChallenge netrServerReqChallenge =
080                            new NetrServerReqChallenge(
081                                    domainControllerName, ntlmServiceAccount.getComputerName(),
082                                    clientChallenge, new byte[8]);
083    
084                    dcerpcHandle.sendrecv(netrServerReqChallenge);
085    
086                    MD4 md4 = new MD4();
087    
088                    md4.update(ntlmServiceAccount.getPassword().getBytes("UTF-16LE"));
089    
090                    byte[] sessionKey = computeSessionKey(
091                            md4.digest(), clientChallenge,
092                            netrServerReqChallenge.getServerChallenge());
093    
094                    byte[] clientCredential = computeNetlogonCredential(
095                            clientChallenge, sessionKey);
096    
097                    NetrServerAuthenticate3 netrServerAuthenticate3 =
098                            new NetrServerAuthenticate3(
099                                    domainControllerName, ntlmServiceAccount.getAccountName(), 2,
100                                    ntlmServiceAccount.getComputerName(), clientCredential,
101                                    new byte[8], 0xFFFFFFFF);
102    
103                    dcerpcHandle.sendrecv(netrServerAuthenticate3);
104    
105                    byte[] serverCredential = computeNetlogonCredential(
106                            netrServerReqChallenge.getServerChallenge(), sessionKey);
107    
108                    if (!Arrays.equals(
109                                    serverCredential,
110                                    netrServerAuthenticate3.getServerCredential())) {
111    
112                            throw new NtlmLogonException("Session key negotiation failed");
113                    }
114    
115                    _clientCredential = clientCredential;
116                    _sessionKey = sessionKey;
117            }
118    
119            public void disconnect() throws IOException {
120                    if (_dcerpcHandle != null) {
121                            _dcerpcHandle.close();
122                    }
123            }
124    
125            public byte[] getClientCredential() {
126                    return _clientCredential;
127            }
128    
129            public DcerpcHandle getDcerpcHandle() {
130                    return _dcerpcHandle;
131            }
132    
133            public byte[] getSessionKey() {
134                    return _sessionKey;
135            }
136    
137            public void setDcerpcHandle(DcerpcHandle dcerpcHandle) {
138                    _dcerpcHandle = dcerpcHandle;
139            }
140    
141            protected byte[] computeNetlogonCredential(
142                    byte[] input, byte[] sessionKey) {
143    
144                    byte[] k1 = new byte[7];
145                    byte[] k2 = new byte[7];
146    
147                    System.arraycopy(sessionKey, 0, k1, 0, 7);
148                    System.arraycopy(sessionKey, 7, k2, 0, 7);
149    
150                    DES k3 = new DES(k1);
151                    DES k4 = new DES(k2);
152    
153                    byte[] output1 = new byte[8];
154                    byte[] output2 = new byte[8];
155    
156                    k3.encrypt(input, output1);
157                    k4.encrypt(output1, output2);
158    
159                    return output2;
160            }
161    
162            protected byte[] computeSessionKey(
163                            byte[] sharedSecret, byte[] clientChallenge, byte[] serverChallenge)
164                    throws NoSuchAlgorithmException {
165    
166                    MessageDigest messageDigest = MessageDigest.getInstance("MD5");
167    
168                    byte[] zeroes = {0, 0, 0, 0};
169    
170                    messageDigest.update(zeroes, 0, 4);
171                    messageDigest.update(clientChallenge, 0, 8);
172                    messageDigest.update(serverChallenge, 0, 8);
173    
174                    HMACT64 hmact64 = new HMACT64(sharedSecret);
175    
176                    hmact64.update(messageDigest.digest());
177    
178                    return hmact64.digest();
179            }
180    
181            private byte[] _clientCredential;
182            private DcerpcHandle _dcerpcHandle;
183            private byte[] _sessionKey;
184    
185    }