windows-nt/Source/XPSP1/NT/admin/admt/moveobj/mover.cpp

654 lines
18 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
// Mover.cpp : Implementation of CMoveObjApp and DLL registration.
#include "stdafx.h"
#include <stdio.h>
#include <basetsd.h>
#include <ntdsapi.h>
#include "MoveObj.h"
#include "Mover.h"
#include "UString.hpp"
#include "EaLen.hpp"
#include "ErrDct.hpp"
#include "TReg.hpp"
#include "ResStr.h"
#include "winldap.h" // use the platform SDK version of winldap.h, which is in the project directory
#define SECURITY_WIN32 1 // Needed for sspi.h
#include <sspi.h> // Needed for ISC_REQ_DELEGATE
#define LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID "1.2.840.113556.1.4.521"
#define LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID_W L"1.2.840.113556.1.4.521"
TErrorDct err;
TErrorDct errLogMain;
StringLoader gString;
BOOL // ret- whether to perform trace logging to a file
MoverTraceLogging(
WCHAR * filename // out- filename to use for trace logging
)
{
DWORD rc = 0;
BOOL bFound = FALSE;
TRegKey key;
WCHAR fnW[MAX_PATH];
rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),(HKEY)HKEY_LOCAL_MACHINE);
if ( ! rc )
{
rc = key.ValueGetStr(L"MoveObjectLog",fnW,MAX_PATH);
if ( ! rc )
{
if ( *fnW )
{
bFound = TRUE;
UStrCpy(filename,fnW);
}
else
{
filename[0] = 0;
}
}
}
return bFound && filename[0];
}
// In the following function we are sending in the logFilename in the tgtCredDomain argument.
// This is done since this is always called with a null value in the ADMT code. To be safe we
// will check if the account value is null then we will treat this as a log file otherwise
// we will need to treat this as credentials.
STDMETHODIMP
CMover::Connect(
BSTR sourceComp, // in - source domain computer to connect to
BSTR targetDSA, // in - target domain computer to connect to
BSTR srcCredDomain, // in - credentials to use for source domain
BSTR srcCredAccount, // in - credentials to use for source domain
BSTR srcCredPassword, // in - credentials to use for source domain
BSTR tgtCredDomain, // in - credentials to use for target domain
BSTR tgtCredAccount, // in - credentials to use for target domain
BSTR tgtCredPassword // in - credentials to use for target domain
)
{
DWORD rc = 0;
LONG value = 0;
ULONG flags = 0;
ULONG result = 0;
SEC_WINNT_AUTH_IDENTITY srcCred;
SEC_WINNT_AUTH_IDENTITY tgtCred;
BOOL bUseSrcCred = FALSE;
BOOL bUseTgtCred = FALSE;
BOOL bSrcGood = FALSE;
WCHAR * logFileMain;
// strip off leading \\ if present
if ( sourceComp && sourceComp[0] == L'\\' )
{
UStrCpy(m_sourceDSA,sourceComp + 2);
}
else
{
UStrCpy(m_sourceDSA,sourceComp);
}
if ( targetDSA && targetDSA[0] == L'\\' )
{
UStrCpy(m_targetDSA,targetDSA + 2);
}
else
{
UStrCpy(m_targetDSA,targetDSA);
}
// set up credentials structure to use for bind, if needed
if ( srcCredDomain && *srcCredDomain && srcCredAccount && *srcCredAccount )
{
srcCred.User = srcCredAccount;
srcCred.Domain = srcCredDomain;
srcCred.Password = srcCredPassword;
srcCred.UserLength = UStrLen(srcCred.User);
srcCred.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
srcCred.DomainLength = UStrLen(srcCred.Domain);
srcCred.PasswordLength = UStrLen(srcCred.Password);
bUseSrcCred = TRUE;
}
if ( tgtCredAccount && *tgtCredAccount )
{
tgtCred.User = tgtCredAccount;
tgtCred.Domain = tgtCredDomain;
tgtCred.Password = tgtCredPassword;
tgtCred.UserLength = UStrLen(tgtCred.User);
tgtCred.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
tgtCred.DomainLength = UStrLen(tgtCred.Domain);
tgtCred.PasswordLength = UStrLen(tgtCred.Password);
bUseTgtCred = TRUE;
}
else if ( tgtCredDomain && *tgtCredDomain )
{
logFileMain = tgtCredDomain;
if (*logFileMain)
{
errLogMain.LogOpen(logFileMain, 1);
}
}
// Open LDAP connections to the source and target computers
// first, connect to the source computer
WCHAR logFile[LEN_Path];
if ( MoverTraceLogging(logFile) )
{
err.LogOpen(logFile,1);
}
err.DbgMsgWrite(0,L"\n\nMoveObject::Connect(%ls,%ls)",m_sourceDSA,m_targetDSA);
// m_srcLD = ldap_openW(m_sourceDSA, LDAP_PORT);
//replace ldap_open(servername,..) with ldap_init and set LDAP_OPT_AREC_EXCLUSIVE
//flag so that the following ldap calls (i.e. ldap_bind) will not need to
//unnecessarily query for the domain controller
m_srcLD = ldap_initW(m_sourceDSA, LDAP_PORT);
if ( m_srcLD == NULL )
{
value = LdapGetLastError();
if (value == LDAP_SUCCESS )
{
rc = ERROR_CONNECTION_REFUSED;
}
else
{
rc = LdapMapErrorToWin32(result);
}
errLogMain.SysMsgWrite(ErrE, rc, DCT_MSG_CONNECT_ERROR_SOURCE_SD, (WCHAR*)m_sourceDSA, rc);
}
if ( m_srcLD )
{
//set LDAP_OPT_AREC_EXCLUSIVE flag so that the following calls tp
//ldap_open will not need to unnecessarily query for the domain controller
flags = PtrToUlong(LDAP_OPT_ON);
ldap_set_option(m_srcLD, LDAP_OPT_AREC_EXCLUSIVE, &flags);
err.DbgMsgWrite(0,L"Setting source options");
flags = 0;
// set the delegation flag for the source handle
result = ldap_get_option(m_srcLD, LDAP_OPT_SSPI_FLAGS,&flags);
if ( result )
{
rc = LdapMapErrorToWin32(result);
}
else
{
flags |= ISC_REQ_DELEGATE;
result = ldap_set_option(m_srcLD,LDAP_OPT_SSPI_FLAGS, &flags);
if ( result )
{
rc = LdapMapErrorToWin32(result);
}
}
}
if ( ! rc )
{
err.DbgMsgWrite(0,L"Binding to source");
// try to bind to the source LDAP server
if( bUseSrcCred )
{
result = ldap_bind_s(m_srcLD,NULL,(WCHAR*)&srcCred, LDAP_AUTH_SSPI);
}
else
{
result = ldap_bind_s(m_srcLD,NULL,NULL, LDAP_AUTH_SSPI);
}
if ( result )
{
rc = LdapMapErrorToWin32(result);
}
else
{
bSrcGood = TRUE;
}
}
if ( ! rc )
{
err.DbgMsgWrite(0,L"Connecting to target");
// now try to connect to the target server
// m_tgtLD = ldap_openW(m_targetDSA, LDAP_PORT);
//replace ldap_open(servername,..) with ldap_init and set LDAP_OPT_AREC_EXCLUSIVE
//flag so that the following ldap calls (i.e. ldap_bind) will not need to
//unnecessarily query for the domain controller
m_tgtLD = ldap_initW(m_targetDSA, LDAP_PORT);
if ( m_tgtLD == NULL )
{
value = LdapGetLastError();
if (value == LDAP_SUCCESS )
{
rc = ERROR_CONNECTION_REFUSED;
}
else
{
rc = LdapMapErrorToWin32(result);
}
errLogMain.SysMsgWrite(ErrE, rc, DCT_MSG_CONNECT_ERROR_TARGET_SD, (WCHAR*)m_targetDSA, rc);
}
if ( m_tgtLD )
{
//set LDAP_OPT_AREC_EXCLUSIVE flag so that the following calls tp
//ldap_open will not need to unnecessarily query for the domain controller
flags = PtrToUlong(LDAP_OPT_ON);
ldap_set_option(m_tgtLD, LDAP_OPT_AREC_EXCLUSIVE, &flags);
err.DbgMsgWrite(0,L"Setting target options.");
flags = 0;
result = ldap_get_option(m_tgtLD,LDAP_OPT_REFERRALS,&flags);
if ( result )
{
rc = LdapMapErrorToWin32(result);
}
else
{
flags = PtrToUlong(LDAP_OPT_OFF);
result = ldap_set_option(m_tgtLD,LDAP_OPT_REFERRALS,&flags);
if ( result )
{
rc = LdapMapErrorToWin32(result);
}
}
if ( ! rc )
{
err.DbgMsgWrite(0,L"Binding to target.");
if ( bUseTgtCred )
{
result = ldap_bind_s(m_tgtLD,NULL,(PWCHAR)&tgtCred,LDAP_AUTH_SSPI);
}
else
{
result = ldap_bind_s(m_tgtLD,NULL,NULL,LDAP_AUTH_SSPI);
}
if ( result )
{
rc = LdapMapErrorToWin32(result);
}
if ( rc )
{
err.DbgMsgWrite(0,L"Bind to target failed,rc=%ld, ldapRC=0x%lx",rc,result);
}
else
{
err.DbgMsgWrite(0,L"Everything succeeded.");
}
}
}
}
if ( bSrcGood )
{
rc = 0;
}
if ( rc )
{
// if failure, clean up any sessions we may have opened
Close();
}
err.LogClose();
if ( logFileMain && *logFileMain )
{
errLogMain.LogClose();
}
if ( SUCCEEDED(rc))
return HRESULT_FROM_WIN32(rc);
else
return rc;
}
STDMETHODIMP CMover::Close()
{
// close any open connections
if ( m_srcLD )
{
ldap_unbind_s(m_srcLD);
m_srcLD = NULL;
}
if ( m_tgtLD )
{
ldap_unbind_s(m_tgtLD);
m_tgtLD = NULL;
}
return S_OK;
}
char * MakeNarrowString(PWCHAR strInput)
{
char * strResult = NULL;
ULONG len = 0;
if ( strInput )
{
len = WideCharToMultiByte(CP_ACP,
0,
strInput,
wcslen(strInput),
NULL,
0,
NULL,
NULL);
strResult = (PCHAR)malloc(len + 1);
if ( strResult )
{
WideCharToMultiByte(CP_ACP,
0,
strInput,
wcslen(strInput),
strResult,
len,
NULL,
NULL);
// make sure the resulting string is null terminated
strResult[len] = 0;
}
}
return strResult;
}
void StripDN(WCHAR * str)
{
int curr=0,i=0;
for ( curr=0,i=0; str[i] ; i++ )
{
if ( str[i] == L'\\' && str[i+1] == L'/' )
{
continue;
}
str[curr] = str[i];
curr++;
}
str[curr] = 0;
}
STDMETHODIMP CMover::MoveObject(BSTR sourcePath, BSTR targetRDN, BSTR targetOUPath )
{
WCHAR sTargetContainer[LEN_Path];
WCHAR sSourceDN[LEN_Path];
WCHAR sTargetRDN[LEN_Path];
WCHAR sTargetDSA[LEN_Path];
char * pTgtDSA = NULL;
WCHAR const * prefix = L"LDAP://";
HRESULT hr = S_OK;
// set up the arguments needed to call the interdomain move operation
UStrCpy(sTargetDSA,m_targetDSA);
pTgtDSA = MakeNarrowString(sTargetDSA);
// the source path and target OuPath are provided in the LDAP:// format
// get the target container, target DN, and source DN in canonical LDAP format
if ( !UStrICmp(targetOUPath,prefix,UStrLen(prefix)) )
{
WCHAR * start = wcschr(targetOUPath+UStrLen(prefix) + 1,L'/');
if ( start )
{
UStrCpy(sTargetContainer,start + 1);
}
else
{
// error!
hr = E_INVALIDARG;
}
}
if ( !UStrICmp(sourcePath,prefix,UStrLen(prefix)) )
{
WCHAR * start = wcschr(sourcePath+UStrLen(prefix)+1,L'/');
if ( start )
{
UStrCpy(sSourceDN,start+1);
UStrCpy(sTargetRDN,start + 1);
WCHAR * temp = wcschr(sTargetRDN,L',');
if ( temp )
{
(*temp) = 0;
}
}
else
{
// error!
hr = E_INVALIDARG;
}
}
StripDN(sSourceDN);
StripDN(sTargetRDN);
StripDN(sTargetContainer);
berval Value;
Value.bv_val = pTgtDSA;
Value.bv_len = strlen(pTgtDSA);
LDAPControl ServerControl;
LDAPControl * ServerControls[2];
LDAPControl * ClientControls = NULL;
ServerControl.ldctl_oid = LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID_W;
ServerControl.ldctl_value = Value;
ServerControl.ldctl_iscritical = TRUE;
ServerControls[0] = NULL;
ServerControls[0] = &ServerControl;
ServerControls[1] = NULL;
/*
DstDSA = dns name of dc
Dn = distinguished name of object to be moved
NewRdn = relative distinguished name of object
NewParent = distinguished name of new parent container
ServerControls= specify the LDAP operational control for cross domain move
*/
DWORD ldaprc = ldap_rename_ext_s(m_srcLD,
sSourceDN,
targetRDN,
sTargetContainer,
TRUE,
ServerControls,
&ClientControls
);
DWORD winrc = 0;
ULONG error;
ULONG result;
WCHAR logFile[LEN_Path];
if ( MoverTraceLogging(logFile) )
{
err.LogOpen(logFile,1);
}
if ( ldaprc )
{
result = ldap_get_option(m_srcLD, LDAP_OPT_SERVER_EXT_ERROR,&error);
if (! result )
{
winrc = error;
}
else
{
err.DbgMsgWrite(0,L"Failed to get extended error, result=%ld",result);
winrc = LdapMapErrorToWin32(ldaprc);
}
}
err.DbgMsgWrite(0,L"\nMoveObject(sSourceDN=%ls\n,sTargetRDN=%ls\n,sTargetContainer=%ls\n,pTargetDSA=%S) rc=%ld,ldapRC=%ld",
sSourceDN,targetRDN,sTargetContainer,pTgtDSA,winrc,ldaprc);
err.LogClose();
free(pTgtDSA);
if ( SUCCEEDED(winrc))
return HRESULT_FROM_WIN32(winrc);
else
return winrc;
}
STDMETHODIMP CMover::CheckMove(BSTR sourcePath, BSTR targetRDN, BSTR targetOUPath )
{
WCHAR sTargetContainer[LEN_Path];
WCHAR sSourceDN[LEN_Path];
WCHAR sTargetRDN[LEN_Path];
WCHAR sTargetDSA[LEN_Path];
char * pTgtDSA = NULL;
WCHAR const * prefix = L"LDAP://";
HRESULT hr = S_OK;
// set up the arguments needed to call the interdomain move operation
UStrCpy(sTargetDSA,m_targetDSA);
pTgtDSA = MakeNarrowString(sTargetDSA);
// the source path and target OuPath are provided in the LDAP:// format
// get the target container, target DN, and source DN in canonical LDAP format
if ( !UStrICmp(targetOUPath,prefix,UStrLen(prefix)) )
{
WCHAR * start = wcschr(targetOUPath+UStrLen(prefix) + 1,L'/');
if ( start )
{
UStrCpy(sTargetContainer,start + 1);
}
else
{
// error!
hr = E_INVALIDARG;
}
}
if ( !UStrICmp(sourcePath,prefix,UStrLen(prefix)) )
{
WCHAR * start = wcschr(sourcePath+UStrLen(prefix)+1,L'/');
if ( start )
{
UStrCpy(sSourceDN,start+1);
UStrCpy(sTargetRDN,start + 1);
WCHAR * temp = wcschr(sTargetRDN,L',');
if ( temp )
{
(*temp) = 0;
}
}
else
{
// error!
hr = E_INVALIDARG;
}
}
StripDN(sSourceDN);
StripDN(sTargetRDN);
StripDN(sTargetContainer);
berval Value;
// this call will just do the source domain checks, so pass in NULL for the target domain
Value.bv_val = NULL;
Value.bv_len = 0;
LDAPControl ServerControl;
LDAPControl * ServerControls[2];
LDAPControl * ClientControls = NULL;
ServerControl.ldctl_oid = LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID_W;
ServerControl.ldctl_value = Value;
ServerControl.ldctl_iscritical = TRUE;
ServerControls[0] = NULL;
ServerControls[0] = &ServerControl;
ServerControls[1] = NULL;
/*
DstDSA = dns name of dc
Dn = distinguished name of object to be moved
NewRdn = relative distinguished name of object
NewParent = distinguished name of new parent container
ServerControls= specify the LDAP operational control for cross domain move
*/
DWORD ldaprc = ldap_rename_ext_s(m_srcLD,
sSourceDN,
targetRDN,
sTargetContainer,
TRUE,
ServerControls,
&ClientControls
);
DWORD winrc = 0;
ULONG error;
ULONG result;
WCHAR logFile[LEN_Path];
if ( ldaprc )
{
result = ldap_get_option(m_srcLD, LDAP_OPT_SERVER_EXT_ERROR,&error);
if (! result )
{
winrc = error;
}
else
{
err.DbgMsgWrite(0,L"Failed to get extended error, result=%ld",result);
winrc = LdapMapErrorToWin32(ldaprc);
}
}
if ( MoverTraceLogging(logFile) )
{
err.LogOpen(logFile,1);
}
err.DbgMsgWrite(0,L"\nMoveObject(sSourceDN=%ls\n,sTargetRDN=%ls\n,sTargetContainer=%ls\n,pTargetDSA=%S) rc=%ld,ldapRC=%ld,result=%ld",
sSourceDN,targetRDN,sTargetContainer,pTgtDSA,winrc,ldaprc,result);
err.LogClose();
free(pTgtDSA);
if ( SUCCEEDED(winrc))
return HRESULT_FROM_WIN32(winrc);
else
return winrc;
}