You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
650 lines
20 KiB
650 lines
20 KiB
/*---------------------------------------------------------------------------
|
|
File: ChangeDomain.cpp
|
|
|
|
Comments: Implementation of COM object that changes the domain affiliation on
|
|
a computer.
|
|
|
|
(c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved
|
|
Proprietary and confidential to Mission Critical Software, Inc.
|
|
|
|
REVISION LOG ENTRY
|
|
Revision By: Christy Boles
|
|
Revised on 02/15/99 11:21:07
|
|
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
|
|
// ChangeDomain.cpp : Implementation of CChangeDomain
|
|
#include "stdafx.h"
|
|
#include "WorkObj.h"
|
|
#include "ChDom.h"
|
|
|
|
#include "Common.hpp"
|
|
#include "UString.hpp"
|
|
#include "EaLen.hpp"
|
|
#include "ResStr.h"
|
|
#include "ErrDct.hpp"
|
|
#include "TxtSid.h"
|
|
#include "TReg.hpp"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CChangeDomain
|
|
|
|
|
|
#include "LSAUtils.h"
|
|
|
|
#import "NetEnum.tlb" no_namespace
|
|
#import "VarSet.tlb" no_namespace rename("property", "aproperty")
|
|
|
|
#include <lm.h> // for NetXxx API
|
|
#include <winbase.h>
|
|
|
|
TErrorDct errLocal;
|
|
|
|
typedef NET_API_STATUS (NET_API_FUNCTION* PNETJOINDOMAIN)
|
|
(
|
|
IN LPCWSTR lpServer OPTIONAL,
|
|
IN LPCWSTR lpDomain,
|
|
IN LPCWSTR lpAccountOU, OPTIONAL
|
|
IN LPCWSTR lpAccount OPTIONAL,
|
|
IN LPCWSTR lpPassword OPTIONAL,
|
|
IN DWORD fJoinOptions
|
|
);
|
|
|
|
HINSTANCE hDll = NULL;
|
|
PNETJOINDOMAIN pNetJoinDomain = NULL;
|
|
|
|
BOOL GetNetJoinDomainFunction()
|
|
{
|
|
BOOL bSuccess = FALSE;
|
|
|
|
hDll = LoadLibrary(L"NetApi32.dll");
|
|
|
|
if ( hDll )
|
|
{
|
|
pNetJoinDomain = (PNETJOINDOMAIN)GetProcAddress(hDll,"NetJoinDomain");
|
|
if ( pNetJoinDomain )
|
|
{
|
|
bSuccess = TRUE;
|
|
}
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
typedef HRESULT (CALLBACK * ADSGETOBJECT)(LPWSTR, REFIID, void**);
|
|
extern ADSGETOBJECT ADsGetObject;
|
|
|
|
STDMETHODIMP
|
|
CChangeDomain::ChangeToDomain(
|
|
BSTR activeComputerName, // in - computer name currently being used (old name if simultaneously renaming and changing domain)
|
|
BSTR domain, // in - domain to move computer to
|
|
BSTR newComputerName, // in - computer name the computer will join the new domain as (the name that will be in effect on reboot, if simultaneously renaming and changing domain)
|
|
BSTR * errReturn // out- string describing any errors that occurred
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CChangeDomain::ChangeToDomainWithSid(
|
|
BSTR activeComputerName, // in - computer name currently being used (old name if simultaneously renaming and changing domain)
|
|
BSTR domain, // in - domain to move computer to
|
|
BSTR domainSid, // in - sid of domain, as string
|
|
BSTR domainController, // in - domain controller to use
|
|
BSTR newComputerName, // in - computer name the computer will join the new domain as (the name that will be in effect on reboot, if simultaneously renaming and changing domain)
|
|
BSTR srcPath, // in - source account original LDAP path
|
|
BSTR * errReturn // out- string describing any errors that occurred
|
|
)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// initialize output parameters
|
|
(*errReturn) = NULL;
|
|
|
|
//
|
|
// Use NetJoinDomain API if available (Windows 2000 and later)
|
|
// otherwise must use LSA APIs (Windows NT 4 and earlier).
|
|
//
|
|
|
|
if (GetNetJoinDomainFunction())
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
//
|
|
// If a preferred domain controller is specified then use it.
|
|
//
|
|
|
|
_bstr_t strNewDomain = domain;
|
|
|
|
if (SysStringLen(domainController) > 0)
|
|
{
|
|
//
|
|
// The preferred domain controller may only be specified for uplevel
|
|
// (W2K or later) domains. During undo of a computer migration the
|
|
// target domain may be a downlevel (NT4) domain. If unable to obtain
|
|
// operating system version information from the specified domain
|
|
// controller or the operating system is downlevel then don't specify
|
|
// a preferred domain controller.
|
|
//
|
|
|
|
PWKSTA_INFO_100 pInfo = NULL;
|
|
|
|
NET_API_STATUS nasStatus = NetWkstaGetInfo(domainController, 100, (LPBYTE*)&pInfo);
|
|
|
|
if ((nasStatus == NERR_Success) && pInfo)
|
|
{
|
|
if (pInfo->wki100_ver_major >= 5)
|
|
{
|
|
NetApiBufferFree(pInfo);
|
|
|
|
strNewDomain += L"\\";
|
|
strNewDomain += domainController;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Join Options
|
|
//
|
|
|
|
const DWORD JOIN_OPTIONS = NETSETUP_JOIN_DOMAIN | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_UNSECURE;
|
|
|
|
//
|
|
// Check whether a new computer name has been specified.
|
|
//
|
|
|
|
if (SysStringLen(newComputerName) == 0)
|
|
{
|
|
//
|
|
// A new name has not been specified therefore simply
|
|
// join the new domain with the current computer name.
|
|
//
|
|
|
|
dwError = pNetJoinDomain(NULL, strNewDomain, NULL, NULL, NULL, JOIN_OPTIONS);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// A new name has been specified therefore computer must be re-named during join.
|
|
//
|
|
// The current APIs only support joining a domain with the current name and then
|
|
// re-naming the computer in the domain after the join. Unfortunately the re-name
|
|
// in the domain requires the credentials of a security principal with the rights
|
|
// to change the name of the computer in the domain or that this process be running
|
|
// under the security context of a security principal with the rights to change the
|
|
// name of the computer in the domain. Since these requirements cannot be met with
|
|
// ADMT the following trick (read hack) must be used.
|
|
//
|
|
// Set the active computer name in the registry to the new name during the duration
|
|
// of the NetJoinDomain call so that the computer is joined to the new domain with
|
|
// the new name without requiring a subsequent re-name in the new domain.
|
|
//
|
|
|
|
TRegKey key;
|
|
static WCHAR c_szKey[] = L"System\\CurrentControlSet\\Control\\ComputerName\\ActiveComputerName";
|
|
|
|
dwError = key.Open(c_szKey, HKEY_LOCAL_MACHINE);
|
|
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
static WCHAR c_szKeyValue[] = L"ComputerName";
|
|
WCHAR szOldComputerName[MAX_PATH];
|
|
|
|
dwError = key.ValueGetStr(c_szKeyValue, szOldComputerName, MAX_PATH - 1);
|
|
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
dwError = key.ValueSetStr(c_szKeyValue, OLE2CW(newComputerName));
|
|
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
dwError = pNetJoinDomain(NULL, strNewDomain, NULL, NULL, NULL, JOIN_OPTIONS);
|
|
|
|
key.ValueSetStr(c_szKeyValue, szOldComputerName);
|
|
key.Close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = HRESULT_FROM_WIN32(dwError);
|
|
}
|
|
else
|
|
{
|
|
do // once
|
|
{
|
|
LSA_HANDLE PolicyHandle = NULL;
|
|
LPWSTR Workstation; // target machine of policy update
|
|
WCHAR Password[LEN_Password];
|
|
PSID DomainSid=NULL; // Sid representing domain to trust
|
|
PSERVER_INFO_101 si101 = NULL;
|
|
DWORD Type;
|
|
NET_API_STATUS nas;
|
|
NTSTATUS Status;
|
|
WCHAR errMsg[1000];
|
|
WCHAR TrustedDomainName[LEN_Domain];
|
|
WCHAR LocalMachine[MAX_PATH] = L"";
|
|
DWORD lenLocalMachine = DIM(LocalMachine);
|
|
LPWSTR activeWorkstation = L"";
|
|
|
|
// use the target name, if provided
|
|
|
|
if ( newComputerName && UStrLen((WCHAR*)newComputerName) )
|
|
{
|
|
Workstation = (WCHAR*)newComputerName;
|
|
|
|
if ( ! activeComputerName || ! UStrLen((WCHAR*)activeComputerName) )
|
|
{
|
|
activeWorkstation = LocalMachine;
|
|
}
|
|
else
|
|
{
|
|
activeWorkstation = (WCHAR*)activeComputerName;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (! activeComputerName || ! UStrLen((WCHAR*)activeComputerName) )
|
|
{
|
|
GetComputerName(LocalMachine,&lenLocalMachine);
|
|
Workstation = LocalMachine;
|
|
activeWorkstation = L"";
|
|
}
|
|
else
|
|
{
|
|
Workstation = (WCHAR*)activeComputerName;
|
|
activeWorkstation = Workstation;
|
|
}
|
|
}
|
|
|
|
wcscpy(TrustedDomainName,(WCHAR*)domain);
|
|
|
|
if ( Workstation[0] == L'\\' )
|
|
Workstation += 2;
|
|
|
|
// Use a default password
|
|
for ( UINT p = 0 ; p < wcslen(Workstation) ; p++ )
|
|
Password[p] = towlower(Workstation[p]);
|
|
Password[wcslen(Workstation)] = 0;
|
|
|
|
// ensure that the password is truncated at 14 characters
|
|
Password[14] = 0;
|
|
//
|
|
// insure the target machine is NOT a DC, as this operation is
|
|
// only appropriate against a workstation.
|
|
//
|
|
nas = NetServerGetInfo(activeWorkstation, 101, (LPBYTE *)&si101);
|
|
if(nas != NERR_Success)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(nas);
|
|
break;
|
|
}
|
|
|
|
// Use LSA APIs
|
|
Type = si101->sv101_type;
|
|
|
|
if( (Type & SV_TYPE_DOMAIN_CTRL) ||
|
|
(Type & SV_TYPE_DOMAIN_BAKCTRL) )
|
|
{
|
|
swprintf(errMsg,GET_STRING(IDS_NotAllowedOnDomainController));
|
|
hr = E_NOTIMPL;
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// do not allow a workstation to trust itself
|
|
//
|
|
if(lstrcmpiW(TrustedDomainName, Workstation) == 0)
|
|
{
|
|
swprintf(errMsg,GET_STRING(IDS_CannotTrustSelf),
|
|
TrustedDomainName);
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
if( lstrlenW(TrustedDomainName ) > MAX_COMPUTERNAME_LENGTH )
|
|
{
|
|
TrustedDomainName[MAX_COMPUTERNAME_LENGTH] = L'\0'; // truncate
|
|
}
|
|
|
|
if ( ! m_bNoChange )
|
|
{
|
|
//
|
|
// build the DomainSid of the domain to trust
|
|
//
|
|
DomainSid = SidFromString(domainSid);
|
|
if(!DomainSid )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
}
|
|
|
|
}
|
|
if ( (!m_bNoChange) && (si101->sv101_version_major < 4) )
|
|
{
|
|
// For NT 3.51 machines, we must move the computer to a workgroup, and
|
|
// then move it into the new domain
|
|
hr = ChangeToWorkgroup(SysAllocString(activeWorkstation),SysAllocString(L"WORKGROUP"),errReturn);
|
|
|
|
if (FAILED(hr)) {
|
|
break;
|
|
}
|
|
|
|
Status = QueryWorkstationTrustedDomainInfo(PolicyHandle,DomainSid,m_bNoChange);
|
|
|
|
if ( Status != STATUS_SUCCESS )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// see if the computer account exists on the domain
|
|
//
|
|
|
|
//
|
|
// open the policy on this computer
|
|
//
|
|
Status = OpenPolicy(
|
|
activeWorkstation,
|
|
DELETE | // to remove a trust
|
|
POLICY_VIEW_LOCAL_INFORMATION | // to view trusts
|
|
POLICY_CREATE_SECRET | // for password set operation
|
|
POLICY_TRUST_ADMIN, // for trust creation
|
|
&PolicyHandle
|
|
);
|
|
|
|
if( Status != STATUS_SUCCESS )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
|
|
break;
|
|
}
|
|
|
|
if ( ! m_bNoChange )
|
|
{
|
|
Status = QueryWorkstationTrustedDomainInfo(PolicyHandle,DomainSid,m_bNoChange);
|
|
if (Status == STATUS_SUCCESS) {
|
|
Status = SetWorkstationTrustedDomainInfo(
|
|
PolicyHandle,
|
|
DomainSid,
|
|
TrustedDomainName,
|
|
Password,
|
|
errMsg
|
|
);
|
|
}
|
|
}
|
|
|
|
if( Status != STATUS_SUCCESS )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Update the primary domain to match the specified trusted domain
|
|
//
|
|
if (! m_bNoChange )
|
|
{
|
|
Status = SetPrimaryDomain(PolicyHandle, DomainSid, TrustedDomainName);
|
|
|
|
if(Status != STATUS_SUCCESS)
|
|
{
|
|
// DisplayNtStatus(errMsg,"SetPrimaryDomain", Status,NULL);
|
|
hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
|
|
break;
|
|
}
|
|
}
|
|
|
|
NetApiBufferFree(si101);
|
|
|
|
// Cleanup
|
|
|
|
//LocalFree(Workstation);
|
|
|
|
//
|
|
// free the Sid which was allocated for the TrustedDomain Sid
|
|
//
|
|
if(DomainSid)
|
|
{
|
|
FreeSid(DomainSid);
|
|
}
|
|
|
|
//
|
|
// close the policy handle
|
|
//
|
|
if ( PolicyHandle )
|
|
{
|
|
LsaClose(PolicyHandle);
|
|
}
|
|
|
|
} while (false); // do once
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
(*errReturn) = NULL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CChangeDomain::ChangeToWorkgroup(
|
|
BSTR Computer, // in - name of computer to update
|
|
BSTR Workgroup, // in - name of workgroup to join
|
|
BSTR * errReturn // out- text describing error if failure
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LSA_HANDLE PolicyHandle = NULL;
|
|
LPWSTR Workstation; // target machine of policy update
|
|
LPWSTR TrustedDomainName; // domain to join
|
|
PSERVER_INFO_101 si101;
|
|
DWORD Type;
|
|
NET_API_STATUS nas;
|
|
NTSTATUS Status;
|
|
WCHAR errMsg[1000] = L"";
|
|
// BOOL bSessionEstablished = FALSE;
|
|
|
|
// initialize output parameters
|
|
(*errReturn) = NULL;
|
|
|
|
Workstation = (WCHAR*)Computer;
|
|
TrustedDomainName = (WCHAR*)Workgroup;
|
|
|
|
errLocal.DbgMsgWrite(0,L"Changing to workgroup...");
|
|
//
|
|
// insure the target machine is NOT a DC, as this operation is
|
|
// only appropriate against a workstation.
|
|
//
|
|
do // once
|
|
{
|
|
/*if ( m_account.length() )
|
|
{
|
|
// Establish our credentials to the target machine
|
|
if (! EstablishSession(Workstation,m_domain,m_account,m_password, TRUE) )
|
|
{
|
|
// DisplayError(errMsg,"EstablishSession",GetLastError(),NULL);
|
|
hr = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
bSessionEstablished = TRUE;
|
|
}
|
|
}
|
|
*/
|
|
nas = NetServerGetInfo(Workstation, 101, (LPBYTE *)&si101);
|
|
|
|
if(nas != NERR_Success)
|
|
{
|
|
//DisplayError(errMsg, "NetServerGetInfo", nas,NULL);
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
Type = si101->sv101_type;
|
|
NetApiBufferFree(si101);
|
|
|
|
if( (Type & SV_TYPE_DOMAIN_CTRL) ||
|
|
(Type & SV_TYPE_DOMAIN_BAKCTRL) )
|
|
{
|
|
swprintf(errMsg,L"Operation is not valid on a domain controller.\n");
|
|
hr = E_FAIL;
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// do not allow a workstation to trust itself
|
|
//
|
|
if(lstrcmpiW(TrustedDomainName, Workstation) == 0)
|
|
{
|
|
swprintf(errMsg,L"Error: Domain %ls cannot be a member of itself.\n",
|
|
TrustedDomainName);
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
if( lstrlenW(TrustedDomainName ) > MAX_COMPUTERNAME_LENGTH )
|
|
{
|
|
TrustedDomainName[MAX_COMPUTERNAME_LENGTH] = L'\0'; // truncate
|
|
}
|
|
|
|
//
|
|
// open the policy on this computer
|
|
//
|
|
Status = OpenPolicy(
|
|
Workstation,
|
|
DELETE | // to remove a trust
|
|
POLICY_VIEW_LOCAL_INFORMATION | // to view trusts
|
|
POLICY_CREATE_SECRET | // for password set operation
|
|
POLICY_TRUST_ADMIN, // for trust creation
|
|
&PolicyHandle
|
|
);
|
|
|
|
if( Status != STATUS_SUCCESS )
|
|
{
|
|
//DisplayNtStatus(errMsg,"OpenPolicy", Status,NULL);
|
|
hr = LsaNtStatusToWinError(Status);
|
|
break;
|
|
}
|
|
|
|
if( Status != STATUS_SUCCESS )
|
|
{
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Update the primary domain to match the specified trusted domain
|
|
//
|
|
if (! m_bNoChange )
|
|
{
|
|
Status = SetPrimaryDomain(PolicyHandle, NULL, TrustedDomainName);
|
|
|
|
if(Status != STATUS_SUCCESS)
|
|
{
|
|
//DisplayNtStatus(errMsg,"SetPrimaryDomain", Status,NULL);
|
|
hr = LsaNtStatusToWinError(Status);
|
|
break;
|
|
}
|
|
|
|
}
|
|
} while (false); // do once
|
|
|
|
// Cleanup
|
|
//
|
|
// close the policy handle
|
|
//
|
|
|
|
if(PolicyHandle)
|
|
{
|
|
LsaClose(PolicyHandle);
|
|
}
|
|
|
|
/*if ( bSessionEstablished )
|
|
{
|
|
EstablishSession(Workstation,m_domain,m_account,m_password,FALSE);
|
|
}
|
|
*/
|
|
if ( FAILED(hr) )
|
|
{
|
|
hr = S_FALSE;
|
|
(*errReturn) = SysAllocString(errMsg);
|
|
}
|
|
return hr;
|
|
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CChangeDomain::ConnectAs(
|
|
BSTR domain, // in - domain name to use for credentials
|
|
BSTR user, // in - account name to use for credentials
|
|
BSTR password // in - password to use for credentials
|
|
)
|
|
{
|
|
m_domain = domain;
|
|
m_account = user;
|
|
m_password = password;
|
|
m_domainAccount = domain;
|
|
m_domainAccount += L"\\";
|
|
m_domainAccount += user;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CChangeDomain::get_NoChange(
|
|
BOOL * pVal // out- flag, whether to write changes
|
|
)
|
|
{
|
|
(*pVal) = m_bNoChange;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CChangeDomain::put_NoChange(
|
|
BOOL newVal // in - flag, whether to write changes
|
|
)
|
|
{
|
|
m_bNoChange = newVal;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// ChangeDomain worknode: Changes the domain affiliation of a workstation or server
|
|
// (This operation cannot be performed on domain controllers)
|
|
//
|
|
// VarSet syntax:
|
|
//
|
|
// Input:
|
|
// ChangeDomain.Computer: <ComputerName>
|
|
// ChangeDomain.TargetDomain: <Domain>
|
|
// ChangeDomain.DomainIsWorkgroup: <Yes|No> default is No
|
|
// ChangeDomain.ConnectAs.Domain: <Domain> optional credentials to use
|
|
// ChangeDomain.ConnectAs.User : <Username>
|
|
// ChangeDomain.ConnectAs.Password : <Password>
|
|
//
|
|
// Output:
|
|
// ChangeDomain.ErrorText : <string-error message>
|
|
|
|
// This function is not currently used by the domain migration tool.
|
|
// The actual implementation is removed from the source.
|
|
STDMETHODIMP
|
|
CChangeDomain::Process(
|
|
IUnknown * pWorkItem
|
|
)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|