/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.security.sso.ntlm.internal;
import com.liferay.portal.kernel.io.BigEndianCodec;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.module.configuration.ConfigurationException;
import com.liferay.portal.kernel.module.configuration.ConfigurationProvider;
import com.liferay.portal.kernel.security.SecureRandomUtil;
import com.liferay.portal.kernel.security.auth.CompanyThreadLocal;
import com.liferay.portal.kernel.settings.CompanyServiceSettingsLocator;
import com.liferay.portal.security.sso.ntlm.configuration.NtlmConfiguration;
import com.liferay.portal.security.sso.ntlm.constants.NtlmConstants;
import com.liferay.portal.security.sso.ntlm.internal.msrpc.NetrServerAuthenticate3;
import com.liferay.portal.security.sso.ntlm.internal.msrpc.NetrServerReqChallenge;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import jcifs.dcerpc.DcerpcHandle;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.util.HMACT64;
import jcifs.util.MD4;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* @author Michael C. Han
*/
@Component(
configurationPid = "com.liferay.portal.security.sso.ntlm.configuration.NtlmConfiguration",
immediate = true, service = NetlogonConnectionManager.class
)
public class NetlogonConnectionManagerImpl
implements NetlogonConnectionManager {
@Override
public NetlogonConnection connect(
String domainController, String domainControllerName,
NtlmServiceAccount ntlmServiceAccount)
throws IOException, NoSuchAlgorithmException, NtlmLogonException {
NtlmPasswordAuthentication ntlmPasswordAuthentication =
new NtlmPasswordAuthentication(
null, ntlmServiceAccount.getAccount(),
ntlmServiceAccount.getPassword());
String endpoint = "ncacn_np:" + domainController + "[\\PIPE\\NETLOGON]";
DcerpcHandle dcerpcHandle = DcerpcHandle.getHandle(
endpoint, ntlmPasswordAuthentication);
dcerpcHandle.bind();
byte[] clientChallenge = new byte[8];
BigEndianCodec.putLong(clientChallenge, 0, SecureRandomUtil.nextLong());
NetrServerReqChallenge netrServerReqChallenge =
new NetrServerReqChallenge(
domainControllerName, ntlmServiceAccount.getComputerName(),
clientChallenge, new byte[8]);
dcerpcHandle.sendrecv(netrServerReqChallenge);
MD4 md4 = new MD4();
md4.update(ntlmServiceAccount.getPassword().getBytes("UTF-16LE"));
byte[] sessionKey = computeSessionKey(
md4.digest(), clientChallenge,
netrServerReqChallenge.getServerChallenge());
byte[] clientCredential =
NetlogonCredentialUtil.computeNetlogonCredential(
clientChallenge, sessionKey);
NetrServerAuthenticate3 netrServerAuthenticate3 =
new NetrServerAuthenticate3(
domainControllerName, ntlmServiceAccount.getAccountName(), 2,
ntlmServiceAccount.getComputerName(), clientCredential,
new byte[8], getNegotiateFlags());
dcerpcHandle.sendrecv(netrServerAuthenticate3);
byte[] serverCredential =
NetlogonCredentialUtil.computeNetlogonCredential(
netrServerReqChallenge.getServerChallenge(), sessionKey);
if (!Arrays.equals(
serverCredential,
netrServerAuthenticate3.getServerCredential())) {
throw new NtlmLogonException("Session key negotiation failed");
}
NetlogonConnection netLogonConnection = new NetlogonConnection(
clientCredential, sessionKey);
netLogonConnection.setDcerpcHandle(dcerpcHandle);
return netLogonConnection;
}
protected byte[] computeSessionKey(
byte[] sharedSecret, byte[] clientChallenge, byte[] serverChallenge)
throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] zeroes = {0, 0, 0, 0};
messageDigest.update(zeroes, 0, 4);
messageDigest.update(clientChallenge, 0, 8);
messageDigest.update(serverChallenge, 0, 8);
HMACT64 hmact64 = new HMACT64(sharedSecret);
hmact64.update(messageDigest.digest());
return hmact64.digest();
}
protected int getNegotiateFlags() {
int negotiateFlags = 0x600FFFFF;
try {
NtlmConfiguration ntlmConfiguration =
_configurationProvider.getConfiguration(
NtlmConfiguration.class,
new CompanyServiceSettingsLocator(
CompanyThreadLocal.getCompanyId(),
NtlmConstants.SERVICE_NAME));
String negotiateFlagsString = ntlmConfiguration.negotiateFlags();
if (negotiateFlagsString.startsWith("0x")) {
negotiateFlags = Integer.valueOf(
negotiateFlagsString.substring(2), 16);
}
}
catch (ConfigurationException ce) {
_log.error("Unable to get NTLM configuration", ce);
}
return negotiateFlags;
}
@Reference(unbind = "-")
protected void setConfigurationProvider(
ConfigurationProvider configurationProvider) {
_configurationProvider = configurationProvider;
}
private static final Log _log = LogFactoryUtil.getLog(
NetlogonConnectionManagerImpl.class);
private ConfigurationProvider _configurationProvider;
}