Source code of Windows XP (NT5)
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.

653 lines
18 KiB

  1. // Mover.cpp : Implementation of CMoveObjApp and DLL registration.
  2. #include "stdafx.h"
  3. #include <stdio.h>
  4. #include <basetsd.h>
  5. #include <ntdsapi.h>
  6. #include "MoveObj.h"
  7. #include "Mover.h"
  8. #include "UString.hpp"
  9. #include "EaLen.hpp"
  10. #include "ErrDct.hpp"
  11. #include "TReg.hpp"
  12. #include "ResStr.h"
  13. #include "winldap.h" // use the platform SDK version of winldap.h, which is in the project directory
  14. #define SECURITY_WIN32 1 // Needed for sspi.h
  15. #include <sspi.h> // Needed for ISC_REQ_DELEGATE
  16. #define LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID "1.2.840.113556.1.4.521"
  17. #define LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID_W L"1.2.840.113556.1.4.521"
  18. TErrorDct err;
  19. TErrorDct errLogMain;
  20. StringLoader gString;
  21. BOOL // ret- whether to perform trace logging to a file
  22. MoverTraceLogging(
  23. WCHAR * filename // out- filename to use for trace logging
  24. )
  25. {
  26. DWORD rc = 0;
  27. BOOL bFound = FALSE;
  28. TRegKey key;
  29. WCHAR fnW[MAX_PATH];
  30. rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),(HKEY)HKEY_LOCAL_MACHINE);
  31. if ( ! rc )
  32. {
  33. rc = key.ValueGetStr(L"MoveObjectLog",fnW,MAX_PATH);
  34. if ( ! rc )
  35. {
  36. if ( *fnW )
  37. {
  38. bFound = TRUE;
  39. UStrCpy(filename,fnW);
  40. }
  41. else
  42. {
  43. filename[0] = 0;
  44. }
  45. }
  46. }
  47. return bFound && filename[0];
  48. }
  49. // In the following function we are sending in the logFilename in the tgtCredDomain argument.
  50. // This is done since this is always called with a null value in the ADMT code. To be safe we
  51. // will check if the account value is null then we will treat this as a log file otherwise
  52. // we will need to treat this as credentials.
  53. STDMETHODIMP
  54. CMover::Connect(
  55. BSTR sourceComp, // in - source domain computer to connect to
  56. BSTR targetDSA, // in - target domain computer to connect to
  57. BSTR srcCredDomain, // in - credentials to use for source domain
  58. BSTR srcCredAccount, // in - credentials to use for source domain
  59. BSTR srcCredPassword, // in - credentials to use for source domain
  60. BSTR tgtCredDomain, // in - credentials to use for target domain
  61. BSTR tgtCredAccount, // in - credentials to use for target domain
  62. BSTR tgtCredPassword // in - credentials to use for target domain
  63. )
  64. {
  65. DWORD rc = 0;
  66. LONG value = 0;
  67. ULONG flags = 0;
  68. ULONG result = 0;
  69. SEC_WINNT_AUTH_IDENTITY srcCred;
  70. SEC_WINNT_AUTH_IDENTITY tgtCred;
  71. BOOL bUseSrcCred = FALSE;
  72. BOOL bUseTgtCred = FALSE;
  73. BOOL bSrcGood = FALSE;
  74. WCHAR * logFileMain;
  75. // strip off leading \\ if present
  76. if ( sourceComp && sourceComp[0] == L'\\' )
  77. {
  78. UStrCpy(m_sourceDSA,sourceComp + 2);
  79. }
  80. else
  81. {
  82. UStrCpy(m_sourceDSA,sourceComp);
  83. }
  84. if ( targetDSA && targetDSA[0] == L'\\' )
  85. {
  86. UStrCpy(m_targetDSA,targetDSA + 2);
  87. }
  88. else
  89. {
  90. UStrCpy(m_targetDSA,targetDSA);
  91. }
  92. // set up credentials structure to use for bind, if needed
  93. if ( srcCredDomain && *srcCredDomain && srcCredAccount && *srcCredAccount )
  94. {
  95. srcCred.User = srcCredAccount;
  96. srcCred.Domain = srcCredDomain;
  97. srcCred.Password = srcCredPassword;
  98. srcCred.UserLength = UStrLen(srcCred.User);
  99. srcCred.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  100. srcCred.DomainLength = UStrLen(srcCred.Domain);
  101. srcCred.PasswordLength = UStrLen(srcCred.Password);
  102. bUseSrcCred = TRUE;
  103. }
  104. if ( tgtCredAccount && *tgtCredAccount )
  105. {
  106. tgtCred.User = tgtCredAccount;
  107. tgtCred.Domain = tgtCredDomain;
  108. tgtCred.Password = tgtCredPassword;
  109. tgtCred.UserLength = UStrLen(tgtCred.User);
  110. tgtCred.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  111. tgtCred.DomainLength = UStrLen(tgtCred.Domain);
  112. tgtCred.PasswordLength = UStrLen(tgtCred.Password);
  113. bUseTgtCred = TRUE;
  114. }
  115. else if ( tgtCredDomain && *tgtCredDomain )
  116. {
  117. logFileMain = tgtCredDomain;
  118. if (*logFileMain)
  119. {
  120. errLogMain.LogOpen(logFileMain, 1);
  121. }
  122. }
  123. // Open LDAP connections to the source and target computers
  124. // first, connect to the source computer
  125. WCHAR logFile[LEN_Path];
  126. if ( MoverTraceLogging(logFile) )
  127. {
  128. err.LogOpen(logFile,1);
  129. }
  130. err.DbgMsgWrite(0,L"\n\nMoveObject::Connect(%ls,%ls)",m_sourceDSA,m_targetDSA);
  131. // m_srcLD = ldap_openW(m_sourceDSA, LDAP_PORT);
  132. //replace ldap_open(servername,..) with ldap_init and set LDAP_OPT_AREC_EXCLUSIVE
  133. //flag so that the following ldap calls (i.e. ldap_bind) will not need to
  134. //unnecessarily query for the domain controller
  135. m_srcLD = ldap_initW(m_sourceDSA, LDAP_PORT);
  136. if ( m_srcLD == NULL )
  137. {
  138. value = LdapGetLastError();
  139. if (value == LDAP_SUCCESS )
  140. {
  141. rc = ERROR_CONNECTION_REFUSED;
  142. }
  143. else
  144. {
  145. rc = LdapMapErrorToWin32(result);
  146. }
  147. errLogMain.SysMsgWrite(ErrE, rc, DCT_MSG_CONNECT_ERROR_SOURCE_SD, (WCHAR*)m_sourceDSA, rc);
  148. }
  149. if ( m_srcLD )
  150. {
  151. //set LDAP_OPT_AREC_EXCLUSIVE flag so that the following calls tp
  152. //ldap_open will not need to unnecessarily query for the domain controller
  153. flags = PtrToUlong(LDAP_OPT_ON);
  154. ldap_set_option(m_srcLD, LDAP_OPT_AREC_EXCLUSIVE, &flags);
  155. err.DbgMsgWrite(0,L"Setting source options");
  156. flags = 0;
  157. // set the delegation flag for the source handle
  158. result = ldap_get_option(m_srcLD, LDAP_OPT_SSPI_FLAGS,&flags);
  159. if ( result )
  160. {
  161. rc = LdapMapErrorToWin32(result);
  162. }
  163. else
  164. {
  165. flags |= ISC_REQ_DELEGATE;
  166. result = ldap_set_option(m_srcLD,LDAP_OPT_SSPI_FLAGS, &flags);
  167. if ( result )
  168. {
  169. rc = LdapMapErrorToWin32(result);
  170. }
  171. }
  172. }
  173. if ( ! rc )
  174. {
  175. err.DbgMsgWrite(0,L"Binding to source");
  176. // try to bind to the source LDAP server
  177. if( bUseSrcCred )
  178. {
  179. result = ldap_bind_s(m_srcLD,NULL,(WCHAR*)&srcCred, LDAP_AUTH_SSPI);
  180. }
  181. else
  182. {
  183. result = ldap_bind_s(m_srcLD,NULL,NULL, LDAP_AUTH_SSPI);
  184. }
  185. if ( result )
  186. {
  187. rc = LdapMapErrorToWin32(result);
  188. }
  189. else
  190. {
  191. bSrcGood = TRUE;
  192. }
  193. }
  194. if ( ! rc )
  195. {
  196. err.DbgMsgWrite(0,L"Connecting to target");
  197. // now try to connect to the target server
  198. // m_tgtLD = ldap_openW(m_targetDSA, LDAP_PORT);
  199. //replace ldap_open(servername,..) with ldap_init and set LDAP_OPT_AREC_EXCLUSIVE
  200. //flag so that the following ldap calls (i.e. ldap_bind) will not need to
  201. //unnecessarily query for the domain controller
  202. m_tgtLD = ldap_initW(m_targetDSA, LDAP_PORT);
  203. if ( m_tgtLD == NULL )
  204. {
  205. value = LdapGetLastError();
  206. if (value == LDAP_SUCCESS )
  207. {
  208. rc = ERROR_CONNECTION_REFUSED;
  209. }
  210. else
  211. {
  212. rc = LdapMapErrorToWin32(result);
  213. }
  214. errLogMain.SysMsgWrite(ErrE, rc, DCT_MSG_CONNECT_ERROR_TARGET_SD, (WCHAR*)m_targetDSA, rc);
  215. }
  216. if ( m_tgtLD )
  217. {
  218. //set LDAP_OPT_AREC_EXCLUSIVE flag so that the following calls tp
  219. //ldap_open will not need to unnecessarily query for the domain controller
  220. flags = PtrToUlong(LDAP_OPT_ON);
  221. ldap_set_option(m_tgtLD, LDAP_OPT_AREC_EXCLUSIVE, &flags);
  222. err.DbgMsgWrite(0,L"Setting target options.");
  223. flags = 0;
  224. result = ldap_get_option(m_tgtLD,LDAP_OPT_REFERRALS,&flags);
  225. if ( result )
  226. {
  227. rc = LdapMapErrorToWin32(result);
  228. }
  229. else
  230. {
  231. flags = PtrToUlong(LDAP_OPT_OFF);
  232. result = ldap_set_option(m_tgtLD,LDAP_OPT_REFERRALS,&flags);
  233. if ( result )
  234. {
  235. rc = LdapMapErrorToWin32(result);
  236. }
  237. }
  238. if ( ! rc )
  239. {
  240. err.DbgMsgWrite(0,L"Binding to target.");
  241. if ( bUseTgtCred )
  242. {
  243. result = ldap_bind_s(m_tgtLD,NULL,(PWCHAR)&tgtCred,LDAP_AUTH_SSPI);
  244. }
  245. else
  246. {
  247. result = ldap_bind_s(m_tgtLD,NULL,NULL,LDAP_AUTH_SSPI);
  248. }
  249. if ( result )
  250. {
  251. rc = LdapMapErrorToWin32(result);
  252. }
  253. if ( rc )
  254. {
  255. err.DbgMsgWrite(0,L"Bind to target failed,rc=%ld, ldapRC=0x%lx",rc,result);
  256. }
  257. else
  258. {
  259. err.DbgMsgWrite(0,L"Everything succeeded.");
  260. }
  261. }
  262. }
  263. }
  264. if ( bSrcGood )
  265. {
  266. rc = 0;
  267. }
  268. if ( rc )
  269. {
  270. // if failure, clean up any sessions we may have opened
  271. Close();
  272. }
  273. err.LogClose();
  274. if ( logFileMain && *logFileMain )
  275. {
  276. errLogMain.LogClose();
  277. }
  278. if ( SUCCEEDED(rc))
  279. return HRESULT_FROM_WIN32(rc);
  280. else
  281. return rc;
  282. }
  283. STDMETHODIMP CMover::Close()
  284. {
  285. // close any open connections
  286. if ( m_srcLD )
  287. {
  288. ldap_unbind_s(m_srcLD);
  289. m_srcLD = NULL;
  290. }
  291. if ( m_tgtLD )
  292. {
  293. ldap_unbind_s(m_tgtLD);
  294. m_tgtLD = NULL;
  295. }
  296. return S_OK;
  297. }
  298. char * MakeNarrowString(PWCHAR strInput)
  299. {
  300. char * strResult = NULL;
  301. ULONG len = 0;
  302. if ( strInput )
  303. {
  304. len = WideCharToMultiByte(CP_ACP,
  305. 0,
  306. strInput,
  307. wcslen(strInput),
  308. NULL,
  309. 0,
  310. NULL,
  311. NULL);
  312. strResult = (PCHAR)malloc(len + 1);
  313. if ( strResult )
  314. {
  315. WideCharToMultiByte(CP_ACP,
  316. 0,
  317. strInput,
  318. wcslen(strInput),
  319. strResult,
  320. len,
  321. NULL,
  322. NULL);
  323. // make sure the resulting string is null terminated
  324. strResult[len] = 0;
  325. }
  326. }
  327. return strResult;
  328. }
  329. void StripDN(WCHAR * str)
  330. {
  331. int curr=0,i=0;
  332. for ( curr=0,i=0; str[i] ; i++ )
  333. {
  334. if ( str[i] == L'\\' && str[i+1] == L'/' )
  335. {
  336. continue;
  337. }
  338. str[curr] = str[i];
  339. curr++;
  340. }
  341. str[curr] = 0;
  342. }
  343. STDMETHODIMP CMover::MoveObject(BSTR sourcePath, BSTR targetRDN, BSTR targetOUPath )
  344. {
  345. WCHAR sTargetContainer[LEN_Path];
  346. WCHAR sSourceDN[LEN_Path];
  347. WCHAR sTargetRDN[LEN_Path];
  348. WCHAR sTargetDSA[LEN_Path];
  349. char * pTgtDSA = NULL;
  350. WCHAR const * prefix = L"LDAP://";
  351. HRESULT hr = S_OK;
  352. // set up the arguments needed to call the interdomain move operation
  353. UStrCpy(sTargetDSA,m_targetDSA);
  354. pTgtDSA = MakeNarrowString(sTargetDSA);
  355. // the source path and target OuPath are provided in the LDAP:// format
  356. // get the target container, target DN, and source DN in canonical LDAP format
  357. if ( !UStrICmp(targetOUPath,prefix,UStrLen(prefix)) )
  358. {
  359. WCHAR * start = wcschr(targetOUPath+UStrLen(prefix) + 1,L'/');
  360. if ( start )
  361. {
  362. UStrCpy(sTargetContainer,start + 1);
  363. }
  364. else
  365. {
  366. // error!
  367. hr = E_INVALIDARG;
  368. }
  369. }
  370. if ( !UStrICmp(sourcePath,prefix,UStrLen(prefix)) )
  371. {
  372. WCHAR * start = wcschr(sourcePath+UStrLen(prefix)+1,L'/');
  373. if ( start )
  374. {
  375. UStrCpy(sSourceDN,start+1);
  376. UStrCpy(sTargetRDN,start + 1);
  377. WCHAR * temp = wcschr(sTargetRDN,L',');
  378. if ( temp )
  379. {
  380. (*temp) = 0;
  381. }
  382. }
  383. else
  384. {
  385. // error!
  386. hr = E_INVALIDARG;
  387. }
  388. }
  389. StripDN(sSourceDN);
  390. StripDN(sTargetRDN);
  391. StripDN(sTargetContainer);
  392. berval Value;
  393. Value.bv_val = pTgtDSA;
  394. Value.bv_len = strlen(pTgtDSA);
  395. LDAPControl ServerControl;
  396. LDAPControl * ServerControls[2];
  397. LDAPControl * ClientControls = NULL;
  398. ServerControl.ldctl_oid = LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID_W;
  399. ServerControl.ldctl_value = Value;
  400. ServerControl.ldctl_iscritical = TRUE;
  401. ServerControls[0] = NULL;
  402. ServerControls[0] = &ServerControl;
  403. ServerControls[1] = NULL;
  404. /*
  405. DstDSA = dns name of dc
  406. Dn = distinguished name of object to be moved
  407. NewRdn = relative distinguished name of object
  408. NewParent = distinguished name of new parent container
  409. ServerControls= specify the LDAP operational control for cross domain move
  410. */
  411. DWORD ldaprc = ldap_rename_ext_s(m_srcLD,
  412. sSourceDN,
  413. targetRDN,
  414. sTargetContainer,
  415. TRUE,
  416. ServerControls,
  417. &ClientControls
  418. );
  419. DWORD winrc = 0;
  420. ULONG error;
  421. ULONG result;
  422. WCHAR logFile[LEN_Path];
  423. if ( MoverTraceLogging(logFile) )
  424. {
  425. err.LogOpen(logFile,1);
  426. }
  427. if ( ldaprc )
  428. {
  429. result = ldap_get_option(m_srcLD, LDAP_OPT_SERVER_EXT_ERROR,&error);
  430. if (! result )
  431. {
  432. winrc = error;
  433. }
  434. else
  435. {
  436. err.DbgMsgWrite(0,L"Failed to get extended error, result=%ld",result);
  437. winrc = LdapMapErrorToWin32(ldaprc);
  438. }
  439. }
  440. err.DbgMsgWrite(0,L"\nMoveObject(sSourceDN=%ls\n,sTargetRDN=%ls\n,sTargetContainer=%ls\n,pTargetDSA=%S) rc=%ld,ldapRC=%ld",
  441. sSourceDN,targetRDN,sTargetContainer,pTgtDSA,winrc,ldaprc);
  442. err.LogClose();
  443. free(pTgtDSA);
  444. if ( SUCCEEDED(winrc))
  445. return HRESULT_FROM_WIN32(winrc);
  446. else
  447. return winrc;
  448. }
  449. STDMETHODIMP CMover::CheckMove(BSTR sourcePath, BSTR targetRDN, BSTR targetOUPath )
  450. {
  451. WCHAR sTargetContainer[LEN_Path];
  452. WCHAR sSourceDN[LEN_Path];
  453. WCHAR sTargetRDN[LEN_Path];
  454. WCHAR sTargetDSA[LEN_Path];
  455. char * pTgtDSA = NULL;
  456. WCHAR const * prefix = L"LDAP://";
  457. HRESULT hr = S_OK;
  458. // set up the arguments needed to call the interdomain move operation
  459. UStrCpy(sTargetDSA,m_targetDSA);
  460. pTgtDSA = MakeNarrowString(sTargetDSA);
  461. // the source path and target OuPath are provided in the LDAP:// format
  462. // get the target container, target DN, and source DN in canonical LDAP format
  463. if ( !UStrICmp(targetOUPath,prefix,UStrLen(prefix)) )
  464. {
  465. WCHAR * start = wcschr(targetOUPath+UStrLen(prefix) + 1,L'/');
  466. if ( start )
  467. {
  468. UStrCpy(sTargetContainer,start + 1);
  469. }
  470. else
  471. {
  472. // error!
  473. hr = E_INVALIDARG;
  474. }
  475. }
  476. if ( !UStrICmp(sourcePath,prefix,UStrLen(prefix)) )
  477. {
  478. WCHAR * start = wcschr(sourcePath+UStrLen(prefix)+1,L'/');
  479. if ( start )
  480. {
  481. UStrCpy(sSourceDN,start+1);
  482. UStrCpy(sTargetRDN,start + 1);
  483. WCHAR * temp = wcschr(sTargetRDN,L',');
  484. if ( temp )
  485. {
  486. (*temp) = 0;
  487. }
  488. }
  489. else
  490. {
  491. // error!
  492. hr = E_INVALIDARG;
  493. }
  494. }
  495. StripDN(sSourceDN);
  496. StripDN(sTargetRDN);
  497. StripDN(sTargetContainer);
  498. berval Value;
  499. // this call will just do the source domain checks, so pass in NULL for the target domain
  500. Value.bv_val = NULL;
  501. Value.bv_len = 0;
  502. LDAPControl ServerControl;
  503. LDAPControl * ServerControls[2];
  504. LDAPControl * ClientControls = NULL;
  505. ServerControl.ldctl_oid = LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID_W;
  506. ServerControl.ldctl_value = Value;
  507. ServerControl.ldctl_iscritical = TRUE;
  508. ServerControls[0] = NULL;
  509. ServerControls[0] = &ServerControl;
  510. ServerControls[1] = NULL;
  511. /*
  512. DstDSA = dns name of dc
  513. Dn = distinguished name of object to be moved
  514. NewRdn = relative distinguished name of object
  515. NewParent = distinguished name of new parent container
  516. ServerControls= specify the LDAP operational control for cross domain move
  517. */
  518. DWORD ldaprc = ldap_rename_ext_s(m_srcLD,
  519. sSourceDN,
  520. targetRDN,
  521. sTargetContainer,
  522. TRUE,
  523. ServerControls,
  524. &ClientControls
  525. );
  526. DWORD winrc = 0;
  527. ULONG error;
  528. ULONG result;
  529. WCHAR logFile[LEN_Path];
  530. if ( ldaprc )
  531. {
  532. result = ldap_get_option(m_srcLD, LDAP_OPT_SERVER_EXT_ERROR,&error);
  533. if (! result )
  534. {
  535. winrc = error;
  536. }
  537. else
  538. {
  539. err.DbgMsgWrite(0,L"Failed to get extended error, result=%ld",result);
  540. winrc = LdapMapErrorToWin32(ldaprc);
  541. }
  542. }
  543. if ( MoverTraceLogging(logFile) )
  544. {
  545. err.LogOpen(logFile,1);
  546. }
  547. err.DbgMsgWrite(0,L"\nMoveObject(sSourceDN=%ls\n,sTargetRDN=%ls\n,sTargetContainer=%ls\n,pTargetDSA=%S) rc=%ld,ldapRC=%ld,result=%ld",
  548. sSourceDN,targetRDN,sTargetContainer,pTgtDSA,winrc,ldaprc,result);
  549. err.LogClose();
  550. free(pTgtDSA);
  551. if ( SUCCEEDED(winrc))
  552. return HRESULT_FROM_WIN32(winrc);
  553. else
  554. return winrc;
  555. }