windows-nt/Source/XPSP1/NT/admin/dsutils/migrate/clonepr/connect.cpp
2020-09-26 16:20:57 +08:00

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;
}