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.

982 lines
34 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 "RebootU.h"
  54. #include "DCTStat.h"
  55. #include "ErrDct.hpp"
  56. #include "Win2KErr.h"
  57. #include "RegTrans.h"
  58. #include "TEvent.hpp"
  59. #include <dsgetdc.h>
  60. #include <sddl.h>
  61. //#import "\bin\NetEnum.tlb" no_namespace
  62. //#import "\bin\DBManager.tlb" no_namespace, named_guids
  63. #import "NetEnum.tlb" no_namespace
  64. //#import "DBMgr.tlb" no_namespace, named_guids //already #imported via ARUtil.hpp
  65. #ifdef _DEBUG
  66. #define new DEBUG_NEW
  67. #undef THIS_FILE
  68. static char THIS_FILE[] = __FILE__;
  69. #endif
  70. extern TErrorDct err;
  71. extern TErrorDct & errC;
  72. bool abortall;
  73. extern bool g_bAddSidWorks = false;
  74. // global counts of accounts processed
  75. AccountStats warnings = { 0,0,0,0 };
  76. AccountStats errors = { 0,0,0,0 };
  77. AccountStats created = { 0,0,0,0 };
  78. AccountStats replaced = { 0,0,0,0 };
  79. AccountStats processed = { 0,0,0,0 };
  80. BOOL machineAcctsCreated = FALSE;
  81. BOOL otherAcctsCreated = FALSE;
  82. PSID srcSid = NULL; // SID for source domain
  83. typedef HRESULT (CALLBACK * DSGETDCNAME)(LPWSTR, LPWSTR, GUID*, LPWSTR, DWORD, PDOMAIN_CONTROLLER_INFO*);
  84. typedef UINT (CALLBACK* DSBINDFUNC)(TCHAR*, TCHAR*, HANDLE*);
  85. typedef UINT (CALLBACK* DSADDSIDHISTORY)(HANDLE, DWORD, LPCTSTR, LPCTSTR, LPCTSTR, RPC_AUTH_IDENTITY_HANDLE,LPCTSTR,LPCTSTR);
  86. int TNodeCompareSourceName(TNode const * t1,TNode const * t2)
  87. {
  88. TAcctReplNode const * n1 = (TAcctReplNode *)t1;
  89. TAcctReplNode const * n2 = (TAcctReplNode *)t2;
  90. return UStrICmp(n1->GetName(),n2->GetName());
  91. }
  92. int TNodeCompareSourceNameValue(TNode const * t1, void const * v)
  93. {
  94. TAcctReplNode const * n1 = (TAcctReplNode *)t1;
  95. WCHAR const * name = (WCHAR const *)v;
  96. return UStrICmp(n1->GetName(),name);
  97. }
  98. bool BindToDS( WCHAR * strDestDC, Options * pOpt)
  99. {
  100. // Get the handle to the Directory service.
  101. DSBINDFUNC DsBind;
  102. HINSTANCE hInst = LoadLibrary(L"NTDSAPI.DLL");
  103. if ( hInst )
  104. {
  105. DsBind = (DSBINDFUNC) GetProcAddress(hInst, "DsBindW");
  106. if (DsBind)
  107. {
  108. DWORD rc = DsBind(strDestDC, NULL, &pOpt->dsBindHandle);
  109. if ( rc != 0 )
  110. {
  111. err.SysMsgWrite( ErrE, rc, DCT_MSG_DSBIND_FAILED_S, strDestDC);
  112. Mark(L"errors", L"generic");
  113. FreeLibrary(hInst);
  114. return false;
  115. }
  116. }
  117. else
  118. {
  119. err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_GET_PROC_ADDRESS_FAILED_SSD,L"NTDSAPI.DLL",L"DsBindW",GetLastError());
  120. Mark(L"errors", L"generic");
  121. FreeLibrary(hInst);
  122. return false;
  123. }
  124. }
  125. else
  126. {
  127. err.SysMsgWrite(ErrW,GetLastError(),DCT_MSG_LOAD_LIBRARY_FAILED_SD,L"NTDSAPI.DLL",GetLastError());
  128. Mark(L"warnings", L"generic");
  129. return false;
  130. }
  131. FreeLibrary(hInst);
  132. return true;
  133. }
  134. // The following function is used to get the actual account name from the source domain
  135. // instead of account that contains the SID in its SID history.
  136. DWORD GetName(PSID pObjectSID, WCHAR * sNameAccount, WCHAR * sDomain)
  137. {
  138. DWORD cb = 255;
  139. DWORD cbDomain = 255;
  140. DWORD tempVal;
  141. PDWORD psubAuth;
  142. PUCHAR pVal;
  143. SID_NAME_USE sid_Use;
  144. WCHAR sDC[255];
  145. DWORD rc = 0;
  146. if ((pObjectSID == NULL) || !IsValidSid(pObjectSID))
  147. {
  148. return ERROR_INVALID_PARAMETER;
  149. }
  150. // Copy the Sid to a temp SID
  151. DWORD sidLen = GetLengthSid(pObjectSID);
  152. PSID pObjectSID1 = new BYTE[sidLen];
  153. if (!pObjectSID1)
  154. return ERROR_NOT_ENOUGH_MEMORY;
  155. if (!CopySid(sidLen, pObjectSID1, pObjectSID))
  156. {
  157. return GetLastError();
  158. }
  159. if (!IsValidSid(pObjectSID1))
  160. {
  161. rc = GetLastError();
  162. err.SysMsgWrite(ErrE, rc,DCT_MSG_DOMAIN_LOOKUP_FAILED_D,rc);
  163. Mark(L"errors", L"generic");
  164. delete pObjectSID1;
  165. return rc;
  166. }
  167. // Get the RID out of the SID and get the domain SID
  168. pVal = GetSidSubAuthorityCount(pObjectSID1);
  169. (*pVal)--;
  170. psubAuth = GetSidSubAuthority(pObjectSID1, *pVal);
  171. tempVal = *psubAuth;
  172. *psubAuth = -1;
  173. //Lookup the domain from the SID
  174. if (!LookupAccountSid(NULL, pObjectSID1, sNameAccount, &cb, sDomain, &cbDomain, &sid_Use))
  175. {
  176. rc = GetLastError();
  177. err.SysMsgWrite(ErrE, rc,DCT_MSG_DOMAIN_LOOKUP_FAILED_D,rc);
  178. Mark(L"errors", L"generic");
  179. delete pObjectSID1;
  180. return rc;
  181. }
  182. // Get a DC for the domain
  183. DSGETDCNAME DsGetDcName = NULL;
  184. DOMAIN_CONTROLLER_INFO * pSrcDomCtrlInfo = NULL;
  185. HMODULE hPro = LoadLibrary(L"NetApi32.dll");
  186. if ( hPro )
  187. DsGetDcName = (DSGETDCNAME)GetProcAddress(hPro, "DsGetDcNameW");
  188. else
  189. {
  190. long rc = GetLastError();
  191. err.SysMsgWrite(ErrE, rc, DCT_MSG_LOAD_LIBRARY_FAILED_SD, L"NetApi32.dll");
  192. Mark(L"errors", L"generic");
  193. }
  194. if (DsGetDcName)
  195. {
  196. if ( DsGetDcName(
  197. NULL ,// LPCTSTR ComputerName ?
  198. sDomain ,// LPCTSTR DomainName
  199. NULL ,// GUID *DomainGuid ?
  200. NULL ,// LPCTSTR SiteName ?
  201. 0 ,// ULONG Flags ?
  202. &pSrcDomCtrlInfo // PDOMAIN_CONTROLLER_INFO *DomainControllerInfo
  203. ))
  204. {
  205. err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_GET_DCNAME_FAILED_SD,sDomain,GetLastError());
  206. Mark(L"errors", L"generic");
  207. delete pObjectSID1;
  208. return GetLastError();
  209. }
  210. else
  211. {
  212. wcscpy(sDC, pSrcDomCtrlInfo->DomainControllerName);
  213. NetApiBufferFree(pSrcDomCtrlInfo);
  214. }
  215. // Reset the sizes
  216. cb = 255;
  217. cbDomain = 255;
  218. // Lookup the account on the PDC that we found above.
  219. if ( LookupAccountSid(sDC, pObjectSID, sNameAccount, &cb, sDomain, &cbDomain, &sid_Use) == 0)
  220. {
  221. delete pObjectSID1;
  222. return GetLastError();
  223. }
  224. }
  225. delete pObjectSID1;
  226. FreeLibrary(hPro);
  227. return 0;
  228. }
  229. /* This is a list of specific error codes that can be returned by DsAddSidHistory.
  230. This was obtained from Microsoft via email
  231. > ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED
  232. > The operation requires that destination domain auditing be enabled for
  233. > Success and Failure auditing of account management operations.
  234. >
  235. > ERROR_DS_UNWILLING_TO_PERFORM
  236. > It may be that the user account is not one of UF_NORMAL_ACCOUNT,
  237. > UF_WORKSTATION_TRUST_ACCOUNT, or UF_SERVER_TRUST_ACCOUNT.
  238. >
  239. > It may be that the source principal is a built in account.
  240. >
  241. > It may be that the source principal is a well known RID being added
  242. > to a destination principal that is a different RID. In other words,
  243. > Administrators of the source domain can only be assigned to
  244. > Administrators of the destination domain.
  245. >
  246. > ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER
  247. > The source object must be a group or user.
  248. >
  249. > ERROR_DS_SRC_SID_EXISTS_IN_FOREST
  250. > The source object's SID already exists in destination forest.
  251. >
  252. > ERROR_DS_INTERNAL_FAILURE;
  253. > The directory service encountered an internal failure. Shouldn't
  254. > happen.
  255. >
  256. > ERROR_DS_MUST_BE_RUN_ON_DST_DC
  257. > For security reasons, the operation must be run on the destination DC.
  258. > Specifically, the connection between the client and server
  259. > (destination
  260. > DC) requires 128-bit encryption when credentials for the source domain
  261. > are supplied.
  262. >
  263. > ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION
  264. > The connection between client and server requires packet privacy or
  265. > better.
  266. >
  267. > ERROR_DS_SOURCE_DOMAIN_IN_FOREST
  268. > The source domain may not be in the same forest as destination.
  269. >
  270. > ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST
  271. > The destination domain must be in the forest.
  272. >
  273. > ERROR_DS_MASTERDSA_REQUIRED
  274. > The operation must be performed at a master DSA (writable DC).
  275. >
  276. > ERROR_DS_INSUFF_ACCESS_RIGHTS
  277. > Insufficient access rights to perform the operation. Most likely
  278. > the caller is not a member of domain admins for the dst domain.
  279. >
  280. > ERROR_DS_DST_DOMAIN_NOT_NATIVE
  281. > Destination domain must be in native mode.
  282. >
  283. > ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN
  284. > The operation couldn't locate a DC for the source domain.
  285. >
  286. > ERROR_DS_OBJ_NOT_FOUND
  287. > Directory object not found. Most likely the FQDN of the
  288. > destination principal could not be found in the destination
  289. > domain.
  290. >
  291. > ERROR_DS_NAME_ERROR_NOT_UNIQUE
  292. > Name translation: Input name mapped to more than one
  293. > output name. Most likely the destination principal mapped
  294. > to more than one FQDN in the destination domain.
  295. >
  296. > ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH
  297. > The source and destination object must be of the same type.
  298. >
  299. > ERROR_DS_OBJ_CLASS_VIOLATION
  300. > The requested operation did not satisfy one or more constraints
  301. > associated with the class of the object. Most likely because the
  302. > destination principal is not a user or group.
  303. >
  304. > ERROR_DS_UNAVAILABLE
  305. > The directory service is unavailable. Most likely the
  306. > ldap_initW() to the NT5 src DC failed.
  307. >
  308. > ERROR_DS_INAPPROPRIATE_AUTH
  309. > Inappropriate authentication. Most likely the ldap_bind_sW() to
  310. > the NT5 src dc failed.
  311. >
  312. > ERROR_DS_SOURCE_AUDITING_NOT_ENABLED
  313. > The operation requires that source domain auditing be enabled for
  314. > Success and Failure auditing of account management operations.
  315. >
  316. > ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER
  317. > For security reasons, the source DC must be Service Pack 4 or greater.
  318. >
  319. */
  320. BOOL GetDnsAndNetbiosFromName(WCHAR * name,WCHAR * netBios, WCHAR * dns)
  321. {
  322. IADs * pDomain = NULL;
  323. WCHAR strText[1000];
  324. // try to connect to the AD, using the specified name
  325. swprintf(strText,L"LDAP://%ls",name);
  326. HRESULT hr = ADsGetObject(strText,IID_IADs,(void**)&pDomain);
  327. if ( SUCCEEDED(hr) )
  328. {
  329. _variant_t var;
  330. // get the NETBIOS name from the LDAP provider
  331. hr = pDomain->Get(L"nETBIOSName",&var);
  332. if ( SUCCEEDED(hr) )
  333. {
  334. UStrCpy(netBios,(WCHAR*)var.bstrVal);
  335. }
  336. else
  337. {
  338. hr = pDomain->Get(L"name",&var);
  339. if ( SUCCEEDED(hr) )
  340. {
  341. UStrCpy(netBios,(WCHAR*)var.bstrVal);
  342. }
  343. }
  344. // get the DNS name from the LDAP provider
  345. hr = pDomain->Get(L"distinguishedName",&var);
  346. if ( SUCCEEDED(hr) )
  347. {
  348. WCHAR * dn = (WCHAR*)var.bstrVal;
  349. WCHAR * curr = dns;
  350. if ( !UStrICmp(dn,L"DC=",3) )
  351. {
  352. // for each ",DC=" in the name, replace it with a .
  353. for ( curr = dns, dn = dn+3 ; // skip the leading "DC="
  354. *dn ; // until the end of the string is reached
  355. curr++ //
  356. )
  357. {
  358. if ( (L',' == *dn) && (L'D' == *(dn+1)) && (L'C' == *(dn+2)) && (L'=' == *(dn+3)) )
  359. {
  360. (*curr) = L'.';
  361. dn+=4;
  362. }
  363. else
  364. {
  365. // just copy the character
  366. (*curr) = (*dn);
  367. dn++;
  368. }
  369. }
  370. *(curr) = 0;
  371. }
  372. }
  373. pDomain->Release();
  374. }
  375. else
  376. {
  377. // default to using the specified name as both DNS and NETBIOS
  378. // this will work for NT 4 domains
  379. UStrCpy(netBios,name);
  380. UStrCpy(dns,name);
  381. }
  382. return TRUE;
  383. }
  384. HRESULT
  385. CopySidHistoryProperty(
  386. Options * pOptions,
  387. TAcctReplNode * pNode,
  388. IStatusObj * pStatus
  389. )
  390. {
  391. HRESULT hr = S_OK;
  392. IADs * pAds = NULL;
  393. _variant_t var;
  394. // long ub = 0, lb = 0;
  395. // fetch the SIDHistory property for the source account
  396. // for each entry in the source's SIDHistory, call DsAddSidHistory
  397. // Get the IADs pointer to the object and get the SIDHistory attribute.
  398. hr = ADsGetObject(const_cast<WCHAR*>(pNode->GetSourcePath()), IID_IADs, (void**)&pAds);
  399. if ( SUCCEEDED(hr) )
  400. {
  401. hr = pAds->Get(L"sIDHistory", &var);
  402. }
  403. if ( SUCCEEDED(hr) )
  404. {
  405. // This is a multivalued property so we need to get all the values
  406. // for each one get the name and the domain of the object and then call the
  407. // add sid history function to add the SID to the target objects SIDHistory.
  408. _variant_t var;
  409. DWORD rc = pAds->GetEx(L"sIDHistory", &var);
  410. if ( !rc )
  411. {
  412. if ( V_VT(&var) == (VT_ARRAY | VT_VARIANT) )
  413. {
  414. // This is the array type that we were looking for.
  415. void HUGEP *pArray;
  416. VARIANT var2;
  417. ULONG dwSLBound = -1;
  418. ULONG dwSUBound = -1;
  419. hr = SafeArrayGetLBound( V_ARRAY(&var),
  420. 1,
  421. (long FAR *) &dwSLBound );
  422. hr = SafeArrayGetUBound( V_ARRAY(&var),
  423. 1,
  424. (long FAR *) &dwSUBound );
  425. if (SUCCEEDED(hr))
  426. {
  427. // Each element in this array is a SID in form of a VARIANT
  428. hr = SafeArrayAccessData( V_ARRAY(&var), &pArray );
  429. for ( long x = (long)dwSLBound; x <= (long)dwSUBound; x++)
  430. {
  431. hr = SafeArrayGetElement(V_ARRAY(&var), &x, &var2);
  432. // Get the SID from the Variant in a ARRAY form
  433. hr = SafeArrayAccessData( V_ARRAY(&var2), &pArray );
  434. PSID pObjectSID = (PSID)pArray;
  435. //Convert SID to string.
  436. if (pObjectSID)
  437. {
  438. WCHAR sNameAccount[255];
  439. WCHAR sDomain[255];
  440. WCHAR sNetBIOS[255];
  441. DWORD rc = 0;
  442. rc = GetName(pObjectSID, sNameAccount, sDomain);
  443. if (!rc)
  444. {
  445. WCHAR sTemp[LEN_Path];
  446. WCHAR sSourceDNS[LEN_Path];
  447. // We are going to temporarily change the Domain DNS to the domain of the SID we are adding
  448. wcscpy(sTemp, pOptions->srcDomainDns);
  449. if ( GetDnsAndNetbiosFromName(sDomain, sNetBIOS, sSourceDNS) )
  450. {
  451. wcscpy(pOptions->srcDomainDns, sSourceDNS);
  452. AddSidHistory(pOptions, sNameAccount, pNode->GetTargetSam(), NULL, FALSE);
  453. // Replace the original domain dns.
  454. wcscpy(pOptions->srcDomainDns, sTemp);
  455. }
  456. else
  457. {
  458. err.SysMsgWrite(ErrE, GetLastError(),DCT_MSG_DOMAIN_DNS_LOOKUP_FAILED_SD, sDomain,GetLastError());
  459. Mark(L"errors", pNode->GetType());
  460. }
  461. }
  462. else
  463. {
  464. // Get name failed we need to log a message.
  465. WCHAR sSid[LEN_Path];
  466. DWORD len = LEN_Path;
  467. GetTextualSid(pObjectSID, sSid, &len);
  468. err.SysMsgWrite(ErrE,rc,DCT_MSG_ERROR_CONVERTING_SID_SSD,
  469. pNode->GetTargetName(), sSid, rc);
  470. Mark(L"errors", pNode->GetType());
  471. }
  472. }
  473. SafeArrayUnaccessData(V_ARRAY(&var2));
  474. }
  475. SafeArrayUnaccessData(V_ARRAY(&var));
  476. }
  477. }
  478. }
  479. else
  480. {
  481. // No SID History to copy.
  482. }
  483. }
  484. return hr;
  485. }
  486. bool AddSidHistory( const Options * pOptions,
  487. const WCHAR * strSrcPrincipal,
  488. const WCHAR * strDestPrincipal,
  489. IStatusObj * pStatus,
  490. BOOL isFatal)
  491. {
  492. //Add the sid to the history
  493. // Authentication Structure
  494. SEC_WINNT_AUTH_IDENTITY auth;
  495. DWORD rc = 0;
  496. //memset(&auth, 0, sizeof(SEC_WINNT_AUTH_IDENTITY));
  497. auth.User = const_cast<WCHAR*>(pOptions->authUser);
  498. auth.UserLength = wcslen(pOptions->authUser);
  499. auth.Password = const_cast<WCHAR*>(pOptions->authPassword);
  500. auth.PasswordLength = wcslen(pOptions->authPassword);
  501. auth.Domain = const_cast<WCHAR*>(pOptions->authDomain);
  502. auth.DomainLength = wcslen(pOptions->authDomain);
  503. auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  504. // Auth Identity handle
  505. // if source domain credentials supplied use them
  506. // otherwise credentials of caller will be used
  507. RPC_AUTH_IDENTITY_HANDLE pHandle = ((auth.DomainLength > 0) && (auth.UserLength > 0)) ? &auth : NULL;
  508. DSADDSIDHISTORY DsAddSidHistory;
  509. HINSTANCE hInst = LoadLibrary(L"NTDSAPI.DLL");
  510. if ( hInst )
  511. {
  512. DsAddSidHistory = (DSADDSIDHISTORY) GetProcAddress(hInst, "DsAddSidHistoryW");
  513. if (DsAddSidHistory)
  514. {
  515. if ( !pOptions->nochange )
  516. {
  517. int loopCount = 0;
  518. rc = RPC_S_SERVER_UNAVAILABLE;
  519. // If we get the RPC server errors we need to retry 5 times.
  520. while ( (((rc == RPC_S_SERVER_UNAVAILABLE) || (rc == RPC_S_CALL_FAILED) || (rc == RPC_S_CALL_FAILED_DNE)) && loopCount < 5)
  521. || ( (rc == ERROR_INVALID_HANDLE) && loopCount < 3 ) ) // In case of invalid handle we try it 3 times now.
  522. {
  523. // Make the API call to add Sid to the history
  524. rc = DsAddSidHistory(
  525. pOptions->dsBindHandle, //DS Handle
  526. NULL, // flags
  527. pOptions->srcDomainDns, // Source domain
  528. strSrcPrincipal, // Source Account name
  529. NULL, // Source Domain Controller
  530. pHandle, // RPC_AUTH_IDENTITY_HANDLE
  531. pOptions->tgtDomainDns, // Target domain
  532. strDestPrincipal); // Target Account name
  533. if ( loopCount > 0 ) Sleep(500);
  534. loopCount++;
  535. }
  536. }
  537. if ( rc != 0 )
  538. {
  539. switch ( rc )
  540. {
  541. // these are the error codes caused by permissions or configuration problems
  542. case ERROR_NONE_MAPPED:
  543. err.MsgWrite(ErrE, DCT_MSG_ADDSIDHISTORY_FAIL_BUILTIN_SSD,strSrcPrincipal, strDestPrincipal, rc);
  544. break;
  545. case ERROR_DS_UNWILLING_TO_PERFORM:
  546. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_DS_UNWILLING_TO_PERFORM_SSSSD,strDestPrincipal,pOptions->srcDomain, strSrcPrincipal, pOptions->tgtDomain,rc);
  547. break;
  548. case ERROR_DS_INSUFF_ACCESS_RIGHTS:
  549. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_INSUFF_ACCESS_SD,strDestPrincipal,rc);
  550. g_bAddSidWorks = FALSE;
  551. break;
  552. case ERROR_INVALID_HANDLE:
  553. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_INVALID_HANDLE_SSD,pOptions->srcDomainDns,strDestPrincipal,rc);
  554. g_bAddSidWorks = FALSE;
  555. break;
  556. case ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED:
  557. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_NOAUDIT_SSD,strDestPrincipal,pOptions->tgtDomainDns,rc);
  558. g_bAddSidWorks = FALSE;
  559. break;
  560. case ERROR_DS_MUST_BE_RUN_ON_DST_DC:
  561. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_DST_DC_SD,strDestPrincipal,rc);
  562. g_bAddSidWorks = FALSE;
  563. break;
  564. case ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION:
  565. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_PKT_PRIVACY_SD,strDestPrincipal,rc);
  566. g_bAddSidWorks = FALSE;
  567. break;
  568. case ERROR_DS_SOURCE_DOMAIN_IN_FOREST:
  569. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_SOURCE_IN_FOREST_S,strDestPrincipal);
  570. g_bAddSidWorks = FALSE;
  571. break;
  572. case ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST:
  573. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_DEST_WRONG_FOREST_S,strDestPrincipal);
  574. g_bAddSidWorks = FALSE;
  575. break;
  576. case ERROR_DS_MASTERDSA_REQUIRED:
  577. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_NO_MASTERDSA_S,strDestPrincipal);
  578. g_bAddSidWorks = FALSE;
  579. break;
  580. case ERROR_ACCESS_DENIED:
  581. g_bAddSidWorks = FALSE;
  582. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_INSUFF2_SSS,strDestPrincipal,pOptions->authDomain,pOptions->authUser);
  583. break;
  584. case ERROR_DS_DST_DOMAIN_NOT_NATIVE:
  585. g_bAddSidWorks = FALSE;
  586. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_NOT_NATIVE_S,strDestPrincipal);
  587. break;
  588. case ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN:
  589. g_bAddSidWorks = FALSE;
  590. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_NO_SOURCE_DC_S,strDestPrincipal);
  591. break;
  592. // case ERROR_DS_INAPPROPRIATE_AUTH:
  593. case ERROR_DS_UNAVAILABLE:
  594. g_bAddSidWorks = FALSE;
  595. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_DS_UNAVAILABLE_S,strDestPrincipal);
  596. break;
  597. case ERROR_DS_SOURCE_AUDITING_NOT_ENABLED:
  598. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_NOAUDIT_SSD,strDestPrincipal,pOptions->srcDomain,rc);
  599. g_bAddSidWorks = FALSE;
  600. break;
  601. case ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER:
  602. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_SOURCE_NOT_SP4_S,strDestPrincipal);
  603. g_bAddSidWorks = FALSE;
  604. break;
  605. case ERROR_SESSION_CREDENTIAL_CONFLICT:
  606. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_CREDENTIALS_CONFLICT_SSSS,strDestPrincipal,pOptions->srcDomain,pOptions->authDomain,pOptions->authUser);
  607. g_bAddSidWorks = FALSE;
  608. break;
  609. // these are error codes that only affect this particular account
  610. case ERROR_SUCCESS:
  611. g_bAddSidWorks = TRUE;
  612. // no error message needed for success case!
  613. break;
  614. case ERROR_DS_SRC_SID_EXISTS_IN_FOREST:
  615. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_IN_FOREST_SD,strDestPrincipal,rc);
  616. g_bAddSidWorks = TRUE;
  617. break;
  618. case ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER:
  619. err.MsgWrite(ErrE,DCT_MSG_SID_HISTORY_WRONGTYPE_SD,strDestPrincipal,rc);
  620. g_bAddSidWorks = TRUE;
  621. break;
  622. default:
  623. err.MsgWrite(ErrE,DCT_MSG_ADDSID_FAILED_SSD,strSrcPrincipal, strDestPrincipal,rc);
  624. g_bAddSidWorks = TRUE;
  625. break;
  626. }
  627. Mark(L"errors", L"generic");
  628. // This may or may not be a fatal error depending on weather we are Adding
  629. // sid history or copying sid history
  630. g_bAddSidWorks |= !(isFatal);
  631. if (! g_bAddSidWorks )
  632. {
  633. // log a message indicating that SIDHistory will not be tried for the rest of the accounts
  634. err.MsgWrite(ErrW,DCT_MSG_SIDHISTORY_FATAL_ERROR);
  635. Mark(L"warnings", L"generic");
  636. // we are going to set the status to abort so that we don't try to migrate anymore.
  637. if ( pStatus )
  638. {
  639. pStatus->put_Status(DCT_STATUS_ABORTING);
  640. }
  641. }
  642. FreeLibrary(hInst);
  643. return false;
  644. }
  645. else
  646. {
  647. err.MsgWrite(0, DCT_MSG_ADD_SID_SUCCESS_SSSS, pOptions->srcDomainDns, strSrcPrincipal, pOptions->tgtDomainDns, strDestPrincipal);
  648. FreeLibrary(hInst);
  649. return true;
  650. }
  651. }
  652. else
  653. {
  654. err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_NO_ADDSIDHISTORY_FUNCTION);
  655. Mark(L"errors", L"generic");
  656. FreeLibrary(hInst);
  657. return false;
  658. }
  659. }
  660. else
  661. {
  662. err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_NO_NTDSAPI_DLL);
  663. Mark(L"errors", L"generic");
  664. return false;
  665. }
  666. }
  667. //--------------------------------------------------------------------------
  668. // FillupNamingContext : This function fills in the target Naming context
  669. // for NT5 domain.
  670. //--------------------------------------------------------------------------
  671. void FillupNamingContext(
  672. Options * options //in,out- Options to fill up
  673. )
  674. {
  675. WCHAR sPath[LEN_Path];
  676. IADs * pAds;
  677. _variant_t var;
  678. HRESULT hr;
  679. wsprintf(sPath, L"LDAP://%s/rootDSE", options->tgtDomain);
  680. hr = ADsGetObject(sPath, IID_IADs, (void**)&pAds);
  681. if ( FAILED(hr) )
  682. {
  683. wcscpy(options->tgtNamingContext, L"");
  684. return;
  685. }
  686. hr = pAds->Get(L"defaultNamingContext", &var);
  687. if ( FAILED(hr) )
  688. {
  689. wcscpy(options->tgtNamingContext, L"");
  690. return;
  691. }
  692. pAds->Release();
  693. wcscpy(options->tgtNamingContext, (WCHAR*) V_BSTR(&var));
  694. }
  695. //--------------------------------------------------------------------------
  696. // MakeFullyQualifiedAdsPath : Makes a LDAP sub path into a fully qualified
  697. // LDAP path name.
  698. //--------------------------------------------------------------------------
  699. void MakeFullyQualifiedAdsPath(
  700. WCHAR * sPath, //out- Fully qulified LDAP path to the object
  701. DWORD nPathLen, //in - MAX size, in characters, of the sPath buffer
  702. WCHAR * sSubPath, //in- LDAP subpath of the object
  703. WCHAR * tgtDomain, //in- Domain name where object exists.
  704. WCHAR * sDN //in- Default naming context for the Domain
  705. )
  706. {
  707. if ((!sPath) || (!sSubPath) || (!tgtDomain) || (!sDN))
  708. return;
  709. _bstr_t sTempPath;
  710. if (wcsncmp(sSubPath, L"LDAP://", 7) == 0)
  711. {
  712. //it is already a fully qualified LDAP path so lets copy it and return it
  713. wcsncpy(sPath, sSubPath, nPathLen-1);
  714. return;
  715. }
  716. //We need to build this path so lets get to work
  717. if ( wcslen(sDN) )
  718. {
  719. sTempPath = L"LDAP://";
  720. sTempPath += tgtDomain;
  721. sTempPath += L"/";
  722. sTempPath += sSubPath;
  723. sTempPath += L",";
  724. sTempPath += sDN;
  725. wcsncpy(sPath, sTempPath, nPathLen-1);
  726. // wsprintf(sPath, L"LDAP://%s/%s,%s", tgtDomain, sSubPath, sDN);
  727. }
  728. else
  729. {
  730. sTempPath = L"LDAP://";
  731. sTempPath += tgtDomain;
  732. sTempPath += L"/";
  733. sTempPath += sSubPath;
  734. wcsncpy(sPath, sTempPath, nPathLen-1);
  735. // wsprintf(sPath, L"LDAP://%s/%s", tgtDomain, sSubPath);
  736. }
  737. }
  738. //--------------------------------------------------------------------------
  739. // IsAccountMigrated : Function checks if the account has been migrated in
  740. // the past. If it has it returns true filling in the
  741. // name of the target object in case it was renamed.
  742. // Otherwise it returns FALSE and Empty string for the
  743. // target name.
  744. //--------------------------------------------------------------------------
  745. bool IsAccountMigrated(
  746. TAcctReplNode * pNode, //in -Account node that contains the Account info
  747. Options * pOptions, //in -Options as specified by the user.
  748. IIManageDBPtr pDb, //in -Pointer to DB manager. We dont want to create this object for every account we process
  749. WCHAR * sTgtSam //in,out - Name of the target object that was copied if any.
  750. )
  751. {
  752. IVarSetPtr pVs(__uuidof(VarSet));
  753. IUnknown * pUnk;
  754. pVs->QueryInterface(IID_IUnknown, (void**) &pUnk);
  755. HRESULT hrFind = pDb->raw_GetAMigratedObject(const_cast<WCHAR*>(pNode->GetName()), pOptions->srcDomain, pOptions->tgtDomain, &pUnk);
  756. pUnk->Release();
  757. if ( hrFind != S_OK )
  758. {
  759. wcscpy(sTgtSam,L"");
  760. return false;
  761. }
  762. else
  763. {
  764. _bstr_t sText;
  765. sText = pVs->get(L"MigratedObjects.TargetSamName");
  766. if (!(WCHAR*)sText)
  767. {
  768. wcscpy(sTgtSam,L"");
  769. return false;
  770. }
  771. wcscpy(sTgtSam, (WCHAR*) sText);
  772. return true;
  773. }
  774. }
  775. bool CheckifAccountExists(
  776. Options const * options, //in-Options as set by the user
  777. WCHAR * acctName //in-Name of the account to look for
  778. )
  779. {
  780. USER_INFO_0 * buf;
  781. long rc = 0;
  782. if ( (rc = NetUserGetInfo(const_cast<WCHAR*>(options->tgtComp), acctName, 0, (LPBYTE *) &buf)) == NERR_Success )
  783. {
  784. NetApiBufferFree(buf);
  785. return true;
  786. }
  787. if ( (rc = NetGroupGetInfo(const_cast<WCHAR*>(options->tgtComp), acctName, 0, (LPBYTE *) &buf)) == NERR_Success )
  788. {
  789. NetApiBufferFree(buf);
  790. return true;
  791. }
  792. if ( (rc = NetLocalGroupGetInfo(const_cast<WCHAR*>(options->tgtComp), acctName, 0, (LPBYTE *) &buf)) == NERR_Success )
  793. {
  794. NetApiBufferFree(buf);
  795. return true;
  796. }
  797. return false;
  798. }
  799. //--------------------------------------------------------------------------
  800. // Mark : Increments appropriate counters depending on the arguments.
  801. //--------------------------------------------------------------------------
  802. void Mark(
  803. _bstr_t sMark, //in- Represents the type of marking { processed, errors, replaced, created }
  804. _bstr_t sObj //in- Type of object being marked { user, group, computer }
  805. )
  806. {
  807. if (!UStrICmp(sMark,L"processed"))
  808. {
  809. if ( !UStrICmp(sObj,L"user")) processed.users++;
  810. else if ( !UStrICmp(sObj,L"group")) processed.globals++;
  811. else if ( !UStrICmp(sObj,L"computer")) processed.computers++;
  812. else if ( !UStrICmp(sObj,L"generic")) processed.generic++;
  813. }
  814. else if (!UStrICmp(sMark,L"errors"))
  815. {
  816. if ( !UStrICmp(sObj,L"user")) errors.users++;
  817. else if ( !UStrICmp(sObj,L"group")) errors.globals++;
  818. else if ( !UStrICmp(sObj,L"computer")) errors.computers++;
  819. else if ( !UStrICmp(sObj,L"generic")) errors.generic++;
  820. }
  821. else if (!UStrICmp(sMark,L"warnings"))
  822. {
  823. if ( !UStrICmp(sObj,L"user")) warnings.users++;
  824. else if ( !UStrICmp(sObj,L"group")) warnings.globals++;
  825. else if ( !UStrICmp(sObj,L"computer")) warnings.computers++;
  826. else if ( !UStrICmp(sObj,L"generic")) warnings.generic++;
  827. }
  828. else if (!UStrICmp(sMark,L"replaced"))
  829. {
  830. if ( !UStrICmp(sObj,L"user")) replaced.users++;
  831. else if ( !UStrICmp(sObj,L"group")) replaced.globals++;
  832. else if ( !UStrICmp(sObj,L"computer")) replaced.computers++;
  833. else if ( !UStrICmp(sObj,L"generic")) replaced.generic++;
  834. }
  835. else if (!UStrICmp(sMark,L"created"))
  836. {
  837. if ( !UStrICmp(sObj,L"user")) created.users++;
  838. else if ( !UStrICmp(sObj,L"group")) created.globals++;
  839. else if ( !UStrICmp(sObj,L"computer")) created.computers++;
  840. else if ( !UStrICmp(sObj,L"generic")) created.generic++;
  841. }
  842. }
  843. HRESULT
  844. GetRidPoolAllocator(
  845. Options * pOptions
  846. )
  847. {
  848. _bstr_t sRidMaster = L"";
  849. BSTR sParent = L"";
  850. _bstr_t sRidAllocator = pOptions->srcComp;
  851. _variant_t varProp;
  852. IADs * pAds = NULL;
  853. WCHAR sPath[LEN_Path];
  854. HRESULT hr = S_OK;
  855. wsprintf(sPath, L"LDAP://%s/CN=RID Manager$,CN=System,%s", pOptions->srcComp + 2, pOptions->srcNamingContext);
  856. hr = ADsGetObject(sPath, IID_IADs, (void**)&pAds);
  857. if ( SUCCEEDED(hr) )
  858. {
  859. hr = pAds->Get(L"fSMORoleOwner", &varProp);
  860. pAds->Release();
  861. }
  862. if ( SUCCEEDED(hr) )
  863. {
  864. sRidMaster = varProp.bstrVal;
  865. wsprintf(sPath, L"LDAP://%s/%s", pOptions->srcDomain, (WCHAR*) sRidMaster);
  866. hr = ADsGetObject(sPath, IID_IADs, (void**)&pAds);
  867. }
  868. if ( SUCCEEDED(hr) )
  869. {
  870. hr = pAds->get_Parent(&sParent);
  871. pAds->Release();
  872. }
  873. if ( SUCCEEDED(hr) )
  874. {
  875. hr = ADsGetObject((WCHAR*)sParent, IID_IADs, (void**)&pAds);
  876. }
  877. if ( SUCCEEDED(hr) )
  878. {
  879. hr = pAds->Get(L"name", &varProp);
  880. pAds->Release();
  881. }
  882. if ( SUCCEEDED(hr) )
  883. sRidAllocator = varProp.bstrVal;
  884. if ( ((WCHAR*) sRidAllocator)[0] == L'\\' )
  885. wcscpy(pOptions->srcComp, (WCHAR*) sRidAllocator);
  886. else
  887. wsprintf(pOptions->srcComp, L"\\\\%s", (WCHAR*) sRidAllocator);
  888. return hr;
  889. }