704 lines
16 KiB
C++
704 lines
16 KiB
C++
//
|
|
// Implementation of ICloneSecurityPrincipal::Connect
|
|
//
|
|
// sburns 5-10-99
|
|
|
|
|
|
|
|
#include "headers.hxx"
|
|
#include "resource.h"
|
|
#include "common.hpp"
|
|
#include "implmain.hpp"
|
|
|
|
|
|
|
|
CloneSecurityPrincipal::Connection::Connection()
|
|
:
|
|
dstComputer(0),
|
|
dstDomainSamHandle(INVALID_HANDLE_VALUE),
|
|
dstDsBindHandle(INVALID_HANDLE_VALUE),
|
|
m_pldap(0),
|
|
srcComputer(0),
|
|
srcDcDnsName(),
|
|
srcDomainSamHandle(INVALID_HANDLE_VALUE)
|
|
{
|
|
LOG_CTOR(CloneSecurityPrincipal::Connection);
|
|
}
|
|
|
|
|
|
|
|
CloneSecurityPrincipal::Connection::~Connection()
|
|
{
|
|
LOG_DTOR(CloneSecurityPrincipal::Connection);
|
|
|
|
Disconnect();
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
ValidateDCAndDomainParameters(
|
|
const String& srcDC,
|
|
const String& srcDomain,
|
|
const String& dstDC,
|
|
const String& dstDomain)
|
|
{
|
|
LOG_FUNCTION(ValidateDCAndDomainParameters);
|
|
|
|
HRESULT hr = S_OK;
|
|
do
|
|
{
|
|
if (srcDC.empty() && srcDomain.empty())
|
|
{
|
|
hr = E_INVALIDARG;
|
|
SetComError(IDS_MUST_SPECIFY_SRC_DC_OR_DOMAIN);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
}
|
|
|
|
if (dstDC.empty() && dstDomain.empty())
|
|
{
|
|
hr = E_INVALIDARG;
|
|
SetComError(IDS_MUST_SPECIFY_DST_DC_OR_DOMAIN);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
}
|
|
|
|
if (!srcDC.empty() && !dstDC.empty())
|
|
{
|
|
if (srcDC.icompare(dstDC) == 0)
|
|
{
|
|
// may not be the same dc
|
|
|
|
hr = E_INVALIDARG;
|
|
SetComError(IDS_SRC_DC_EQUALS_DST_DC);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
}
|
|
}
|
|
|
|
if (!srcDomain.empty() && dstDomain.empty())
|
|
{
|
|
if (srcDomain.icompare(dstDomain) == 0)
|
|
{
|
|
// may not be the same domain
|
|
|
|
hr = E_INVALIDARG;
|
|
SetComError(IDS_SRC_DOMAIN_EQUALS_DST_DOMAIN);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
}
|
|
}
|
|
}
|
|
while (0);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// Creates a Computer object representing the domain controller specified, or
|
|
// located domain controller for the domain specified. Does additional
|
|
// validation of the dc and domain parameters.
|
|
|
|
HRESULT
|
|
CreateComputer(
|
|
const String& dc,
|
|
const String& domain,
|
|
Computer*& computer)
|
|
{
|
|
LOG_FUNCTION(CreateComputer);
|
|
ASSERT(computer == 0);
|
|
|
|
computer = 0;
|
|
HRESULT hr = S_OK;
|
|
do
|
|
{
|
|
if (dc.empty())
|
|
{
|
|
// source DC was not specified: find a writeable DC
|
|
|
|
// must have supplied the source domain: we checked for that
|
|
// in an earlier call to ValidateDCAndDomainParameters
|
|
ASSERT(!domain.empty());
|
|
if (domain.empty())
|
|
{
|
|
hr = E_INVALIDARG;
|
|
SetComError(IDS_MUST_SPECIFY_SRC_DC_OR_DOMAIN);
|
|
break;
|
|
}
|
|
|
|
DOMAIN_CONTROLLER_INFO* info = 0;
|
|
hr =
|
|
Win32ToHresult(
|
|
MyDsGetDcName(
|
|
0,
|
|
domain,
|
|
DS_WRITABLE_REQUIRED | DS_DIRECTORY_SERVICE_PREFERRED,
|
|
info));
|
|
|
|
LOG_HRESULT(hr);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
SetComError(
|
|
String::format(
|
|
IDS_CANT_FIND_DC,
|
|
domain.c_str(),
|
|
GetErrorMessage(hr).c_str()));
|
|
break;
|
|
}
|
|
|
|
if (info && info->DomainControllerName)
|
|
{
|
|
computer = new Computer(info->DomainControllerName);
|
|
::NetApiBufferFree(info);
|
|
}
|
|
else
|
|
{
|
|
// should always get a result if successful
|
|
ASSERT(false);
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// source dc was supplied
|
|
|
|
computer = new Computer(dc);
|
|
}
|
|
}
|
|
while (0);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// HRESULT
|
|
// Authenticate(
|
|
// const Computer& computer,
|
|
// const String& username,
|
|
// const String& userDomain,
|
|
// const String& password)
|
|
// {
|
|
// LOG_FUNCTION(Authenticate);
|
|
//
|
|
// // attempt to authenticate to the computer.
|
|
// String name = computer.NameWithBackslashes();
|
|
//
|
|
// NETRESOURCE nr;
|
|
// memset(&nr, 0, sizeof(nr));
|
|
//
|
|
// nr.dwType = RESOURCETYPE_ANY;
|
|
// nr.lpRemoteName = const_cast<String::value_type*>(name.c_str());
|
|
//
|
|
// // see KB articles Q218497, Q180548, Q183366 for the pitfalls here...
|
|
//
|
|
// String u;
|
|
// if (userDomain.empty())
|
|
// {
|
|
// u = username;
|
|
// }
|
|
// else
|
|
// {
|
|
// ASSERT(!username.empty());
|
|
// u = userDomain + L"\\" + username;
|
|
// }
|
|
//
|
|
// LOG(L"Calling WNetAddConnection2");
|
|
// LOG(String::format(L"username : %1", u.empty() ? L"(null)" : u.c_str()));
|
|
//
|
|
// HRESULT hr =
|
|
// Win32ToHresult(
|
|
// ::WNetAddConnection2(
|
|
// &nr,
|
|
// password.c_str(),
|
|
// u.empty() ? 0 : u.c_str(),
|
|
// 0));
|
|
//
|
|
// LOG_HRESULT(hr);
|
|
//
|
|
// if (FAILED(hr))
|
|
// {
|
|
// SetComError(
|
|
// String::format(
|
|
// IDS_UNABLE_TO_CONNECT,
|
|
// name.c_str(),
|
|
// GetErrorMessage(hr).c_str()));
|
|
// }
|
|
//
|
|
// return hr;
|
|
// }
|
|
|
|
|
|
|
|
HRESULT
|
|
ValidateInitializedComputer(
|
|
const Computer& computer,
|
|
const String& domain)
|
|
{
|
|
LOG_FUNCTION(ValidateInitializedComputer);
|
|
|
|
HRESULT hr = S_OK;
|
|
do
|
|
{
|
|
if (!computer.IsDomainController())
|
|
{
|
|
hr = E_INVALIDARG;
|
|
SetComError(
|
|
String::format(
|
|
IDS_COMPUTER_IS_NOT_DC,
|
|
computer.GetNetbiosName().c_str()));
|
|
break;
|
|
}
|
|
|
|
if (!domain.empty())
|
|
{
|
|
// check that the DC is really a DC of the specified domain
|
|
if (
|
|
computer.GetDomainDnsName().icompare(domain) != 0
|
|
&& computer.GetDomainNetbiosName().icompare(domain) != 0)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
SetComError(
|
|
String::format(
|
|
IDS_NOT_DC_FOR_WRONG_DOMAIN,
|
|
computer.GetNetbiosName().c_str(),
|
|
domain.c_str()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (0);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// Returns an open handle to the SAM database for the named domain on the
|
|
// given DC. Should be freed with SamCloseHandle.
|
|
|
|
HRESULT
|
|
OpenSamDomain(
|
|
const String& dcName,
|
|
const String& domainNetBiosName,
|
|
SAM_HANDLE& resultHandle)
|
|
{
|
|
LOG_FUNCTION2(OpenSamDomain, dcName);
|
|
ASSERT(!dcName.empty());
|
|
|
|
resultHandle = INVALID_HANDLE_VALUE;
|
|
|
|
HRESULT hr = S_OK;
|
|
SAM_HANDLE serverHandle = INVALID_HANDLE_VALUE;
|
|
PSID domainSID = 0;
|
|
do
|
|
{
|
|
UNICODE_STRING serverName;
|
|
memset(&serverName, 0, sizeof(serverName));
|
|
::RtlInitUnicodeString(&serverName, dcName.c_str());
|
|
|
|
LOG(L"Calling SamConnect");
|
|
|
|
hr =
|
|
NtStatusToHRESULT(
|
|
::SamConnect(
|
|
&serverName,
|
|
&serverHandle,
|
|
MAXIMUM_ALLOWED,
|
|
0));
|
|
if (FAILED(hr))
|
|
{
|
|
SetComError(
|
|
String::format(
|
|
IDS_UNABLE_TO_CONNECT_TO_SAM_SERVER,
|
|
dcName.c_str(),
|
|
GetErrorMessage(hr).c_str()));
|
|
break;
|
|
}
|
|
|
|
UNICODE_STRING domainName;
|
|
memset(&domainName, 0, sizeof(domainName));
|
|
::RtlInitUnicodeString(&domainName, domainNetBiosName.c_str());
|
|
|
|
hr =
|
|
NtStatusToHRESULT(
|
|
::SamLookupDomainInSamServer(
|
|
serverHandle,
|
|
&domainName,
|
|
&domainSID));
|
|
if (FAILED(hr))
|
|
{
|
|
SetComError(
|
|
String::format(
|
|
IDS_UNABLE_TO_LOOKUP_SAM_DOMAIN,
|
|
domainNetBiosName.c_str(),
|
|
GetErrorMessage(hr).c_str()));
|
|
break;
|
|
}
|
|
|
|
hr =
|
|
NtStatusToHRESULT(
|
|
::SamOpenDomain(
|
|
serverHandle,
|
|
MAXIMUM_ALLOWED,
|
|
domainSID,
|
|
&resultHandle));
|
|
if (FAILED(hr))
|
|
{
|
|
SetComError(
|
|
String::format(
|
|
IDS_UNABLE_TO_OPEN_SAM_DOMAIN,
|
|
domainNetBiosName.c_str(),
|
|
GetErrorMessage(hr).c_str()));
|
|
break;
|
|
}
|
|
}
|
|
while (0);
|
|
|
|
if (serverHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
::SamCloseHandle(serverHandle);
|
|
}
|
|
|
|
if (domainSID)
|
|
{
|
|
::SamFreeMemory(domainSID);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
DetermineSourceDcDnsName(
|
|
const String& srcDcNetbiosName,
|
|
const String& srcDomainDnsName,
|
|
String& srcDcDnsName)
|
|
{
|
|
LOG_FUNCTION(DetermineSourceDcDnsName);
|
|
ASSERT(!srcDcNetbiosName.empty());
|
|
|
|
srcDcDnsName.erase();
|
|
|
|
if (srcDomainDnsName.empty())
|
|
{
|
|
// The computer is not a DS DC, so we don't need its DNS name.
|
|
LOG(L"source DC is not a DS DC");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
HANDLE hds = 0;
|
|
do
|
|
{
|
|
// Bind to self
|
|
hr =
|
|
MyDsBind(
|
|
srcDcNetbiosName,
|
|
srcDomainDnsName,
|
|
hds);
|
|
if (FAILED(hr))
|
|
{
|
|
SetComError(
|
|
String::format(
|
|
IDS_BIND_FAILED,
|
|
srcDcNetbiosName.c_str(),
|
|
GetErrorMessage(hr).c_str()));
|
|
break;
|
|
}
|
|
|
|
// find all the dc's for my domain. the list should contain
|
|
// srcDcNetbiosName.
|
|
|
|
DS_DOMAIN_CONTROLLER_INFO_1W* info = 0;
|
|
DWORD infoCount = 0;
|
|
hr =
|
|
MyDsGetDomainControllerInfo(
|
|
hds,
|
|
srcDomainDnsName,
|
|
infoCount,
|
|
info);
|
|
if (FAILED(hr))
|
|
{
|
|
SetComError(
|
|
String::format(
|
|
IDS_GET_DC_INFO_FAILED,
|
|
GetErrorMessage(hr).c_str()));
|
|
break;
|
|
}
|
|
|
|
// there should be at least 1 entry, the source DC itself
|
|
ASSERT(infoCount);
|
|
ASSERT(info);
|
|
|
|
if (info)
|
|
{
|
|
for (DWORD i = 0; i < infoCount; i++)
|
|
{
|
|
if (info[i].NetbiosName)
|
|
{
|
|
LOG(info[i].NetbiosName);
|
|
|
|
if (srcDcNetbiosName.icompare(info[i].NetbiosName) == 0)
|
|
{
|
|
// we found ourselves in the list
|
|
|
|
LOG(L"netbios name found");
|
|
|
|
if (info[i].DnsHostName)
|
|
{
|
|
LOG(L"dns hostname found!");
|
|
srcDcDnsName = info[i].DnsHostName;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
::DsFreeDomainControllerInfo(1, infoCount, info);
|
|
|
|
if (srcDcDnsName.empty())
|
|
{
|
|
hr = E_FAIL;
|
|
SetComError(
|
|
String::format(
|
|
IDS_CANT_FIND_SRC_DC_DNS_NAME,
|
|
srcDcNetbiosName.c_str()));
|
|
break;
|
|
}
|
|
|
|
LOG(srcDcDnsName);
|
|
}
|
|
while (0);
|
|
|
|
if (hds)
|
|
{
|
|
::DsUnBind(&hds);
|
|
hds = 0;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CloneSecurityPrincipal::Connection::Connect(
|
|
const String& srcDC,
|
|
const String& srcDomain,
|
|
const String& dstDC,
|
|
const String& dstDomain)
|
|
{
|
|
LOG_FUNCTION(CloneSecurityPrincipal::Connection::Connect);
|
|
|
|
HRESULT hr = S_OK;
|
|
do
|
|
{
|
|
hr = ValidateDCAndDomainParameters(srcDC, srcDomain, dstDC, dstDomain);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
hr = CreateComputer(srcDC, srcDomain, srcComputer);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
hr = CreateComputer(dstDC, dstDomain, dstComputer);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
// hr =
|
|
// Authenticate(
|
|
// *srcComputer,
|
|
// srcUsername,
|
|
// srcUserDomain,
|
|
// srcPassword);
|
|
// BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
hr = srcComputer->Refresh();
|
|
if (FAILED(hr))
|
|
{
|
|
SetComError(
|
|
String::format(
|
|
IDS_UNABLE_TO_READ_COMPUTER_INFO,
|
|
srcComputer->GetNetbiosName().c_str(),
|
|
GetErrorMessage(hr).c_str()));
|
|
break;
|
|
}
|
|
|
|
hr = ValidateInitializedComputer(*srcComputer, srcDomain);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
hr = dstComputer->Refresh();
|
|
if (FAILED(hr))
|
|
{
|
|
SetComError(
|
|
String::format(
|
|
IDS_UNABLE_TO_READ_COMPUTER_INFO,
|
|
dstComputer->GetNetbiosName().c_str(),
|
|
GetErrorMessage(hr).c_str()));
|
|
break;
|
|
}
|
|
|
|
hr = ValidateInitializedComputer(*dstComputer, dstDomain);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
// bind to the destination DC.
|
|
|
|
ASSERT(dstDsBindHandle == INVALID_HANDLE_VALUE);
|
|
|
|
hr =
|
|
MyDsBind(
|
|
dstComputer->GetNetbiosName(),
|
|
String(),
|
|
dstDsBindHandle);
|
|
if (FAILED(hr))
|
|
{
|
|
SetComError(
|
|
String::format(
|
|
IDS_BIND_FAILED,
|
|
dstComputer->GetNetbiosName().c_str(),
|
|
GetErrorMessage(hr).c_str()));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// open ldap connection to dstDC
|
|
//
|
|
m_pldap = ldap_open(const_cast<String::value_type*>(dstDC.c_str()), LDAP_PORT);
|
|
if (!m_pldap)
|
|
{
|
|
hr = Win::GetLastErrorAsHresult();
|
|
SetComError(
|
|
String::format(
|
|
IDS_LDAPOPEN_FAILED,
|
|
dstComputer->GetNetbiosName().c_str(),
|
|
GetErrorMessage(hr).c_str()));
|
|
break;
|
|
}
|
|
|
|
// SEC_WINNT_AUTH_IDENTITY authInfo;
|
|
// authInfo.User = const_cast<wchar_t*>(dstUsername.c_str());
|
|
// authInfo.UserLength = dstUsername.length();
|
|
// authInfo.Domain = const_cast<wchar_t*>(dstUserDomain.c_str());
|
|
// authInfo.DomainLength = dstUserDomain.length();
|
|
// authInfo.Password = const_cast<wchar_t*>(dstPassword.c_str());
|
|
// authInfo.PasswordLength = dstPassword.length();
|
|
// authInfo.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
|
|
|
|
DWORD dwErr = ldap_bind_s(
|
|
m_pldap,
|
|
NULL,
|
|
(TCHAR *) 0,
|
|
LDAP_AUTH_NEGOTIATE);
|
|
if (LDAP_SUCCESS != dwErr)
|
|
{
|
|
hr = Win::GetLastErrorAsHresult();
|
|
|
|
ldap_unbind_s(m_pldap);
|
|
m_pldap = 0;
|
|
|
|
SetComError(
|
|
String::format(
|
|
IDS_LDAPBIND_FAILED,
|
|
dstComputer->GetNetbiosName().c_str(),
|
|
GetErrorMessage(hr).c_str()));
|
|
break;
|
|
}
|
|
|
|
// obtain sam handles to source and dst domains
|
|
|
|
ASSERT(srcDomainSamHandle == INVALID_HANDLE_VALUE);
|
|
|
|
hr =
|
|
OpenSamDomain(
|
|
srcComputer->GetNetbiosName(),
|
|
srcComputer->GetDomainNetbiosName(),
|
|
srcDomainSamHandle);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
ASSERT(dstDomainSamHandle == INVALID_HANDLE_VALUE);
|
|
|
|
hr =
|
|
OpenSamDomain(
|
|
dstComputer->GetNetbiosName(),
|
|
dstComputer->GetDomainNetbiosName(),
|
|
dstDomainSamHandle);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
hr =
|
|
DetermineSourceDcDnsName(
|
|
srcComputer->GetNetbiosName(),
|
|
srcComputer->GetDomainDnsName(),
|
|
srcDcDnsName);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
}
|
|
while (0);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Disconnect();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
CloneSecurityPrincipal::Connection::IsConnected() const
|
|
{
|
|
LOG_FUNCTION(CloneSecurityPrincipal::Connection::IsConnected);
|
|
|
|
bool result =
|
|
srcComputer
|
|
&& dstComputer
|
|
&& (dstDsBindHandle != INVALID_HANDLE_VALUE)
|
|
&& (srcDomainSamHandle != INVALID_HANDLE_VALUE);
|
|
|
|
LOG(
|
|
String::format(
|
|
L"object %1 connected.",
|
|
result ? L"is" : L"is NOT"));
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
CloneSecurityPrincipal::Connection::Disconnect()
|
|
{
|
|
LOG_FUNCTION(CloneSecurityPrincipal::Connection::Disconnect);
|
|
|
|
// may be called if Connect fails, so we might be in a partially
|
|
// connected state. So we need to check the handle values.
|
|
|
|
if (srcDomainSamHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
::SamCloseHandle(srcDomainSamHandle);
|
|
srcDomainSamHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (dstDsBindHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
::DsUnBind(&dstDsBindHandle);
|
|
dstDsBindHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (m_pldap)
|
|
{
|
|
ldap_unbind_s(m_pldap);
|
|
m_pldap = 0;
|
|
}
|
|
|
|
delete dstComputer;
|
|
dstComputer = 0;
|
|
|
|
delete srcComputer;
|
|
srcComputer = 0;
|
|
}
|
|
|
|
|
|
|