Leaked source code of windows server 2003
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.

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