// // 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(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(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(dstUsername.c_str()); // authInfo.UserLength = dstUsername.length(); // authInfo.Domain = const_cast(dstUserDomain.c_str()); // authInfo.DomainLength = dstUserDomain.length(); // authInfo.Password = const_cast(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; }