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