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