001    /**
002     * Copyright (c) 2000-present 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.kernel.io.BigEndianCodec;
018    import com.liferay.portal.kernel.security.SecureRandomUtil;
019    import com.liferay.portal.security.ntlm.msrpc.NetlogonAuthenticator;
020    import com.liferay.portal.security.ntlm.msrpc.NetrServerAuthenticate3;
021    import com.liferay.portal.security.ntlm.msrpc.NetrServerReqChallenge;
022    import com.liferay.portal.util.PropsValues;
023    
024    import java.io.IOException;
025    
026    import java.security.MessageDigest;
027    import java.security.NoSuchAlgorithmException;
028    
029    import java.util.Arrays;
030    
031    import jcifs.dcerpc.DcerpcHandle;
032    
033    import jcifs.smb.NtlmPasswordAuthentication;
034    
035    import jcifs.util.DES;
036    import jcifs.util.Encdec;
037    import jcifs.util.HMACT64;
038    import jcifs.util.MD4;
039    
040    /**
041     * @author Michael C. Han
042     */
043    public class NetlogonConnection {
044    
045            public NetlogonAuthenticator computeNetlogonAuthenticator() {
046                    int timestamp = (int)System.currentTimeMillis();
047    
048                    int input = Encdec.dec_uint32le(_clientCredential, 0) + timestamp;
049    
050                    Encdec.enc_uint32le(input, _clientCredential, 0);
051    
052                    byte[] credential = computeNetlogonCredential(
053                            _clientCredential, _sessionKey);
054    
055                    return new NetlogonAuthenticator(credential, timestamp);
056            }
057    
058            public void connect(
059                            String domainController, String domainControllerName,
060                            NtlmServiceAccount ntlmServiceAccount)
061                    throws IOException, NoSuchAlgorithmException, NtlmLogonException {
062    
063                    NtlmPasswordAuthentication ntlmPasswordAuthentication =
064                            new NtlmPasswordAuthentication(
065                                    null, ntlmServiceAccount.getAccount(),
066                                    ntlmServiceAccount.getPassword());
067    
068                    String endpoint = "ncacn_np:" + domainController + "[\\PIPE\\NETLOGON]";
069    
070                    DcerpcHandle dcerpcHandle = DcerpcHandle.getHandle(
071                            endpoint, ntlmPasswordAuthentication);
072    
073                    setDcerpcHandle(dcerpcHandle);
074    
075                    dcerpcHandle.bind();
076    
077                    byte[] clientChallenge = new byte[8];
078    
079                    BigEndianCodec.putLong(clientChallenge, 0, SecureRandomUtil.nextLong());
080    
081                    NetrServerReqChallenge netrServerReqChallenge =
082                            new NetrServerReqChallenge(
083                                    domainControllerName, ntlmServiceAccount.getComputerName(),
084                                    clientChallenge, new byte[8]);
085    
086                    dcerpcHandle.sendrecv(netrServerReqChallenge);
087    
088                    MD4 md4 = new MD4();
089    
090                    md4.update(ntlmServiceAccount.getPassword().getBytes("UTF-16LE"));
091    
092                    byte[] sessionKey = computeSessionKey(
093                            md4.digest(), clientChallenge,
094                            netrServerReqChallenge.getServerChallenge());
095    
096                    byte[] clientCredential = computeNetlogonCredential(
097                            clientChallenge, sessionKey);
098    
099                    NetrServerAuthenticate3 netrServerAuthenticate3 =
100                            new NetrServerAuthenticate3(
101                                    domainControllerName, ntlmServiceAccount.getAccountName(), 2,
102                                    ntlmServiceAccount.getComputerName(), clientCredential,
103                                    new byte[8], _NEGOTIATE_FLAGS);
104    
105                    dcerpcHandle.sendrecv(netrServerAuthenticate3);
106    
107                    byte[] serverCredential = computeNetlogonCredential(
108                            netrServerReqChallenge.getServerChallenge(), sessionKey);
109    
110                    if (!Arrays.equals(
111                                    serverCredential,
112                                    netrServerAuthenticate3.getServerCredential())) {
113    
114                            throw new NtlmLogonException("Session key negotiation failed");
115                    }
116    
117                    _clientCredential = clientCredential;
118                    _sessionKey = sessionKey;
119            }
120    
121            public void disconnect() throws IOException {
122                    if (_dcerpcHandle != null) {
123                            _dcerpcHandle.close();
124                    }
125            }
126    
127            public byte[] getClientCredential() {
128                    return _clientCredential;
129            }
130    
131            public DcerpcHandle getDcerpcHandle() {
132                    return _dcerpcHandle;
133            }
134    
135            public byte[] getSessionKey() {
136                    return _sessionKey;
137            }
138    
139            public void setDcerpcHandle(DcerpcHandle dcerpcHandle) {
140                    _dcerpcHandle = dcerpcHandle;
141            }
142    
143            protected byte[] computeNetlogonCredential(
144                    byte[] input, byte[] sessionKey) {
145    
146                    byte[] k1 = new byte[7];
147                    byte[] k2 = new byte[7];
148    
149                    System.arraycopy(sessionKey, 0, k1, 0, 7);
150                    System.arraycopy(sessionKey, 7, k2, 0, 7);
151    
152                    DES k3 = new DES(k1);
153                    DES k4 = new DES(k2);
154    
155                    byte[] output1 = new byte[8];
156                    byte[] output2 = new byte[8];
157    
158                    k3.encrypt(input, output1);
159                    k4.encrypt(output1, output2);
160    
161                    return output2;
162            }
163    
164            protected byte[] computeSessionKey(
165                            byte[] sharedSecret, byte[] clientChallenge, byte[] serverChallenge)
166                    throws NoSuchAlgorithmException {
167    
168                    MessageDigest messageDigest = MessageDigest.getInstance("MD5");
169    
170                    byte[] zeroes = {0, 0, 0, 0};
171    
172                    messageDigest.update(zeroes, 0, 4);
173                    messageDigest.update(clientChallenge, 0, 8);
174                    messageDigest.update(serverChallenge, 0, 8);
175    
176                    HMACT64 hmact64 = new HMACT64(sharedSecret);
177    
178                    hmact64.update(messageDigest.digest());
179    
180                    return hmact64.digest();
181            }
182    
183            private static final int _NEGOTIATE_FLAGS;
184    
185            static {
186                    String negotiateFlags = PropsValues.NTLM_AUTH_NEGOTIATE_FLAGS;
187    
188                    if (negotiateFlags.startsWith("0x")) {
189                            _NEGOTIATE_FLAGS = Integer.valueOf(negotiateFlags.substring(2), 16);
190                    }
191                    else {
192                            _NEGOTIATE_FLAGS = 0x600FFFFF;
193                    }
194            }
195    
196            private byte[] _clientCredential;
197            private DcerpcHandle _dcerpcHandle;
198            private byte[] _sessionKey;
199    
200    }