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.

1182 lines
40 KiB

  1. //#pragma title("usercopy- copies user accounts")
  2. /*
  3. ================================================================================
  4. (c) Copyright 1995-1998, Mission Critical Software, Inc., All Rights Reserved
  5. Proprietary and confidential to Mission Critical Software, Inc.
  6. Program - usercopy
  7. Class - LAN Manager Utilities
  8. Author - Tom Bernhardt
  9. Created - 05/08/91
  10. Description- Merges the NetUser information from the specified source
  11. server with the target system (or the current system if no
  12. target is specified). Group information is also merged if
  13. the /g option is given. Existing entries on the target system
  14. are not overwritten unless the /r option is used.
  15. Syntax - USERCOPY source [target] [/u] [/l] [/g] [/r]
  16. where:
  17. source source server
  18. target destination server
  19. /g copies global group information
  20. /l copies local group information
  21. /u copies user information
  22. /r replaces existing target entries with source entries
  23. /AddTo:x Adds all newly created users (/u) to group "x"
  24. Updates -
  25. 91/06/17 TPB General code cleanup and change so that all stdout i/o lines up
  26. nicely on-screen for reporting.
  27. 93/06/12 TPB Port to Win32
  28. 96/06/21 TPB Support for local groups
  29. 97/09/20 CAB Added subset of accounts option for GUI
  30. 98/06 TPB/CAB Support for computer accounts
  31. 99/01 COM-ization of DCT.
  32. ================================================================================
  33. */
  34. #include "StdAfx.h"
  35. #include <stdlib.h>
  36. #include <stdio.h>
  37. #include <string.h>
  38. #include <ctype.h>
  39. #include <ntdsapi.h>
  40. #include <lm.h>
  41. #include <iads.h>
  42. #include "TxtSid.h"
  43. #define INCL_NETUSER
  44. #define INCL_NETGROUP
  45. #define INCL_NETERRORS
  46. #include <lm.h>
  47. #include "Common.hpp"
  48. #include "UString.hpp"
  49. #include "WorkObj.h"
  50. //#include "Usercopy.hpp" //#included by ARUtil.hpp below
  51. #include "ARUtil.hpp"
  52. #include "BkupRstr.hpp"
  53. #include "DCTStat.h"
  54. #include "ErrDct.hpp"
  55. #include "RegTrans.h"
  56. #include "TEvent.hpp"
  57. #include "LSAUtils.h"
  58. #include "GetDcName.h"
  59. #include <sddl.h>
  60. //#import "\bin\NetEnum.tlb" no_namespace
  61. //#import "\bin\DBManager.tlb" no_namespace, named_guids
  62. #import "NetEnum.tlb" no_namespace
  63. //#import "DBMgr.tlb" no_namespace, named_guids //already #imported via ARUtil.hpp
  64. #ifdef _DEBUG
  65. #define new DEBUG_NEW
  66. #undef THIS_FILE
  67. static char THIS_FILE[] = __FILE__;
  68. #endif
  69. extern TErrorDct err;
  70. extern TErrorDct & errC;
  71. bool abortall;
  72. extern bool g_bAddSidWorks = false;
  73. // global counts of accounts processed
  74. AccountStats warnings = { 0,0,0,0 };
  75. AccountStats errors = { 0,0,0,0 };
  76. AccountStats created = { 0,0,0,0 };
  77. AccountStats replaced = { 0,0,0,0 };
  78. AccountStats processed = { 0,0,0,0 };
  79. BOOL machineAcctsCreated = FALSE;
  80. BOOL otherAcctsCreated = FALSE;
  81. PSID srcSid = NULL; // SID for source domain
  82. typedef UINT (CALLBACK* DSBINDFUNC)(TCHAR*, TCHAR*, HANDLE*);
  83. typedef UINT (CALLBACK* DSADDSIDHISTORY)(HANDLE, DWORD, LPCTSTR, LPCTSTR, LPCTSTR, RPC_AUTH_IDENTITY_HANDLE,LPCTSTR,LPCTSTR);
  84. #ifndef IADsPtr
  85. _COM_SMARTPTR_TYPEDEF(IADs, IID_IADs);
  86. #endif
  87. int TNodeCompareSourceName(TNode const * t1,TNode const * t2)
  88. {
  89. TAcctReplNode const * n1 = (TAcctReplNode *)t1;
  90. TAcctReplNode const * n2 = (TAcctReplNode *)t2;
  91. return UStrICmp(n1->GetName(),n2->GetName());
  92. }
  93. int TNodeCompareSourceNameValue(TNode const * t1, void const * v)
  94. {
  95. TAcctReplNode const * n1 = (TAcctReplNode *)t1;
  96. WCHAR const * name = (WCHAR const *)v;
  97. return UStrICmp(n1->GetName(),name);
  98. }
  99. bool BindToDS(Options* pOpt)
  100. {
  101. // Get the handle to the Directory service.
  102. DSBINDFUNC DsBind;
  103. HINSTANCE hInst = LoadLibrary(L"NTDSAPI.DLL");
  104. if ( hInst )
  105. {
  106. DsBind = (DSBINDFUNC) GetProcAddress(hInst, "DsBindW");
  107. if (DsBind)
  108. {
  109. //
  110. // If source domain controllers are running W2K or later then specify
  111. // DNS name of target domain controller otherwise must specify flat
  112. // (NetBIOS) name of target domain controller.
  113. //
  114. // Note that this is a requirement of the DsAddSidHistory implementation
  115. // when the source domain is NT4 and explicit source domain credentials
  116. // are not supplied. As ADMT does not supply explicit credentials this
  117. // will always be the case when the source domain is NT4.
  118. //
  119. PWSTR strDestDC = (pOpt->srcDomainVer > 4) ? pOpt->tgtCompDns : pOpt->tgtCompFlat;
  120. DWORD rc = DsBind(strDestDC, NULL, &pOpt->dsBindHandle);
  121. if ( rc != 0 )
  122. {
  123. err.SysMsgWrite( ErrE, rc, DCT_MSG_DSBIND_FAILED_S, strDestDC);
  124. Mark(L"errors", L"generic");
  125. FreeLibrary(hInst);
  126. return false;
  127. }
  128. }
  129. else
  130. {
  131. err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_GET_PROC_ADDRESS_FAILED_SSD,L"NTDSAPI.DLL",L"DsBindW",GetLastError());
  132. Mark(L"errors", L"generic");
  133. FreeLibrary(hInst);
  134. return false;
  135. }
  136. }
  137. else
  138. {
  139. err.SysMsgWrite(ErrW,GetLastError(),DCT_MSG_LOAD_LIBRARY_FAILED_SD,L"NTDSAPI.DLL",GetLastError());
  140. Mark(L"warnings", L"generic");
  141. return false;
  142. }
  143. FreeLibrary(hInst);
  144. return true;
  145. }
  146. // The following function is used to get the actual account name from the source domain
  147. // instead of account that contains the SID in its SID history.
  148. DWORD GetName(PSID pObjectSID, WCHAR * sNameAccount, WCHAR * sDomain)
  149. {
  150. DWORD cb = 255;
  151. DWORD cbDomain = 255;
  152. DWORD tempVal;
  153. PDWORD psubAuth;
  154. PUCHAR pVal;
  155. SID_NAME_USE sid_Use;
  156. _bstr_t sDC;
  157. DWORD rc = 0;
  158. if ((pObjectSID == NULL) || !IsValidSid(pObjectSID))
  159. {
  160. return ERROR_INVALID_PARAMETER;
  161. }
  162. // Copy the Sid to a temp SID
  163. DWORD sidLen = GetLengthSid(pObjectSID);
  164. PSID pObjectSID1 = new BYTE[sidLen];
  165. if (!pObjectSID1)
  166. return ERROR_NOT_ENOUGH_MEMORY;
  167. if (!CopySid(sidLen, pObjectSID1, pObjectSID))
  168. {
  169. delete pObjectSID1;
  170. return GetLastError();
  171. }
  172. if (!IsValidSid(pObjectSID1))
  173. {
  174. rc = GetLastError();
  175. err.SysMsgWrite(ErrE, rc,DCT_MSG_DOMAIN_LOOKUP_FAILED_D,rc);
  176. try
  177. {
  178. Mark(L"errors", L"generic");
  179. }
  180. catch (...)
  181. {
  182. }
  183. delete pObjectSID1;
  184. return rc;
  185. }
  186. // Get the RID out of the SID and get the domain SID
  187. pVal = GetSidSubAuthorityCount(pObjectSID1);
  188. (*pVal)--;
  189. psubAuth = GetSidSubAuthority(pObjectSID1, *pVal);
  190. tempVal = *psubAuth;
  191. *psubAuth = -1;
  192. //Lookup the domain from the SID
  193. if (!LookupAccountSid(NULL, pObjectSID1, sNameAccount, &cb, sDomain, &cbDomain, &sid_Use))
  194. {
  195. rc = GetLastError();
  196. err.SysMsgWrite(ErrE, rc,DCT_MSG_DOMAIN_LOOKUP_FAILED_D,rc);
  197. Mark(L"errors", L"generic");
  198. delete pObjectSID1;
  199. return rc;
  200. }
  201. // Get a DC for the domain
  202. rc = GetAnyDcName4(sDomain, sDC);
  203. if ( rc )
  204. {
  205. err.SysMsgWrite(ErrE,rc,DCT_MSG_GET_DCNAME_FAILED_SD,sDomain,rc);
  206. Mark(L"errors", L"generic");
  207. delete pObjectSID1;
  208. return rc;
  209. }
  210. // Reset the sizes
  211. cb = 255;
  212. cbDomain = 255;
  213. // Lookup the account on the PDC that we found above.
  214. if ( LookupAccountSid(sDC, pObjectSID, sNameAccount, &cb, sDomain, &cbDomain, &sid_Use) == 0)
  215. {
  216. delete pObjectSID1;
  217. return GetLastError();
  218. }
  219. delete pObjectSID1;
  220. return 0;
  221. }
  222. /* This is a list of specific error codes that can be returned by DsAddSidHistory.
  223. This was obtained from Microsoft via email
  224. > ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED
  225. > The operation requires that destination domain auditing be enabled for
  226. > Success and Failure auditing of account management operations.
  227. >
  228. > ERROR_DS_UNWILLING_TO_PERFORM
  229. > It may be that the user account is not one of UF_NORMAL_ACCOUNT,
  230. > UF_WORKSTATION_TRUST_ACCOUNT, or UF_SERVER_TRUST_ACCOUNT.
  231. >
  232. > It may be that the source principal is a built in account.
  233. >
  234. > It may be that the source principal is a well known RID being added
  235. > to a destination principal that is a different RID. In other words,
  236. > Administrators of the source domain can only be assigned to
  237. > Administrators of the destination domain.
  238. >
  239. > ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER
  240. > The source object must be a group or user.
  241. >
  242. > ERROR_DS_SRC_SID_EXISTS_IN_FOREST
  243. > The source object's SID already exists in destination forest.
  244. >
  245. > ERROR_DS_INTERNAL_FAILURE;
  246. > The directory service encountered an internal failure. Shouldn't
  247. > happen.
  248. >
  249. > ERROR_DS_MUST_BE_RUN_ON_DST_DC
  250. > For security reasons, the operation must be run on the destination DC.
  251. > Specifically, the connection between the client and server
  252. > (destination
  253. > DC) requires 128-bit encryption when credentials for the source domain
  254. > are supplied.
  255. >
  256. > ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION
  257. > The connection between client and server requires packet privacy or
  258. > better.
  259. >
  260. > ERROR_DS_SOURCE_DOMAIN_IN_FOREST
  261. > The source domain may not be in the same forest as destination.
  262. >
  263. > ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST
  264. > The destination domain must be in the forest.
  265. >
  266. > ERROR_DS_MASTERDSA_REQUIRED
  267. > The operation must be performed at a master DSA (writable DC).
  268. >
  269. > ERROR_DS_INSUFF_ACCESS_RIGHTS
  270. > Insufficient access rights to perform the operation. Most likely
  271. > the caller is not a member of domain admins for the dst domain.
  272. >
  273. > ERROR_DS_DST_DOMAIN_NOT_NATIVE
  274. > Destination domain must be in native mode.
  275. >
  276. > ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN
  277. > The operation couldn't locate a DC for the source domain.
  278. >
  279. > ERROR_DS_OBJ_NOT_FOUND
  280. > Directory object not found. Most likely the FQDN of the
  281. > destination principal could not be found in the destination
  282. > domain.
  283. >
  284. > ERROR_DS_NAME_ERROR_NOT_UNIQUE
  285. > Name translation: Input name mapped to more than one
  286. > output name. Most likely the destination principal mapped
  287. > to more than one FQDN in the destination domain.
  288. >
  289. > ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH
  290. > The source and destination object must be of the same type.
  291. >
  292. > ERROR_DS_OBJ_CLASS_VIOLATION
  293. > The requested operation did not satisfy one or more constraints
  294. > associated with the class of the object. Most likely because the
  295. > destination principal is not a user or group.
  296. >
  297. > ERROR_DS_UNAVAILABLE
  298. > The directory service is unavailable. Most likely the
  299. > ldap_initW() to the NT5 src DC failed.
  300. >
  301. > ERROR_DS_INAPPROPRIATE_AUTH
  302. > Inappropriate authentication. Most likely the ldap_bind_sW() to
  303. > the NT5 src dc failed.
  304. >
  305. > ERROR_DS_SOURCE_AUDITING_NOT_ENABLED
  306. > The operation requires that source domain auditing be enabled for
  307. > Success and Failure auditing of account management operations.
  308. >
  309. > ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER
  310. > For security reasons, the source DC must be Service Pack 4 or greater.
  311. >
  312. */
  313. HRESULT
  314. CopySidHistoryProperty(
  315. Options * pOptions,
  316. TAcctReplNode * pNode,
  317. IStatusObj * pStatus
  318. )
  319. {
  320. HRESULT hr = S_OK;
  321. IADs * pAds = NULL;
  322. _variant_t var;
  323. // long ub = 0, lb = 0;
  324. // fetch the SIDHistory property for the source account
  325. // for each entry in the source's SIDHistory, call DsAddSidHistory
  326. // Get the IADs pointer to the object and get the SIDHistory attribute.
  327. hr = ADsGetObject(const_cast<WCHAR*>(pNode->GetSourcePath()), IID_IADs, (void**)&pAds);
  328. if ( SUCCEEDED(hr) )
  329. {
  330. hr = pAds->Get(L"sIDHistory", &var);
  331. }
  332. if ( SUCCEEDED(hr) )
  333. {
  334. // This is a multivalued property so we need to get all the values
  335. // for each one get the name and the domain of the object and then call the
  336. // add sid history function to add the SID to the target objects SIDHistory.
  337. _variant_t var;
  338. DWORD rc = pAds->GetEx(L"sIDHistory", &var);
  339. if ( !rc )
  340. {
  341. if ( V_VT(&var) == (VT_ARRAY | VT_VARIANT) )
  342. {
  343. // This is the array type that we were looking for.
  344. void HUGEP *pArray;
  345. VARIANT var2;
  346. ULONG dwSLBound = -1;
  347. ULONG dwSUBound = -1;
  348. hr = SafeArrayGetLBound( V_ARRAY(&var),
  349. 1,
  350. (long FAR *) &dwSLBound );
  351. hr = SafeArrayGetUBound( V_ARRAY(&var),
  352. 1,
  353. (long FAR *) &dwSUBound );
  354. if (SUCCEEDED(hr))
  355. {
  356. // Each element in this array is a SID in form of a VARIANT
  357. hr = SafeArrayAccessData( V_ARRAY(&var), &pArray );
  358. for ( long x = (long)dwSLBound; x <= (long)dwSUBound; x++)
  359. {
  360. hr = SafeArrayGetElement(V_ARRAY(&var), &x, &var2);
  361. // Get the SID from the Variant in a ARRAY form
  362. hr = SafeArrayAccessData( V_ARRAY(&var2), &pArray );
  363. PSID pObjectSID = (PSID)pArray;
  364. //Convert SID to string.
  365. if (pObjectSID)
  366. {
  367. WCHAR sNameAccount[255];
  368. WCHAR sDomain[255];
  369. WCHAR sNetBIOS[255];
  370. DWORD rc = 0;
  371. rc = GetName(pObjectSID, sNameAccount, sDomain);
  372. if (!rc)
  373. {
  374. WCHAR sTemp[LEN_Path];
  375. WCHAR sSourceDNS[LEN_Path];
  376. // We are going to temporarily change the Domain DNS to the domain of the SID we are adding
  377. wcscpy(sTemp, pOptions->srcDomainDns);
  378. if ( GetDnsAndNetbiosFromName(sDomain, sNetBIOS, sSourceDNS) )
  379. {
  380. wcscpy(pOptions->srcDomainDns, sSourceDNS);
  381. AddSidHistory(pOptions, sNameAccount, pNode->GetTargetSam(), NULL, FALSE);
  382. // Replace the original domain dns.
  383. wcscpy(pOptions->srcDomainDns, sTemp);
  384. }
  385. else
  386. {
  387. err.SysMsgWrite(ErrE, GetLastError(),DCT_MSG_DOMAIN_DNS_LOOKUP_FAILED_SD, sDomain,GetLastError());
  388. Mark(L"errors", pNode->GetType());
  389. }
  390. }
  391. else
  392. {
  393. // Get name failed we need to log a message.
  394. WCHAR sSid[LEN_Path];
  395. DWORD len = LEN_Path;
  396. GetTextualSid(pObjectSID, sSid, &len);
  397. err.SysMsgWrite(ErrE,rc,DCT_MSG_ERROR_CONVERTING_SID_SSD,
  398. pNode->GetTargetName(), sSid, rc);
  399. Mark(L"errors", pNode->GetType());
  400. }
  401. }
  402. SafeArrayUnaccessData(V_ARRAY(&var2));
  403. }
  404. SafeArrayUnaccessData(V_ARRAY(&var));
  405. }
  406. }
  407. }
  408. else
  409. {
  410. // No SID History to copy.
  411. }
  412. }
  413. return hr;
  414. }
  415. bool AddSidHistory( const Options * pOptions,
  416. const WCHAR * strSrcPrincipal,
  417. const WCHAR * strDestPrincipal,
  418. IStatusObj * pStatus,
  419. BOOL isFatal)
  420. {
  421. //Add the sid to the history
  422. // Authentication Structure
  423. SEC_WINNT_AUTH_IDENTITY auth;
  424. DWORD rc = 0;
  425. WCHAR szPassword[LEN_Password];
  426. auth.Domain = const_cast<WCHAR*>(pOptions->authDomain);
  427. auth.DomainLength = wcslen(pOptions->authDomain);
  428. auth.User = const_cast<WCHAR*>(pOptions->authUser);
  429. auth.UserLength = wcslen(pOptions->authUser);
  430. //
  431. // If credentials were supplied then retrieve password.
  432. //
  433. if ((auth.DomainLength > 0) && (auth.UserLength > 0))
  434. {
  435. DWORD dwError = RetrievePassword(pOptions->authPassword, szPassword, sizeof(szPassword) / sizeof(szPassword[0]));
  436. if (dwError == ERROR_SUCCESS)
  437. {
  438. auth.Password = szPassword;
  439. auth.PasswordLength = wcslen(szPassword);
  440. }
  441. else
  442. {
  443. err.SysMsgWrite(ErrE, dwError, DCT_MSG_UNABLE_TO_RETRIEVE_PASSWORD);
  444. g_bAddSidWorks = FALSE;
  445. // log a message indicating that SIDHistory will not be tried for the rest of the accounts
  446. err.MsgWrite(ErrW,DCT_MSG_SIDHISTORY_FATAL_ERROR);
  447. Mark(L"warnings", L"generic");
  448. // we are going to set the status to abort so that we don't try to migrate anymore.
  449. if ( pStatus )
  450. {
  451. pStatus->put_Status(DCT_STATUS_ABORTING);
  452. }
  453. return false;
  454. }
  455. }
  456. else
  457. {
  458. auth.Password = NULL;
  459. auth.PasswordLength = 0;
  460. }
  461. auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  462. // Auth Identity handle
  463. // if source domain credentials supplied use them
  464. // otherwise credentials of caller will be used
  465. RPC_AUTH_IDENTITY_HANDLE pHandle = ((auth.DomainLength > 0) && (auth.UserLength > 0)) ? &auth : NULL;
  466. DSADDSIDHISTORY DsAddSidHistory;
  467. HINSTANCE hInst = LoadLibrary(L"NTDSAPI.DLL");
  468. if ( hInst )
  469. {
  470. DsAddSidHistory = (DSADDSIDHISTORY) GetProcAddress(hInst, "DsAddSidHistoryW");
  471. if (DsAddSidHistory)
  472. {
  473. if ( !pOptions->nochange )
  474. {
  475. int loopCount = 0;
  476. rc = RPC_S_SERVER_UNAVAILABLE;
  477. // If we get the RPC server errors we need to retry 5 times.
  478. while ( (((rc == RPC_S_SERVER_UNAVAILABLE) || (rc == RPC_S_CALL_FAILED) || (rc == RPC_S_CALL_FAILED_DNE)) && loopCount < 5)
  479. || ( (rc == ERROR_INVALID_HANDLE) && loopCount < 3 ) ) // In case of invalid handle we try it 3 times now.
  480. {
  481. // Make the API call to add Sid to the history
  482. rc = DsAddSidHistory(
  483. pOptions->dsBindHandle, //DS Handle
  484. NULL, // flags
  485. pOptions->srcDomain, // Source domain
  486. strSrcPrincipal, // Source Account name
  487. NULL, // Source Domain Controller
  488. pHandle, // RPC_AUTH_IDENTITY_HANDLE
  489. pOptions->tgtDomainDns, // Target domain
  490. strDestPrincipal); // Target Account name
  491. if ( loopCount > 0 ) Sleep(500);
  492. loopCount++;
  493. }
  494. }
  495. SecureZeroMemory(szPassword, sizeof(szPassword));
  496. if ( rc != 0 )
  497. {
  498. switch ( rc )
  499. {
  500. // these are the error codes caused by permissions or configuration problems
  501. case ERROR_NONE_MAPPED:
  502. err.MsgWrite(ErrE, DCT_MSG_ADDSIDHISTORY_FAIL_BUILTIN_SSD,strSrcPrincipal, strDestPrincipal, rc);
  503. break;
  504. case ERROR_DS_UNWILLING_TO_PERFORM:
  505. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_DS_UNWILLING_TO_PERFORM_SSSSD,strDestPrincipal,pOptions->srcDomain, strSrcPrincipal, pOptions->tgtDomain,rc);
  506. break;
  507. case ERROR_DS_INSUFF_ACCESS_RIGHTS:
  508. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_INSUFF_ACCESS_SD,strDestPrincipal,rc);
  509. g_bAddSidWorks = FALSE;
  510. break;
  511. case ERROR_INVALID_HANDLE:
  512. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_INVALID_HANDLE_SSD,pOptions->srcDomainDns,strDestPrincipal,rc);
  513. g_bAddSidWorks = FALSE;
  514. break;
  515. case ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED:
  516. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_NOAUDIT_SSD,strDestPrincipal,pOptions->tgtDomainDns,rc);
  517. g_bAddSidWorks = FALSE;
  518. break;
  519. case ERROR_DS_MUST_BE_RUN_ON_DST_DC:
  520. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_DST_DC_SD,strDestPrincipal,rc);
  521. g_bAddSidWorks = FALSE;
  522. break;
  523. case ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION:
  524. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_PKT_PRIVACY_SD,strDestPrincipal,rc);
  525. g_bAddSidWorks = FALSE;
  526. break;
  527. case ERROR_DS_SOURCE_DOMAIN_IN_FOREST:
  528. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_SOURCE_IN_FOREST_S,strDestPrincipal);
  529. g_bAddSidWorks = FALSE;
  530. break;
  531. case ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST:
  532. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_DEST_WRONG_FOREST_S,strDestPrincipal);
  533. g_bAddSidWorks = FALSE;
  534. break;
  535. case ERROR_DS_MASTERDSA_REQUIRED:
  536. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_NO_MASTERDSA_S,strDestPrincipal);
  537. g_bAddSidWorks = FALSE;
  538. break;
  539. case ERROR_ACCESS_DENIED:
  540. g_bAddSidWorks = FALSE;
  541. if (pHandle)
  542. {
  543. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_INSUFF2_SSS,strDestPrincipal,pOptions->authDomain,pOptions->authUser);
  544. }
  545. else
  546. {
  547. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_INSUFF2_S,strDestPrincipal);
  548. }
  549. break;
  550. case ERROR_DS_DST_DOMAIN_NOT_NATIVE:
  551. g_bAddSidWorks = FALSE;
  552. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_NOT_NATIVE_S,strDestPrincipal);
  553. break;
  554. case ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN:
  555. g_bAddSidWorks = FALSE;
  556. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_NO_SOURCE_DC_S,strDestPrincipal);
  557. break;
  558. // case ERROR_DS_INAPPROPRIATE_AUTH:
  559. case ERROR_DS_UNAVAILABLE:
  560. g_bAddSidWorks = FALSE;
  561. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_DS_UNAVAILABLE_S,strDestPrincipal);
  562. break;
  563. case ERROR_DS_SOURCE_AUDITING_NOT_ENABLED:
  564. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_NOAUDIT_SSD,strDestPrincipal,pOptions->srcDomain,rc);
  565. g_bAddSidWorks = FALSE;
  566. break;
  567. case ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER:
  568. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_SOURCE_NOT_SP4_S,strDestPrincipal);
  569. g_bAddSidWorks = FALSE;
  570. break;
  571. case ERROR_SESSION_CREDENTIAL_CONFLICT:
  572. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_CREDENTIALS_CONFLICT_SSSS,strDestPrincipal,pOptions->srcDomain,pOptions->authDomain,pOptions->authUser);
  573. g_bAddSidWorks = FALSE;
  574. break;
  575. // these are error codes that only affect this particular account
  576. case ERROR_SUCCESS:
  577. g_bAddSidWorks = TRUE;
  578. // no error message needed for success case!
  579. break;
  580. case ERROR_DS_SRC_SID_EXISTS_IN_FOREST:
  581. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_IN_FOREST_SD,strDestPrincipal,rc);
  582. g_bAddSidWorks = TRUE;
  583. break;
  584. case ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER:
  585. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_WRONGTYPE_SD,strDestPrincipal,rc);
  586. g_bAddSidWorks = TRUE;
  587. break;
  588. case ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH:
  589. err.MsgWrite(ErrE, DCT_MSG_SID_HISTORY_CLASS_MISMATCH_SSD, strDestPrincipal, strSrcPrincipal, rc);
  590. g_bAddSidWorks = TRUE;
  591. break;
  592. default:
  593. err.MsgWrite(ErrE,DCT_MSG_ADDSID_FAILED_SSD,strSrcPrincipal, strDestPrincipal,rc);
  594. g_bAddSidWorks = TRUE;
  595. break;
  596. }
  597. Mark(L"errors", L"generic");
  598. // This may or may not be a fatal error depending on weather we are Adding
  599. // sid history or copying sid history
  600. g_bAddSidWorks |= !(isFatal);
  601. if (! g_bAddSidWorks )
  602. {
  603. // log a message indicating that SIDHistory will not be tried for the rest of the accounts
  604. err.MsgWrite(ErrW,DCT_MSG_SIDHISTORY_FATAL_ERROR);
  605. Mark(L"warnings", L"generic");
  606. // we are going to set the status to abort so that we don't try to migrate anymore.
  607. if ( pStatus )
  608. {
  609. pStatus->put_Status(DCT_STATUS_ABORTING);
  610. }
  611. }
  612. FreeLibrary(hInst);
  613. return false;
  614. }
  615. else
  616. {
  617. err.MsgWrite(0, DCT_MSG_ADD_SID_SUCCESS_SSSS, pOptions->srcDomainFlat, strSrcPrincipal, pOptions->tgtDomainFlat, strDestPrincipal);
  618. FreeLibrary(hInst);
  619. return true;
  620. }
  621. }
  622. else
  623. {
  624. SecureZeroMemory(szPassword, sizeof(szPassword));
  625. err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_NO_ADDSIDHISTORY_FUNCTION);
  626. Mark(L"errors", L"generic");
  627. FreeLibrary(hInst);
  628. return false;
  629. }
  630. }
  631. else
  632. {
  633. SecureZeroMemory(szPassword, sizeof(szPassword));
  634. err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_NO_NTDSAPI_DLL);
  635. Mark(L"errors", L"generic");
  636. return false;
  637. }
  638. }
  639. //--------------------------------------------------------------------------
  640. // FillupNamingContext : This function fills in the target Naming context
  641. // for NT5 domain.
  642. //--------------------------------------------------------------------------
  643. void FillupNamingContext(
  644. Options * options //in,out- Options to fill up
  645. )
  646. {
  647. WCHAR sPath[LEN_Path];
  648. IADs * pAds;
  649. _variant_t var;
  650. HRESULT hr;
  651. wsprintf(sPath, L"LDAP://%s/rootDSE", options->tgtDomain);
  652. hr = ADsGetObject(sPath, IID_IADs, (void**)&pAds);
  653. if ( FAILED(hr) )
  654. {
  655. wcscpy(options->tgtNamingContext, L"");
  656. return;
  657. }
  658. hr = pAds->Get(L"defaultNamingContext", &var);
  659. if ( FAILED(hr) )
  660. {
  661. wcscpy(options->tgtNamingContext, L"");
  662. return;
  663. }
  664. pAds->Release();
  665. wcscpy(options->tgtNamingContext, (WCHAR*) V_BSTR(&var));
  666. }
  667. //--------------------------------------------------------------------------
  668. // MakeFullyQualifiedAdsPath : Makes a LDAP sub path into a fully qualified
  669. // LDAP path name.
  670. //--------------------------------------------------------------------------
  671. void MakeFullyQualifiedAdsPath(
  672. WCHAR * sPath, //out- Fully qulified LDAP path to the object
  673. DWORD nPathLen, //in - MAX size, in characters, of the sPath buffer
  674. WCHAR * sSubPath, //in- LDAP subpath of the object
  675. WCHAR * tgtDomain, //in- Domain name where object exists.
  676. WCHAR * sDN //in- Default naming context for the Domain
  677. )
  678. {
  679. if ((!sPath) || (!sSubPath) || (!tgtDomain) || (!sDN))
  680. return;
  681. _bstr_t sTempPath;
  682. if (wcsncmp(sSubPath, L"LDAP://", 7) == 0)
  683. {
  684. //it is already a fully qualified LDAP path so lets copy it and return it
  685. wcsncpy(sPath, sSubPath, nPathLen-1);
  686. sPath[nPathLen - 1] = L'\0';
  687. return;
  688. }
  689. //We need to build this path so lets get to work
  690. if ( wcslen(sDN) )
  691. {
  692. sTempPath = L"LDAP://";
  693. sTempPath += tgtDomain;
  694. sTempPath += L"/";
  695. sTempPath += sSubPath;
  696. sTempPath += L",";
  697. sTempPath += sDN;
  698. }
  699. else
  700. {
  701. sTempPath = L"LDAP://";
  702. sTempPath += tgtDomain;
  703. sTempPath += L"/";
  704. sTempPath += sSubPath;
  705. }
  706. if (sTempPath.length() > 0)
  707. {
  708. wcsncpy(sPath, sTempPath, nPathLen - 1);
  709. sPath[nPathLen - 1] = L'\0';
  710. }
  711. else
  712. {
  713. *sPath = L'\0';
  714. }
  715. }
  716. //--------------------------------------------------------------------------
  717. // IsAccountMigrated : Function checks if the account has been migrated in
  718. // the past. If it has it returns true filling in the
  719. // name of the target object in case it was renamed.
  720. // Otherwise it returns FALSE and Empty string for the
  721. // target name.
  722. //--------------------------------------------------------------------------
  723. bool IsAccountMigrated(
  724. TAcctReplNode * pNode, //in -Account node that contains the Account info
  725. Options * pOptions, //in -Options as specified by the user.
  726. IIManageDBPtr pDb, //in -Pointer to DB manager. We dont want to create this object for every account we process
  727. WCHAR * sTgtSam //in,out - Name of the target object that was copied if any.
  728. )
  729. {
  730. IVarSetPtr pVs(__uuidof(VarSet));
  731. IUnknown * pUnk;
  732. pVs->QueryInterface(IID_IUnknown, (void**) &pUnk);
  733. HRESULT hrFind = pDb->raw_GetAMigratedObject(const_cast<WCHAR*>(pNode->GetSourceSam()), pOptions->srcDomain, pOptions->tgtDomain, &pUnk);
  734. pUnk->Release();
  735. if ( hrFind != S_OK )
  736. {
  737. wcscpy(sTgtSam,L"");
  738. return false;
  739. }
  740. else
  741. {
  742. _bstr_t sText;
  743. sText = pVs->get(L"MigratedObjects.TargetSamName");
  744. if (!(WCHAR*)sText)
  745. {
  746. wcscpy(sTgtSam,L"");
  747. return false;
  748. }
  749. wcscpy(sTgtSam, (WCHAR*) sText);
  750. return true;
  751. }
  752. }
  753. bool CheckifAccountExists(
  754. Options const * options, //in-Options as set by the user
  755. WCHAR * acctName //in-Name of the account to look for
  756. )
  757. {
  758. USER_INFO_0 * buf;
  759. long rc = 0;
  760. if ( (rc = NetUserGetInfo(const_cast<WCHAR*>(options->tgtComp), acctName, 0, (LPBYTE *) &buf)) == NERR_Success )
  761. {
  762. NetApiBufferFree(buf);
  763. return true;
  764. }
  765. if ( (rc = NetGroupGetInfo(const_cast<WCHAR*>(options->tgtComp), acctName, 0, (LPBYTE *) &buf)) == NERR_Success )
  766. {
  767. NetApiBufferFree(buf);
  768. return true;
  769. }
  770. if ( (rc = NetLocalGroupGetInfo(const_cast<WCHAR*>(options->tgtComp), acctName, 0, (LPBYTE *) &buf)) == NERR_Success )
  771. {
  772. NetApiBufferFree(buf);
  773. return true;
  774. }
  775. return false;
  776. }
  777. //--------------------------------------------------------------------------
  778. // Mark : Increments appropriate counters depending on the arguments.
  779. //--------------------------------------------------------------------------
  780. void Mark(
  781. _bstr_t sMark, //in- Represents the type of marking { processed, errors, replaced, created }
  782. _bstr_t sObj //in- Type of object being marked { user, group, computer }
  783. )
  784. {
  785. if (!UStrICmp(sMark,L"processed"))
  786. {
  787. if ( !UStrICmp(sObj,L"user") || !UStrICmp(sObj,L"inetOrgPerson") ) processed.users++;
  788. else if ( !UStrICmp(sObj,L"group")) processed.globals++;
  789. else if ( !UStrICmp(sObj,L"computer")) processed.computers++;
  790. else if ( !UStrICmp(sObj,L"generic")) processed.generic++;
  791. }
  792. else if (!UStrICmp(sMark,L"errors"))
  793. {
  794. if ( !UStrICmp(sObj,L"user") || !UStrICmp(sObj,L"inetOrgPerson") ) errors.users++;
  795. else if ( !UStrICmp(sObj,L"group")) errors.globals++;
  796. else if ( !UStrICmp(sObj,L"computer")) errors.computers++;
  797. else if ( !UStrICmp(sObj,L"generic")) errors.generic++;
  798. }
  799. else if (!UStrICmp(sMark,L"warnings"))
  800. {
  801. if ( !UStrICmp(sObj,L"user") || !UStrICmp(sObj,L"inetOrgPerson") ) warnings.users++;
  802. else if ( !UStrICmp(sObj,L"group")) warnings.globals++;
  803. else if ( !UStrICmp(sObj,L"computer")) warnings.computers++;
  804. else if ( !UStrICmp(sObj,L"generic")) warnings.generic++;
  805. }
  806. else if (!UStrICmp(sMark,L"replaced"))
  807. {
  808. if ( !UStrICmp(sObj,L"user") || !UStrICmp(sObj,L"inetOrgPerson") ) replaced.users++;
  809. else if ( !UStrICmp(sObj,L"group")) replaced.globals++;
  810. else if ( !UStrICmp(sObj,L"computer")) replaced.computers++;
  811. else if ( !UStrICmp(sObj,L"generic")) replaced.generic++;
  812. }
  813. else if (!UStrICmp(sMark,L"created"))
  814. {
  815. if ( !UStrICmp(sObj,L"user") || !UStrICmp(sObj,L"inetOrgPerson") ) created.users++;
  816. else if ( !UStrICmp(sObj,L"group")) created.globals++;
  817. else if ( !UStrICmp(sObj,L"computer")) created.computers++;
  818. else if ( !UStrICmp(sObj,L"generic")) created.generic++;
  819. }
  820. }
  821. //
  822. // This function batch-marks one category of AccountStats based on an EAMAccountStatItem struct.
  823. // statItem: the EAMAccountStatItem struct
  824. // aStat: the AccountStats struct; should be one of errors, warnings, replaced, created, processed
  825. //
  826. static void BatchMarkCategory(const EAMAccountStatItem& statItem, AccountStats& aStat)
  827. {
  828. aStat.locals += statItem.locals;
  829. aStat.users += statItem.users;
  830. aStat.globals += statItem.globals;
  831. aStat.computers += statItem.computers;
  832. aStat.generic += statItem.generic;
  833. }
  834. //
  835. // This function batch-marks all categories of AccountStats based on an EAMAccountStats struct.
  836. // stats: the EAMAccountStats struct
  837. //
  838. void BatchMark(const EAMAccountStats& stats)
  839. {
  840. BatchMarkCategory(stats.errors, errors);
  841. BatchMarkCategory(stats.warnings, warnings);
  842. BatchMarkCategory(stats.replaced, replaced);
  843. BatchMarkCategory(stats.created, created);
  844. BatchMarkCategory(stats.processed, processed);
  845. }
  846. HRESULT __stdcall GetRidPoolAllocator(Options* pOptions)
  847. {
  848. WCHAR szADsPath[LEN_Path];
  849. //
  850. // Bind to source Domain object and retrieve distinguished name of RID Manager object.
  851. //
  852. IADsPtr spDomain;
  853. _bstr_t strRIDManagerReference;
  854. szADsPath[countof(szADsPath) - 1] = L'\0';
  855. int cch = _snwprintf(
  856. szADsPath,
  857. countof(szADsPath),
  858. L"LDAP://%s/%s",
  859. pOptions->srcComp + 2,
  860. pOptions->srcNamingContext
  861. );
  862. if ((cch < 0) || (szADsPath[countof(szADsPath) - 1] != L'\0'))
  863. {
  864. return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  865. }
  866. szADsPath[countof(szADsPath) - 1] = L'\0';
  867. HRESULT hr = ADsGetObject(szADsPath, IID_IADs, (VOID**)&spDomain);
  868. if (FAILED(hr))
  869. {
  870. return hr;
  871. }
  872. VARIANT varRIDManagerReference;
  873. VariantInit(&varRIDManagerReference);
  874. hr = spDomain->Get(L"rIDManagerReference", &varRIDManagerReference);
  875. if (FAILED(hr))
  876. {
  877. return hr;
  878. }
  879. strRIDManagerReference = _variant_t(varRIDManagerReference, false);
  880. //
  881. // Bind to RID Manager object and retrieve distinguished name of the FSMO Role Owner.
  882. //
  883. IADsPtr spRIDManager;
  884. _bstr_t strFSMORoleOwner;
  885. szADsPath[countof(szADsPath) - 1] = L'\0';
  886. cch = _snwprintf(
  887. szADsPath,
  888. countof(szADsPath),
  889. L"LDAP://%s/%s",
  890. pOptions->srcComp + 2,
  891. (PCWSTR)strRIDManagerReference
  892. );
  893. if ((cch < 0) || (szADsPath[countof(szADsPath) - 1] != L'\0'))
  894. {
  895. return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  896. }
  897. szADsPath[countof(szADsPath) - 1] = L'\0';
  898. hr = ADsGetObject(szADsPath, IID_IADs, (VOID**)&spRIDManager);
  899. if (FAILED(hr))
  900. {
  901. return hr;
  902. }
  903. VARIANT varFSMORoleOwner;
  904. VariantInit(&varFSMORoleOwner);
  905. hr = spRIDManager->Get(L"fSMORoleOwner", &varFSMORoleOwner);
  906. if (FAILED(hr))
  907. {
  908. return hr;
  909. }
  910. strFSMORoleOwner = _variant_t(varFSMORoleOwner, false);
  911. //
  912. // Bind to NTDS-DSA object and retrieve ADsPath of parent Server object.
  913. //
  914. IADsPtr spNTDSDSA;
  915. _bstr_t strServer;
  916. szADsPath[countof(szADsPath) - 1] = L'\0';
  917. cch = _snwprintf(
  918. szADsPath,
  919. countof(szADsPath),
  920. L"LDAP://%s/%s",
  921. pOptions->srcComp + 2,
  922. (PCWSTR)strFSMORoleOwner
  923. );
  924. if ((cch < 0) || (szADsPath[countof(szADsPath) - 1] != L'\0'))
  925. {
  926. return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  927. }
  928. szADsPath[countof(szADsPath) - 1] = L'\0';
  929. hr = ADsGetObject(szADsPath, IID_IADs, (VOID**)&spNTDSDSA);
  930. if (FAILED(hr))
  931. {
  932. return hr;
  933. }
  934. BSTR bstrServer;
  935. hr = spNTDSDSA->get_Parent(&bstrServer);
  936. if (FAILED(hr))
  937. {
  938. return hr;
  939. }
  940. strServer = _bstr_t(bstrServer, false);
  941. //
  942. // Bind to Server object and retrieve distinguished name of Computer object.
  943. //
  944. IADsPtr spServer;
  945. _bstr_t strServerReference;
  946. hr = ADsGetObject(strServer, IID_IADs, (VOID**)&spServer);
  947. if (FAILED(hr))
  948. {
  949. return hr;
  950. }
  951. VARIANT varServerReference;
  952. VariantInit(&varServerReference);
  953. hr = spServer->Get(L"serverReference", &varServerReference);
  954. if (FAILED(hr))
  955. {
  956. return hr;
  957. }
  958. strServerReference = _variant_t(varServerReference, false);
  959. //
  960. // Bind to Computer object and retrieve DNS host name and SAM account name.
  961. //
  962. IADsPtr spComputer;
  963. _bstr_t strDNSHostName;
  964. _bstr_t strSAMAccountName;
  965. szADsPath[countof(szADsPath) - 1] = L'\0';
  966. cch = _snwprintf(
  967. szADsPath,
  968. countof(szADsPath),
  969. L"LDAP://%s/%s",
  970. pOptions->srcComp + 2,
  971. (PCWSTR)strServerReference
  972. );
  973. if ((cch < 0) || (szADsPath[countof(szADsPath) - 1] != L'\0'))
  974. {
  975. return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  976. }
  977. szADsPath[countof(szADsPath) - 1] = L'\0';
  978. hr = ADsGetObject(szADsPath, IID_IADs, (VOID**)&spComputer);
  979. if (FAILED(hr))
  980. {
  981. return hr;
  982. }
  983. VARIANT varDNSHostName;
  984. VariantInit(&varDNSHostName);
  985. hr = spComputer->Get(L"dNSHostName", &varDNSHostName);
  986. if (FAILED(hr))
  987. {
  988. return hr;
  989. }
  990. strDNSHostName = _variant_t(varDNSHostName, false);
  991. VARIANT varSAMAccountName;
  992. VariantInit(&varSAMAccountName);
  993. hr = spComputer->Get(L"SAMAccountName", &varSAMAccountName);
  994. if (FAILED(hr))
  995. {
  996. return hr;
  997. }
  998. strSAMAccountName = _variant_t(varSAMAccountName, false);
  999. if ((strDNSHostName.length() == 0) || (strSAMAccountName.length() == 0))
  1000. {
  1001. return E_OUTOFMEMORY;
  1002. }
  1003. //
  1004. // Update source domain controller names.
  1005. //
  1006. if ((2 + strDNSHostName.length() >= countof(pOptions->srcComp)) ||
  1007. (2 + strDNSHostName.length() >= countof(pOptions->srcCompDns)) ||
  1008. (2 + strSAMAccountName.length() >= countof(pOptions->srcCompFlat)))
  1009. {
  1010. return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  1011. }
  1012. wcscpy(pOptions->srcComp, L"\\\\");
  1013. wcscat(pOptions->srcComp, strDNSHostName);
  1014. wcscpy(pOptions->srcCompDns, pOptions->srcComp);
  1015. wcscpy(pOptions->srcCompFlat, L"\\\\");
  1016. wcscat(pOptions->srcCompFlat, strSAMAccountName);
  1017. // Remove trailing $ character.
  1018. pOptions->srcCompFlat[wcslen(pOptions->srcCompFlat) - 1] = L'\0';
  1019. return S_OK;
  1020. }