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.

2440 lines
65 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. profmap.c
  5. Abstract:
  6. Implements profile mapping APIs, to move local profile ownership
  7. from one user to another.
  8. Author:
  9. Jim Schmidt (jimschm) 27-May-1999
  10. Revision History:
  11. <alias> <date> <comments>
  12. --*/
  13. #include "pch.h"
  14. #define PCOMMON_IMPL
  15. #include "pcommon.h"
  16. //
  17. // Worker prototypes
  18. //
  19. DWORD
  20. pRemapUserProfile (
  21. IN DWORD Flags,
  22. IN PSID SidCurrent,
  23. IN PSID SidNew
  24. );
  25. BOOL
  26. pLocalRemapAndMoveUserW (
  27. IN DWORD Flags,
  28. IN PCWSTR ExistingUser,
  29. IN PCWSTR NewUser
  30. );
  31. VOID
  32. pFixSomeSidReferences (
  33. PSID ExistingSid,
  34. PSID NewSid
  35. );
  36. VOID
  37. pOurGetProfileRoot (
  38. IN PCWSTR SidString,
  39. OUT PWSTR ProfileRoot
  40. );
  41. #define REMAP_KEY_NAME L"$remap$"
  42. //
  43. // Implementation
  44. //
  45. BOOL
  46. WINAPI
  47. DllMain (
  48. IN HINSTANCE hInstance,
  49. IN DWORD dwReason,
  50. IN LPVOID lpReserved
  51. )
  52. {
  53. return TRUE;
  54. }
  55. /*++
  56. Routine Description:
  57. SmartLocalFree and SmartRegCloseKey are cleanup routines that ignore NULL
  58. values.
  59. Arguments:
  60. Mem or Key - Specifies the value to clean up.
  61. Return Value:
  62. None.
  63. --*/
  64. VOID
  65. SmartLocalFree (
  66. PVOID Mem OPTIONAL
  67. )
  68. {
  69. if (Mem) {
  70. LocalFree (Mem);
  71. }
  72. }
  73. VOID
  74. SmartRegCloseKey (
  75. HKEY Key OPTIONAL
  76. )
  77. {
  78. if (Key) {
  79. RegCloseKey (Key);
  80. }
  81. }
  82. BOOL
  83. WINAPI
  84. pLocalRemapUserProfileW (
  85. IN DWORD Flags,
  86. IN PSID SidCurrent,
  87. IN PSID SidNew
  88. )
  89. /*++
  90. Routine Description:
  91. pLocalRemapUserProfile begins the process of remapping a profile from one
  92. SID to another. This function validates the caller's arguments, and then
  93. calls pRemapUserProfile to do the work. Top-level exceptions are handled
  94. here.
  95. Arguments:
  96. Flags - Specifies zero or more profile mapping flags.
  97. SidCurrent - Specifies the SID of the user who's profile is going to be
  98. remaped.
  99. SidNew - Specifies the SID of the user who will own the profile.
  100. Return Value:
  101. TRUE if success, FALSE if failure. GetLastError provides failure code.
  102. --*/
  103. {
  104. DWORD Error;
  105. PWSTR CurrentSidString = NULL;
  106. PWSTR NewSidString = NULL;
  107. INT Order;
  108. PWSTR p, q;
  109. HANDLE hToken = NULL;
  110. DWORD dwErr1 = ERROR_ACCESS_DENIED, dwErr2 = ERROR_ACCESS_DENIED;
  111. DEBUGMSG((DM_VERBOSE, "========================================================="));
  112. DEBUGMSG((
  113. DM_VERBOSE,
  114. "RemapUserProfile: Entering, Flags = <0x%x>, SidCurrent = <0x%x>, SidNew = <0x%x>",
  115. Flags,
  116. SidCurrent,
  117. SidNew
  118. ));
  119. if (!OpenThreadToken (GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hToken)) {
  120. if (!OpenProcessToken (GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) {
  121. Error = GetLastError();
  122. DEBUGMSG((DM_VERBOSE, "RemapAndMoveUserW: OpenProcessToken failed with code %u", Error));
  123. goto Exit;
  124. }
  125. }
  126. if (!IsUserAnAdminMember (hToken)) {
  127. Error = ERROR_ACCESS_DENIED;
  128. DEBUGMSG((DM_VERBOSE, "RemapAndMoveUserW: Caller is not an administrator"));
  129. goto Exit;
  130. }
  131. #ifdef DBG
  132. {
  133. PSID DbgSid;
  134. PWSTR DbgSidString;
  135. DbgSid = GetUserSid (hToken);
  136. if (OurConvertSidToStringSid (DbgSid, &DbgSidString)) {
  137. DEBUGMSG ((DM_VERBOSE, "RemapAndMoveUserW: Caller's SID is %s", DbgSidString));
  138. DeleteSidString (DbgSidString);
  139. }
  140. DeleteUserSid (DbgSid);
  141. }
  142. #endif
  143. //
  144. // Validate args
  145. //
  146. Error = ERROR_INVALID_PARAMETER;
  147. if (!IsValidSid (SidCurrent)) {
  148. DEBUGMSG((DM_WARNING, "RemapUserProfile: received invalid current user sid."));
  149. goto Exit;
  150. }
  151. if (!IsValidSid (SidNew)) {
  152. DEBUGMSG((DM_WARNING, "RemapUserProfile: received invalid new user sid."));
  153. goto Exit;
  154. }
  155. //
  156. // All arguments are valid. Lock the users and call a worker.
  157. //
  158. if (!OurConvertSidToStringSid (SidCurrent, &CurrentSidString)) {
  159. Error = GetLastError();
  160. DEBUGMSG((DM_WARNING, "RemapUserProfile: Can't stringify current sid."));
  161. goto Exit;
  162. }
  163. if (!OurConvertSidToStringSid (SidNew, &NewSidString)) {
  164. Error = GetLastError();
  165. DEBUGMSG((DM_WARNING, "RemapUserProfile: Can't stringify new sid."));
  166. goto Exit;
  167. }
  168. //
  169. // SID arguments must be unique. We assume the OS uses the same character set
  170. // to stringify a SID, even if something like a locale change happens in the
  171. // middle of our code.
  172. //
  173. p = CurrentSidString;
  174. q = NewSidString;
  175. while (*p && *p == *q) {
  176. p++;
  177. q++;
  178. }
  179. Order = *p - *q;
  180. if (!Order) {
  181. DEBUGMSG((DM_WARNING, "RemapUserProfile: Both sids match (%s=%s)",
  182. CurrentSidString, NewSidString));
  183. Error = ERROR_INVALID_PARAMETER;
  184. goto Exit;
  185. }
  186. ASSERT (lstrcmpi (CurrentSidString, NewSidString));
  187. //
  188. // Grab the user profile mutexes in wchar-sorted order. This eliminates
  189. // a deadlock with another RemapUserProfile call.
  190. //
  191. if (Order < 0) {
  192. dwErr1 = EnterUserProfileLock (CurrentSidString);
  193. if (dwErr1 == ERROR_SUCCESS) {
  194. dwErr2 = EnterUserProfileLock (NewSidString);
  195. }
  196. } else {
  197. dwErr2 = EnterUserProfileLock (NewSidString);
  198. if (dwErr2 == ERROR_SUCCESS) {
  199. dwErr1 = EnterUserProfileLock (CurrentSidString);
  200. }
  201. }
  202. if (dwErr1 != ERROR_SUCCESS || dwErr2 != ERROR_SUCCESS) {
  203. Error = GetLastError();
  204. DEBUGMSG((DM_WARNING, "RemapUserProfile: Failed to grab a user profile lock, error = %u", Error));
  205. goto Exit;
  206. }
  207. __try {
  208. Error = pRemapUserProfile (Flags, SidCurrent, SidNew);
  209. }
  210. __except (EXCEPTION_EXECUTE_HANDLER) {
  211. Error = ERROR_NOACCESS;
  212. DEBUGMSG((DM_WARNING, "RemapUserProfile: Exception thrown in PrivateRemapUserProfile."));
  213. }
  214. Exit:
  215. if (hToken) {
  216. CloseHandle (hToken);
  217. }
  218. if (CurrentSidString) {
  219. if(dwErr1 == ERROR_SUCCESS) {
  220. LeaveUserProfileLock (CurrentSidString);
  221. }
  222. DeleteSidString (CurrentSidString);
  223. }
  224. if (NewSidString) {
  225. if(dwErr2 == ERROR_SUCCESS) {
  226. LeaveUserProfileLock (NewSidString);
  227. }
  228. DeleteSidString (NewSidString);
  229. }
  230. SetLastError (Error);
  231. return Error == ERROR_SUCCESS;
  232. }
  233. BOOL
  234. GetNamesFromUserSid (
  235. IN PCWSTR RemoteTo,
  236. IN PSID Sid,
  237. OUT PWSTR *User,
  238. OUT PWSTR *Domain
  239. )
  240. /*++
  241. Routine Description:
  242. GetNamesFromUserSid obtains the user and domain name from a SID. The SID
  243. must be a user account (not a group, printer, etc.).
  244. Arguments:
  245. RemoteTo - Specifies the computer to remote the call to
  246. Sid - Specifies the SID to look up
  247. User - Receives the user name. If non-NULL, the caller must free this
  248. buffer with LocalFree.
  249. Domain - Receives the domain name. If non-NULL, the caller must free the
  250. buffer with LocalFree.
  251. Return Value:
  252. TRUE on success, FALSE on failure, GetLastError provides failure code.
  253. --*/
  254. {
  255. DWORD UserSize = 256;
  256. DWORD DomainSize = 256;
  257. PWSTR UserBuffer = NULL;
  258. PWSTR DomainBuffer = NULL;
  259. DWORD Result = ERROR_SUCCESS;
  260. BOOL b;
  261. SID_NAME_USE use;
  262. //
  263. // Allocate initial buffers of 256 chars
  264. //
  265. UserBuffer = LocalAlloc (LPTR, UserSize * sizeof (WCHAR));
  266. if (!UserBuffer) {
  267. Result = ERROR_OUTOFMEMORY;
  268. DEBUGMSG((DM_WARNING, "GetNamesFromUserSid: Alloc error %u.", GetLastError()));
  269. goto Exit;
  270. }
  271. DomainBuffer = LocalAlloc (LPTR, DomainSize * sizeof (WCHAR));
  272. if (!DomainBuffer) {
  273. Result = ERROR_OUTOFMEMORY;
  274. DEBUGMSG((DM_WARNING, "GetNamesFromUserSid: Alloc error %u.", GetLastError()));
  275. goto Exit;
  276. }
  277. b = LookupAccountSid (
  278. RemoteTo,
  279. Sid,
  280. UserBuffer,
  281. &UserSize,
  282. DomainBuffer,
  283. &DomainSize,
  284. &use
  285. );
  286. if (!b) {
  287. Result = GetLastError();
  288. if (Result == ERROR_NONE_MAPPED) {
  289. DEBUGMSG((DM_WARNING, "GetNamesFromUserSid: Account not found"));
  290. goto Exit;
  291. }
  292. if (UserSize <= 256 && DomainSize <= 256) {
  293. DEBUGMSG((DM_WARNING, "GetNamesFromUserSid: Unexpected error %u", Result));
  294. Result = ERROR_UNEXP_NET_ERR;
  295. goto Exit;
  296. }
  297. //
  298. // Try allocating new buffers
  299. //
  300. if (UserSize > 256) {
  301. SmartLocalFree (UserBuffer);
  302. UserBuffer = LocalAlloc (LPTR, UserSize * sizeof (WCHAR));
  303. if (!UserBuffer) {
  304. Result = ERROR_OUTOFMEMORY;
  305. DEBUGMSG((DM_WARNING, "GetNamesFromUserSid: Alloc error %u.", GetLastError()));
  306. goto Exit;
  307. }
  308. }
  309. if (DomainSize > 256) {
  310. SmartLocalFree (DomainBuffer);
  311. DomainBuffer = LocalAlloc (LPTR, DomainSize * sizeof (WCHAR));
  312. if (!DomainBuffer) {
  313. Result = ERROR_OUTOFMEMORY;
  314. DEBUGMSG((DM_WARNING, "GetNamesFromUserSid: Alloc error %u.", GetLastError()));
  315. goto Exit;
  316. }
  317. }
  318. //
  319. // Try look up again
  320. //
  321. b = LookupAccountSid (
  322. RemoteTo,
  323. Sid,
  324. UserBuffer,
  325. &UserSize,
  326. DomainBuffer,
  327. &DomainSize,
  328. &use
  329. );
  330. if (!b) {
  331. //
  332. // All attempts failed.
  333. //
  334. Result = GetLastError();
  335. if (Result != ERROR_NONE_MAPPED) {
  336. DEBUGMSG((DM_WARNING, "GetNamesFromUserSid: Unexpected error %u (2)", Result));
  337. Result = ERROR_UNEXP_NET_ERR;
  338. }
  339. goto Exit;
  340. }
  341. }
  342. //
  343. // LookupAccountSid succeeded. Now verify that the accout type
  344. // is correct.
  345. //
  346. if (use != SidTypeUser) {
  347. DEBUGMSG((DM_WARNING, "GetNamesFromUserSid: SID specifies bad account type: %u", (DWORD) use));
  348. Result = ERROR_NONE_MAPPED;
  349. goto Exit;
  350. }
  351. ASSERT (Result == ERROR_SUCCESS);
  352. Exit:
  353. if (Result != ERROR_SUCCESS) {
  354. SmartLocalFree (UserBuffer);
  355. UserBuffer = NULL;
  356. SmartLocalFree (DomainBuffer);
  357. DomainBuffer = NULL;
  358. SetLastError (Result);
  359. }
  360. *User = UserBuffer;
  361. *Domain = DomainBuffer;
  362. return (Result == ERROR_SUCCESS);
  363. }
  364. DWORD
  365. pRemapUserProfile (
  366. IN DWORD Flags,
  367. IN PSID SidCurrent,
  368. IN PSID SidNew
  369. )
  370. /*++
  371. Routine Description:
  372. pRemapUserProfile changes the security of a profile from one SID to
  373. another. Upon completion, the original user will not have access to the
  374. profile, and the new user will.
  375. Arguments:
  376. Flags - Specifies zero or more profile remap flags. Specify
  377. REMAP_PROFILE_NOOVERWRITE to guarantee no existing user
  378. setting is overwritten. Specify
  379. REMAP_PROFILE_NOUSERNAMECHANGE to make sure the user name does
  380. not change.
  381. SidCurrent - Specifies the current user SID. This user must own the profile,
  382. and upon completion, the user will not have a local profile.
  383. SidNew - Specifies the new user SID. This user will own the profile
  384. upon completion.
  385. Return Value:
  386. A Win32 status code.
  387. --*/
  388. {
  389. PWSTR CurrentUser = NULL;
  390. PWSTR CurrentDomain = NULL;
  391. PWSTR CurrentSidString = NULL;
  392. PWSTR NewUser = NULL;
  393. PWSTR NewDomain = NULL;
  394. PWSTR NewSidString = NULL;
  395. DWORD Size;
  396. DWORD Result = ERROR_SUCCESS;
  397. INT UserCompare;
  398. INT DomainCompare;
  399. BOOL b;
  400. HKEY hCurrentProfile = NULL;
  401. HKEY hNewProfile = NULL;
  402. HKEY hProfileList = NULL;
  403. LONG rc;
  404. DWORD Type;
  405. BOOL CleanUpFailedCopy = FALSE;
  406. DWORD Loaded;
  407. UNICODE_STRING Unicode_String;
  408. NTSTATUS Status;
  409. //
  410. // The caller must make sure we have valid args.
  411. //
  412. //
  413. // Get the names for the user
  414. //
  415. b = GetNamesFromUserSid (NULL, SidCurrent, &CurrentUser, &CurrentDomain);
  416. if (!b) {
  417. Result = GetLastError();
  418. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Current user SID is not a valid user"));
  419. goto Exit;
  420. }
  421. b = GetNamesFromUserSid (NULL, SidNew, &NewUser, &NewDomain);
  422. if (!b) {
  423. Result = GetLastError();
  424. DEBUGMSG((DM_WARNING, "pRemapUserProfile: New user SID is not a valid user"));
  425. goto Exit;
  426. }
  427. //
  428. // Compare them
  429. //
  430. UserCompare = lstrcmpi (CurrentUser, NewUser);
  431. DomainCompare = lstrcmpi (CurrentDomain, NewDomain);
  432. //
  433. // Either the user or domain must be different. If the caller specifies
  434. // REMAP_PROFILE_NOUSERNAMECHANGE, then user cannot be different.
  435. //
  436. if (UserCompare == 0 && DomainCompare == 0) {
  437. //
  438. // This case should not be possible.
  439. //
  440. ASSERT (FALSE);
  441. Result = ERROR_INVALID_PARAMETER;
  442. DEBUGMSG((DM_WARNING, "pRemapUserProfile: User and domain names match for different SIDs"));
  443. goto Exit;
  444. }
  445. if ((Flags & REMAP_PROFILE_NOUSERNAMECHANGE) && UserCompare != 0) {
  446. DEBUGMSG((DM_WARNING, "pRemapUserProfile: User name can't change from %s to %s",
  447. CurrentUser, NewUser));
  448. Result = ERROR_BAD_USERNAME;
  449. goto Exit;
  450. }
  451. //
  452. // The SID change now makes sense. Let's change it. Start by
  453. // obtaining a string version of the SID.
  454. //
  455. if (!OurConvertSidToStringSid (SidCurrent, &CurrentSidString)) {
  456. Result = GetLastError();
  457. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Can't stringify current sid."));
  458. goto Exit;
  459. }
  460. if (!OurConvertSidToStringSid (SidNew, &NewSidString)) {
  461. Result = GetLastError();
  462. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Can't stringify new sid."));
  463. goto Exit;
  464. }
  465. //
  466. // Open the profile list key
  467. //
  468. rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE, PROFILE_LIST_PATH, 0, KEY_READ|KEY_WRITE,
  469. &hProfileList);
  470. if (rc != ERROR_SUCCESS) {
  471. Result = rc;
  472. DEBUGMSG((DM_WARNING, "PrivateRemapUserProfile: Can't open profile list key."));
  473. goto Exit;
  474. }
  475. //
  476. // Open the current user's profile list key. Then make sure the profile is not
  477. // loaded, and get the profile directory.
  478. //
  479. rc = RegOpenKeyEx (hProfileList, CurrentSidString, 0, KEY_READ | KEY_WRITE, &hCurrentProfile);
  480. if (rc != ERROR_SUCCESS) {
  481. if (rc == ERROR_FILE_NOT_FOUND) {
  482. Result = ERROR_NO_SUCH_USER;
  483. } else {
  484. Result = rc;
  485. }
  486. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Can't open current user's profile list key."));
  487. goto Exit;
  488. }
  489. Size = sizeof(Loaded);
  490. rc = RegQueryValueEx (hCurrentProfile, L"RefCount", NULL, &Type, (PBYTE) &Loaded, &Size);
  491. if (rc != ERROR_SUCCESS || Type != REG_DWORD) {
  492. DEBUGMSG((DM_VERBOSE, "pRemapUserProfile: Current user does not have a ref count."));
  493. Loaded = 0;
  494. }
  495. if (Loaded) {
  496. Result = ERROR_ACCESS_DENIED;
  497. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Current user profile is loaded."));
  498. goto Exit;
  499. }
  500. //
  501. // Now open the new user's key. If it already exists, then the
  502. // caller can specify REMAP_PROFILE_NOOVERWRITE to make sure
  503. // we don't blow away an existing profile setting.
  504. //
  505. rc = RegOpenKeyEx(hProfileList, NewSidString, 0, KEY_READ | KEY_WRITE, &hNewProfile);
  506. if (rc == ERROR_SUCCESS) {
  507. //
  508. // Did the caller specify REMAP_PROFILE_NOOVERWRITE?
  509. //
  510. if (Flags & REMAP_PROFILE_NOOVERWRITE) {
  511. Result = ERROR_USER_EXISTS;
  512. DEBUGMSG((DM_VERBOSE, "pRemapUserProfile: Destination profile entry exists."));
  513. goto Exit;
  514. }
  515. //
  516. // Verify existing profile is not loaded
  517. //
  518. Size = sizeof(Loaded);
  519. rc = RegQueryValueEx (hNewProfile, L"RefCount", NULL, &Type, (PBYTE) &Loaded, &Size);
  520. if (rc != ERROR_SUCCESS || Type != REG_DWORD) {
  521. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Existing destination user does not have a ref count."));
  522. Loaded = 0;
  523. }
  524. if (Loaded) {
  525. Result = ERROR_ACCESS_DENIED;
  526. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Existing destination user profile is loaded."));
  527. goto Exit;
  528. }
  529. //
  530. // Remove the key
  531. //
  532. RegCloseKey (hNewProfile);
  533. hNewProfile = NULL;
  534. rc = RegDelnode (hProfileList, NewSidString);
  535. if (rc != ERROR_SUCCESS) {
  536. Result = rc;
  537. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Can't reset new profile list key."));
  538. goto Exit;
  539. }
  540. }
  541. //
  542. // Transfer contents of current user key to new user using NtRenameKey.
  543. //
  544. // If an error is encountered, we abandon the successful work above,
  545. // which includes possibly deletion of an existing profile list key.
  546. //
  547. CleanUpFailedCopy = TRUE;
  548. Unicode_String.Length = ByteCountW(NewSidString);
  549. Unicode_String.MaximumLength = Unicode_String.Length + sizeof(WCHAR);
  550. Unicode_String.Buffer = NewSidString;
  551. Status = NtRenameKey(hCurrentProfile, &Unicode_String);
  552. if (Status != STATUS_SUCCESS) {
  553. Result = RtlNtStatusToDosError(Status);
  554. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Error %d in renaming profile list entry.", Result));
  555. goto Exit;
  556. }
  557. // Close the old key
  558. RegCloseKey (hCurrentProfile);
  559. hCurrentProfile = NULL;
  560. //
  561. // Now open the new key and update SID & GUID information in it
  562. //
  563. rc = RegOpenKeyEx(hProfileList, NewSidString, 0, KEY_READ | KEY_WRITE, &hNewProfile);
  564. if (rc != ERROR_SUCCESS) {
  565. Result = rc;
  566. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Error %d in opening new profile list entry.", Result));
  567. goto Exit;
  568. }
  569. //
  570. // Update new profile's SID
  571. //
  572. rc = RegSetValueEx (hNewProfile, L"SID", 0, REG_BINARY, SidNew, GetLengthSid (SidNew));
  573. if (rc != ERROR_SUCCESS) {
  574. Result = rc;
  575. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Error %d setting new profile SID.", Result));
  576. goto Exit;
  577. }
  578. //
  579. // Delete GUID value & associated GUID key if it exists.
  580. // It will get re-established on the next logon.
  581. //
  582. if (!DeleteProfileGuidSettings (hNewProfile)) {
  583. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Error %d in deleting profile GUID settings"));
  584. }
  585. //
  586. // Set security on the new key. We pass pNewSid and that is all
  587. // CreateUserProfile needs. To get by arg checking, we throw in
  588. // NewUser as the user name.
  589. //
  590. if (!UpdateProfileSecurity (SidNew)) {
  591. Result = GetLastError();
  592. DEBUGMSG((DM_WARNING, "pRemapUserProfile: UpdateProfileSecurity returned %u.", Result));
  593. goto Exit;
  594. }
  595. //
  596. // Success -- the profile was transferred and nothing went wrong
  597. //
  598. CleanUpFailedCopy = FALSE;
  599. RegFlushKey (HKEY_LOCAL_MACHINE);
  600. ASSERT (Result == ERROR_SUCCESS);
  601. Exit:
  602. SmartRegCloseKey (hCurrentProfile);
  603. SmartRegCloseKey (hNewProfile);
  604. if (CleanUpFailedCopy) {
  605. DEBUGMSG((DM_WARNING, "pRemapUserProfile: Backing out changes because of failure"));
  606. RegDelnode (hProfileList, NewSidString);
  607. }
  608. SmartLocalFree (CurrentUser);
  609. SmartLocalFree (CurrentDomain);
  610. SmartLocalFree (NewUser);
  611. SmartLocalFree (NewDomain);
  612. DeleteSidString (CurrentSidString);
  613. DeleteSidString (NewSidString);
  614. SmartRegCloseKey (hProfileList);
  615. return Result;
  616. }
  617. BOOL
  618. WINAPI
  619. RemapUserProfileW (
  620. IN PCWSTR Computer,
  621. IN DWORD Flags,
  622. IN PSID SidCurrent,
  623. IN PSID SidNew
  624. )
  625. /*++
  626. Routine Description:
  627. RemapUserProfileW is the exported API. It calls the local version via RPC.
  628. Arguments:
  629. Computer - Specifies the computer to remote the API to. If NULL or ".",
  630. the API will run locally. If non-NULL, the API will run on
  631. the remote computer.
  632. Flags - Specifies the profile mapping flags. See implementation above
  633. for details.
  634. SidCurrent - Specifies the SID of the user who owns the profile.
  635. SidNew - Specifies the SID of the user who will own the profile after
  636. the API completes.
  637. Return Value:
  638. TRUE if success, FALSE if failure. GetLastError provides the failure code.
  639. --*/
  640. {
  641. DWORD Result = ERROR_SUCCESS;
  642. HANDLE RpcHandle;
  643. if (!IsValidSid (SidCurrent)) {
  644. DEBUGMSG((DM_WARNING, "RemapUserProfileW: received invalid current user sid."));
  645. SetLastError (ERROR_INVALID_SID);
  646. return FALSE;
  647. }
  648. if (!IsValidSid (SidNew)) {
  649. DEBUGMSG((DM_WARNING, "RemapUserProfileW: received invalid new user sid."));
  650. SetLastError (ERROR_INVALID_SID);
  651. return FALSE;
  652. }
  653. //
  654. // We do not support profmap to run in remote computer until we move
  655. // the ProfMapApi rpc interface to a LocalService hosted process
  656. //
  657. if (Computer) {
  658. DEBUGMSG((DM_WARNING, "RemapUserProfileW: received computer name"));
  659. SetLastError (ERROR_INVALID_PARAMETER);
  660. return FALSE;
  661. }
  662. __try {
  663. if (pLocalRemapUserProfileW (Flags, SidCurrent, SidNew)) {
  664. Result = ERROR_SUCCESS;
  665. } else {
  666. Result = GetLastError();
  667. }
  668. }
  669. __except (EXCEPTION_EXECUTE_HANDLER) {
  670. Result = ERROR_NOACCESS;
  671. }
  672. if (Result != ERROR_SUCCESS) {
  673. SetLastError (Result);
  674. return FALSE;
  675. }
  676. return TRUE;
  677. }
  678. BOOL
  679. WINAPI
  680. RemapUserProfileA (
  681. IN PCSTR Computer,
  682. IN DWORD Flags,
  683. IN PSID SidCurrent,
  684. IN PSID SidNew
  685. )
  686. /*++
  687. Routine Description:
  688. RemapUserProfileA is a wrapper to RemapUserProfileW.
  689. Arguments:
  690. Computer - Specifies the computer to remote the API to. If NULL or ".",
  691. the API will run locally. If non-NULL, the API will run on
  692. the remote computer.
  693. Flags - Specifies the profile mapping flags. See implementation above
  694. for details.
  695. SidCurrent - Specifies the SID of the user who owns the profile.
  696. SidNew - Specifies the SID of the user who will own the profile after
  697. the API completes.
  698. Return Value:
  699. TRUE if success, FALSE if failure. GetLastError provides the failure code.
  700. --*/
  701. {
  702. PWSTR UnicodeComputer;
  703. BOOL b;
  704. DWORD Err;
  705. if (!Computer) {
  706. UnicodeComputer = NULL;
  707. } else {
  708. UnicodeComputer = ProduceWFromA (Computer);
  709. if (!UnicodeComputer) {
  710. return FALSE;
  711. }
  712. }
  713. b = RemapUserProfileW (UnicodeComputer, Flags, SidCurrent, SidNew);
  714. Err = GetLastError();
  715. SmartLocalFree (UnicodeComputer);
  716. SetLastError (Err);
  717. return b;
  718. }
  719. BOOL
  720. WINAPI
  721. InitializeProfileMappingApi (
  722. VOID
  723. )
  724. /*++
  725. Routine Description:
  726. InitializeProfileMappingApi is called by winlogon.exe to initialize the RPC
  727. server interfaces.
  728. Arguments:
  729. None.
  730. Return Value:
  731. TRUE if successful, FALSE otherwise. GetLastError provides the failure
  732. code.
  733. --*/
  734. {
  735. // We do not instantiate any rpc interface in console winlogon any more
  736. return TRUE;
  737. }
  738. BOOL
  739. pHasPrefix (
  740. IN PCWSTR Prefix,
  741. IN PCWSTR String
  742. )
  743. /*++
  744. Routine Description:
  745. pHasPrefix checks String to see if it begins with Prefix. The check is
  746. case-insensitive.
  747. Arguments:
  748. Prefix - Specifies the prefix to check
  749. String - Specifies the string that may or may not have the prefix.
  750. Return Value:
  751. TRUE if String has the prefix, FALSE otherwise.
  752. --*/
  753. {
  754. WCHAR c1 = 0, c2 = 0;
  755. while (*Prefix && *String) {
  756. c1 = (WCHAR) CharLower ((PWSTR) (*Prefix++));
  757. c2 = (WCHAR) CharLower ((PWSTR) (*String++));
  758. if (c1 != c2) {
  759. return FALSE;
  760. }
  761. }
  762. if (*Prefix) {
  763. return FALSE; // String is smaller than Prefix
  764. }
  765. return TRUE;
  766. }
  767. PSID
  768. pGetSidForUser (
  769. IN PCWSTR Name
  770. )
  771. /*++
  772. Routine Description:
  773. pGetSidForUser is a wrapper to LookupAccountSid. It allocates the SID via
  774. LocalAlloc.
  775. Arguments:
  776. Name - Specifies the user name to look up
  777. Return Value:
  778. A pointer to the SID, which must be freed with LocalFree, or NULL on error.
  779. GetLastError provides failure code.
  780. --*/
  781. {
  782. DWORD cbSid = 0;
  783. PSID pSid = NULL;
  784. DWORD cchDomain = 0;
  785. PWSTR szDomain;
  786. SID_NAME_USE SidUse;
  787. BOOL bRet = FALSE;
  788. bRet = LookupAccountName( NULL,
  789. Name,
  790. NULL,
  791. &cbSid,
  792. NULL,
  793. &cchDomain,
  794. &SidUse );
  795. if (!bRet && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
  796. pSid = (PSID) LocalAlloc(LPTR, cbSid);
  797. if (!pSid) {
  798. return NULL;
  799. }
  800. szDomain = (PWSTR) LocalAlloc(LPTR, cchDomain * sizeof(WCHAR));
  801. if (!szDomain) {
  802. LocalFree(pSid);
  803. return NULL;
  804. }
  805. bRet = LookupAccountName( NULL,
  806. Name,
  807. pSid,
  808. &cbSid,
  809. szDomain,
  810. &cchDomain,
  811. &SidUse );
  812. LocalFree(szDomain);
  813. if (!bRet) {
  814. LocalFree(pSid);
  815. pSid = NULL;
  816. }
  817. }
  818. return pSid;
  819. }
  820. BOOL
  821. WINAPI
  822. RemapAndMoveUserW (
  823. IN PCWSTR RemoteTo,
  824. IN DWORD Flags,
  825. IN PCWSTR ExistingUser,
  826. IN PCWSTR NewUser
  827. )
  828. /*++
  829. Routine Description:
  830. RemapAndMoveUserW is an API entry point to move settings of one SID to
  831. another. In particular, it moves the local user profile, local group
  832. memberships, some registry use of the SID, and some file system use of the
  833. SID.
  834. Arguments:
  835. RemoteTo - Specifies the computer to remote the call to. Specify a
  836. standard name ("\\computer" or "."). If NULL, the call will
  837. be run locally.
  838. Flags - Specifies zero, or REMAP_PROFILE_NOOVERWRITE.
  839. ExistingUser - Specifies the existing user, in DOMAIN\user format. This
  840. user must have a local profile.
  841. NewUser - Specifies the new user who will own ExistingUser's profile
  842. after completion. Specify the user in DOMAIN\User format.
  843. Return Value:
  844. TRUE on success, FALSE on failure, GetLastError provides the failure code.
  845. --*/
  846. {
  847. DWORD Result = ERROR_SUCCESS;
  848. HANDLE RpcHandle;
  849. //
  850. // We do not support profmap to run in remote computer until we move
  851. // the ProfMapApi rpc interface to a LocalService hosted process
  852. //
  853. if (RemoteTo) {
  854. DEBUGMSG((DM_WARNING, "RemapUserProfileW: received computer name"));
  855. SetLastError (ERROR_INVALID_PARAMETER);
  856. return FALSE;
  857. }
  858. __try {
  859. if (pLocalRemapAndMoveUserW (Flags, ExistingUser, NewUser)) {
  860. Result = ERROR_SUCCESS;
  861. } else {
  862. Result = GetLastError();
  863. }
  864. }
  865. __except (EXCEPTION_EXECUTE_HANDLER) {
  866. Result = ERROR_NOACCESS;
  867. }
  868. if (Result != ERROR_SUCCESS) {
  869. SetLastError (Result);
  870. return FALSE;
  871. }
  872. return TRUE;
  873. }
  874. BOOL
  875. WINAPI
  876. RemapAndMoveUserA (
  877. IN PCSTR RemoteTo,
  878. IN DWORD Flags,
  879. IN PCSTR ExistingUser,
  880. IN PCSTR NewUser
  881. )
  882. /*++
  883. Routine Description:
  884. RemapAndMoveUserA is the ANSI API entry point. It calls RemapAndMoveUserW.
  885. Arguments:
  886. RemoteTo - Specifies the computer to remote the call to. Specify a
  887. standard name ("\\computer" or "."). If NULL, the call will
  888. be run locally.
  889. Flags - Specifies zero, or REMAP_PROFILE_NOOVERWRITE.
  890. ExistingUser - Specifies the existing user, in DOMAIN\user format. This
  891. user must have a local profile.
  892. NewUser - Specifies the new user who will own ExistingUser's profile
  893. after completion. Specify the user in DOMAIN\User format.
  894. Return Value:
  895. TRUE on success, FALSE on failure, GetLastError provides the failure code.
  896. --*/
  897. {
  898. PWSTR UnicodeRemoteTo = NULL;
  899. PWSTR UnicodeExistingUser = NULL;
  900. PWSTR UnicodeNewUser = NULL;
  901. DWORD Err;
  902. BOOL b = TRUE;
  903. Err = GetLastError();
  904. __try {
  905. if (RemoteTo) {
  906. UnicodeRemoteTo = ProduceWFromA (RemoteTo);
  907. if (!UnicodeRemoteTo) {
  908. b = FALSE;
  909. Err = GetLastError();
  910. }
  911. }
  912. if (b) {
  913. UnicodeExistingUser = ProduceWFromA (ExistingUser);
  914. if (!UnicodeExistingUser) {
  915. b = FALSE;
  916. Err = GetLastError();
  917. }
  918. }
  919. if (b) {
  920. UnicodeNewUser = ProduceWFromA (NewUser);
  921. if (!UnicodeNewUser) {
  922. b = FALSE;
  923. Err = GetLastError();
  924. }
  925. }
  926. if (b) {
  927. b = RemapAndMoveUserW (
  928. UnicodeRemoteTo,
  929. Flags,
  930. UnicodeExistingUser,
  931. UnicodeNewUser
  932. );
  933. if (!b) {
  934. Err = GetLastError();
  935. }
  936. }
  937. }
  938. __except (EXCEPTION_EXECUTE_HANDLER) {
  939. Err = ERROR_NOACCESS;
  940. }
  941. SmartLocalFree (UnicodeRemoteTo);
  942. SmartLocalFree (UnicodeExistingUser);
  943. SmartLocalFree (UnicodeNewUser);
  944. SetLastError (Err);
  945. return b;
  946. }
  947. BOOL
  948. pDoesUserHaveProfile (
  949. IN PSID Sid
  950. )
  951. /*++
  952. Routine Description:
  953. pDoesUserHaveProfile checks if a profile exists for the user, who is
  954. specified by the SID.
  955. Arguments:
  956. Sid - Specifies the SID of the user who may or may not have a local
  957. profile
  958. Return Value:
  959. TRUE if the user has a profile and the profile directory exists, FALSE
  960. otherwise.
  961. --*/
  962. {
  963. WCHAR ProfileDir[MAX_PATH];
  964. if (GetProfileRoot (Sid, ProfileDir, ARRAYSIZE(ProfileDir))) {
  965. return TRUE;
  966. }
  967. return FALSE;
  968. }
  969. BOOL
  970. pLocalRemapAndMoveUserW (
  971. IN DWORD Flags,
  972. IN PCWSTR ExistingUser,
  973. IN PCWSTR NewUser
  974. )
  975. /*++
  976. Routine Description:
  977. pLocalRemapAndMoveUserW implements the remap and move of a user's security
  978. settings. This includes moving the user's profile, moving local group
  979. membership, adjusting some of the SIDs in the registry, and adjusting some
  980. of the SID directories in the file system. Upon completion, NewUser will
  981. own ExistingUser's profile.
  982. Arguments:
  983. Flags - Specifies the profile remap flags. See RemapUserProfile.
  984. ExistingUser - Specifies the user who owns the local profile, in
  985. DOMAIN\User format.
  986. NewUser - Specifies the user who will own ExistingUser's profile upon
  987. completion. Specify the user in DOMAIN\User format.
  988. Return Value:
  989. TRUE upon success, FALSE otherwise, GetLastError provides the failure code.
  990. --*/
  991. {
  992. NET_API_STATUS Status;
  993. DWORD Entries;
  994. DWORD EntriesRead;
  995. BOOL b = FALSE;
  996. DWORD Error;
  997. WCHAR Computer[MAX_PATH];
  998. DWORD Size;
  999. PSID ExistingSid = NULL;
  1000. PSID NewSid = NULL;
  1001. USER_INFO_0 ui0;
  1002. PLOCALGROUP_USERS_INFO_0 lui0 = NULL;
  1003. LOCALGROUP_MEMBERS_INFO_0 lmi0;
  1004. PCWSTR FixedExistingUser;
  1005. PCWSTR FixedNewUser;
  1006. BOOL NewUserIsOnDomain = FALSE;
  1007. BOOL ExistingUserIsOnDomain = FALSE;
  1008. HANDLE hToken = NULL;
  1009. BOOL NewUserProfileExists = FALSE;
  1010. HRESULT hr;
  1011. Error = GetLastError();
  1012. __try {
  1013. //
  1014. // Guard the API for admins only
  1015. //
  1016. if (!OpenThreadToken (GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hToken)) {
  1017. if (!OpenProcessToken (GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) {
  1018. Error = GetLastError();
  1019. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: OpenProcessToken failed with code %u", Error));
  1020. goto Exit;
  1021. }
  1022. }
  1023. if (!IsUserAnAdminMember (hToken)) {
  1024. Error = ERROR_ACCESS_DENIED;
  1025. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Caller is not an administrator"));
  1026. goto Exit;
  1027. }
  1028. #ifdef DBG
  1029. {
  1030. PSID DbgSid;
  1031. PWSTR DbgSidString;
  1032. DbgSid = GetUserSid (hToken);
  1033. if (OurConvertSidToStringSid (DbgSid, &DbgSidString)) {
  1034. DEBUGMSG ((DM_VERBOSE, "pLocalRemapAndMoveUserW: Caller's SID is %s", DbgSidString));
  1035. DeleteSidString (DbgSidString);
  1036. }
  1037. DeleteUserSid (DbgSid);
  1038. }
  1039. #endif
  1040. //
  1041. // Determine if profile users are domain users or local users.
  1042. // Because the user names are in netbios format of domain\user,
  1043. // we know the user is local only when domain matches the
  1044. // computer name.
  1045. //
  1046. Size = ARRAYSIZE(Computer) - 2;
  1047. if (!GetComputerName (Computer, &Size)) {
  1048. Error = GetLastError();
  1049. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: GetComputerName failed with code %u", Error));
  1050. goto Exit;
  1051. }
  1052. hr = StringCchCat(Computer, ARRAYSIZE(Computer), L"\\");
  1053. if (FAILED(hr)) {
  1054. Error = HRESULT_CODE(hr);
  1055. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: StringCchCat failed with code %u", Error));
  1056. goto Exit;
  1057. }
  1058. if (pHasPrefix (Computer, ExistingUser)) {
  1059. FixedExistingUser = ExistingUser + lstrlen (Computer);
  1060. } else {
  1061. FixedExistingUser = ExistingUser;
  1062. ExistingUserIsOnDomain = TRUE;
  1063. }
  1064. if (pHasPrefix (Computer, NewUser)) {
  1065. FixedNewUser = NewUser + lstrlen (Computer);
  1066. } else {
  1067. FixedNewUser = NewUser;
  1068. NewUserIsOnDomain = TRUE;
  1069. }
  1070. //
  1071. // Get the SID of the NewUser. This might fail.
  1072. //
  1073. NewSid = pGetSidForUser (NewUser);
  1074. if (!NewSid) {
  1075. Status = GetLastError();
  1076. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Can't get info for %s, rc=%u", NewUser, Status));
  1077. } else {
  1078. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: NewUser exists"));
  1079. //
  1080. // Determine if new user has a profile
  1081. //
  1082. NewUserProfileExists = pDoesUserHaveProfile (NewSid);
  1083. if (NewUserProfileExists) {
  1084. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: NewUser profile exists"));
  1085. }
  1086. }
  1087. if (NewUserProfileExists && (Flags & REMAP_PROFILE_NOOVERWRITE)) {
  1088. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Can't overwrite existing user"));
  1089. Error = ERROR_USER_EXISTS;
  1090. goto Exit;
  1091. }
  1092. //
  1093. // Get the SID for ExitingUser. This cannot fail.
  1094. //
  1095. ExistingSid = pGetSidForUser (ExistingUser);
  1096. if (!ExistingSid) {
  1097. Error = ERROR_NONE_MAPPED;
  1098. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: No SID for %s", ExistingUser));
  1099. goto Exit;
  1100. }
  1101. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Got SIDs for users"));
  1102. //
  1103. // Decide which of the four cases we are doing:
  1104. //
  1105. // Case 1: Local account to local account
  1106. // Case 2: Domain account to local account
  1107. // Case 3: Local account to domain account
  1108. // Case 4: Domain account to domain account
  1109. //
  1110. if (!NewUserIsOnDomain && !ExistingUserIsOnDomain) {
  1111. //
  1112. // For Case 1, all we do is rename the user, and we're done.
  1113. //
  1114. //
  1115. // To rename the user, it may be necessary to delete the
  1116. // existing "new" user. This abandons a profile.
  1117. //
  1118. if (NewSid) {
  1119. if (Flags & REMAP_PROFILE_NOOVERWRITE) {
  1120. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Can't overwrite %s", FixedNewUser));
  1121. Error = ERROR_USER_EXISTS;
  1122. goto Exit;
  1123. }
  1124. Status = NetUserDel (NULL, FixedNewUser);
  1125. if (Status != ERROR_SUCCESS) {
  1126. Error = Status;
  1127. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Can't remove %s, code %u", FixedNewUser, Status));
  1128. goto Exit;
  1129. }
  1130. }
  1131. //
  1132. // Now the NewUser does not exist. We can move ExistingUser
  1133. // to MoveUser via net apis. The SID doesn't change.
  1134. //
  1135. ui0.usri0_name = (PWSTR) FixedNewUser;
  1136. Status = NetUserSetInfo (
  1137. NULL,
  1138. FixedExistingUser,
  1139. 0,
  1140. (PBYTE) &ui0,
  1141. NULL
  1142. );
  1143. if (Status != ERROR_SUCCESS) {
  1144. Error = Status;
  1145. DEBUGMSG((
  1146. DM_VERBOSE,
  1147. "pLocalRemapAndMoveUserW: Error renaming %s to %s, code %u",
  1148. FixedExistingUser,
  1149. FixedNewUser,
  1150. Status
  1151. ));
  1152. goto Exit;
  1153. }
  1154. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Renamed local user %s to %s", FixedExistingUser, FixedNewUser));
  1155. b = TRUE;
  1156. goto Exit;
  1157. }
  1158. //
  1159. // For cases 2 through 4, we need to verify that the new user
  1160. // already exists, then we adjust the system security and fix
  1161. // SID use.
  1162. //
  1163. if (!NewSid) {
  1164. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: User %s must exist", FixedNewUser));
  1165. Error = ERROR_NO_SUCH_USER;
  1166. goto Exit;
  1167. }
  1168. //
  1169. // Move the profile from ExistingUser to NewUser
  1170. //
  1171. if (!pLocalRemapUserProfileW (0, ExistingSid, NewSid)) {
  1172. Error = GetLastError();
  1173. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: LocalRemapUserProfileW failed with code %u", Error));
  1174. goto Exit;
  1175. }
  1176. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Profile was remapped"));
  1177. //
  1178. // Put NewUser in all the same groups as ExistingUser
  1179. //
  1180. Status = NetUserGetLocalGroups (
  1181. NULL,
  1182. FixedExistingUser,
  1183. 0,
  1184. 0,
  1185. (PBYTE *) &lui0,
  1186. MAX_PREFERRED_LENGTH,
  1187. &EntriesRead,
  1188. &Entries
  1189. );
  1190. if (Status != ERROR_SUCCESS) {
  1191. Error = Status;
  1192. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: NetUserGetLocalGroups failed with code %u for %s", Status, FixedExistingUser));
  1193. goto Exit;
  1194. }
  1195. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Local groups: %u", EntriesRead));
  1196. lmi0.lgrmi0_sid = NewSid;
  1197. for (Entries = 0 ; Entries < EntriesRead ; Entries++) {
  1198. Status = NetLocalGroupAddMembers (
  1199. NULL,
  1200. lui0[Entries].lgrui0_name,
  1201. 0,
  1202. (PBYTE) &lmi0,
  1203. 1
  1204. );
  1205. if (Status == ERROR_MEMBER_IN_ALIAS) {
  1206. Status = ERROR_SUCCESS;
  1207. }
  1208. if (Status != ERROR_SUCCESS) {
  1209. Error = Status;
  1210. DEBUGMSG((
  1211. DM_VERBOSE,
  1212. "pLocalRemapAndMoveUserW: NetLocalGroupAddMembers failed with code %u for %s",
  1213. Status,
  1214. lui0[Entries].lgrui0_name
  1215. ));
  1216. goto Exit;
  1217. }
  1218. }
  1219. NetApiBufferFree (lui0);
  1220. lui0 = NULL;
  1221. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Local groups transferred"));
  1222. //
  1223. // Perform fixups
  1224. //
  1225. pFixSomeSidReferences (ExistingSid, NewSid);
  1226. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Some SID references fixed"));
  1227. //
  1228. // Remove ExistingUser
  1229. //
  1230. if (!ExistingUserIsOnDomain) {
  1231. //
  1232. // Local user case: delete the user account
  1233. //
  1234. if (Flags & REMAP_PROFILE_KEEPLOCALACCOUNT) {
  1235. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Keeping local account"));
  1236. } else {
  1237. Status = NetUserDel (NULL, FixedExistingUser);
  1238. if (Status != ERROR_SUCCESS) {
  1239. Error = Status;
  1240. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: NetUserDel failed with code %u for %s", Error, FixedExistingUser));
  1241. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Ignoring error because changes cannot be undone!"));
  1242. }
  1243. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Removed local user %s", FixedExistingUser));
  1244. }
  1245. } else {
  1246. //
  1247. // Non-local user case: remove the user from each local group
  1248. //
  1249. Status = NetUserGetLocalGroups (
  1250. NULL,
  1251. FixedExistingUser,
  1252. 0,
  1253. 0,
  1254. (PBYTE *) &lui0,
  1255. MAX_PREFERRED_LENGTH,
  1256. &EntriesRead,
  1257. &Entries
  1258. );
  1259. if (Status != ERROR_SUCCESS) {
  1260. Error = Status;
  1261. DEBUGMSG((
  1262. DM_VERBOSE,
  1263. "pLocalRemapAndMoveUserW: NetUserGetLocalGroups failed with code %u for %s",
  1264. Error,
  1265. FixedExistingUser
  1266. ));
  1267. goto Exit;
  1268. }
  1269. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Got local groups for %s", FixedExistingUser));
  1270. lmi0.lgrmi0_sid = ExistingSid;
  1271. for (Entries = 0 ; Entries < EntriesRead ; Entries++) {
  1272. Status = NetLocalGroupDelMembers (
  1273. NULL,
  1274. lui0[Entries].lgrui0_name,
  1275. 0,
  1276. (PBYTE) &lmi0,
  1277. 1
  1278. );
  1279. if (Status != ERROR_SUCCESS) {
  1280. Error = Status;
  1281. DEBUGMSG((
  1282. DM_VERBOSE,
  1283. "pLocalRemapAndMoveUserW: NetLocalGroupDelMembers failed with code %u for %s",
  1284. Error,
  1285. lui0[Entries].lgrui0_name
  1286. ));
  1287. goto Exit;
  1288. }
  1289. }
  1290. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Removed local group membership"));
  1291. }
  1292. DEBUGMSG((DM_VERBOSE, "pLocalRemapAndMoveUserW: Success"));
  1293. b = TRUE;
  1294. }
  1295. __except (EXCEPTION_EXECUTE_HANDLER) {
  1296. Error = ERROR_NOACCESS;
  1297. }
  1298. Exit:
  1299. if (hToken) {
  1300. CloseHandle (hToken);
  1301. }
  1302. if (lui0) {
  1303. NetApiBufferFree (lui0);
  1304. }
  1305. SmartLocalFree (ExistingSid);
  1306. SmartLocalFree (NewSid);
  1307. SetLastError (Error);
  1308. return b;
  1309. }
  1310. typedef struct {
  1311. PCWSTR Path;
  1312. WCHAR ExpandedPath[MAX_PATH];
  1313. } IGNOREPATH, *PIGNOREPATH;
  1314. BOOL
  1315. IsPatternMatchW (
  1316. IN PCWSTR Pattern,
  1317. IN PCWSTR Str
  1318. )
  1319. {
  1320. WCHAR chSrc, chPat;
  1321. while (*Str) {
  1322. chSrc = (WCHAR) CharLowerW ((PWSTR) *Str);
  1323. chPat = (WCHAR) CharLowerW ((PWSTR) *Pattern);
  1324. if (chPat == L'*') {
  1325. // Skip all asterisks that are grouped together
  1326. while (Pattern[1] == L'*') {
  1327. Pattern++;
  1328. }
  1329. // Check if asterisk is at the end. If so, we have a match already.
  1330. chPat = (WCHAR) CharLowerW ((PWSTR) Pattern[1]);
  1331. if (!chPat) {
  1332. return TRUE;
  1333. }
  1334. // Otherwise check if next pattern char matches current char
  1335. if (chPat == chSrc || chPat == L'?') {
  1336. // do recursive check for rest of pattern
  1337. Pattern++;
  1338. if (IsPatternMatchW (Pattern, Str)) {
  1339. return TRUE;
  1340. }
  1341. // no, that didn't work, stick with star
  1342. Pattern--;
  1343. }
  1344. //
  1345. // Allow any character and continue
  1346. //
  1347. Str++;
  1348. continue;
  1349. }
  1350. if (chPat != L'?') {
  1351. //
  1352. // if next pattern character is not a question mark, src and pat
  1353. // must be identical.
  1354. //
  1355. if (chSrc != chPat) {
  1356. return FALSE;
  1357. }
  1358. }
  1359. //
  1360. // Advance when pattern character matches string character
  1361. //
  1362. Pattern++;
  1363. Str++;
  1364. }
  1365. //
  1366. // Fail when there is more pattern and pattern does not end in an asterisk
  1367. //
  1368. chPat = *Pattern;
  1369. if (chPat && (chPat != L'*' || Pattern[1])) {
  1370. return FALSE;
  1371. }
  1372. return TRUE;
  1373. }
  1374. void FindAndReplaceSD(
  1375. IN PSECURITY_DESCRIPTOR pSD,
  1376. OUT PSECURITY_DESCRIPTOR* ppNewSD,
  1377. IN PCWSTR ExistingSidString,
  1378. IN PCWSTR NewSidString)
  1379. /*++
  1380. Routine Description:
  1381. FindAndReplaceSD replaces ExistingSidString with NewSidString from
  1382. string representation of pSD and return the newly constructed SD
  1383. Arguments:
  1384. pSD - Security Descriptor to replace
  1385. pNewSD - New security descriptor place holder
  1386. ExistingSidString - Specifies the string version of the SID to find.
  1387. NewSidString - Specifies the SID to replace in ACE when
  1388. ExistingSidString is found.
  1389. Return Value:
  1390. None.
  1391. --*/
  1392. {
  1393. LPWSTR szStringSD;
  1394. LPWSTR szNewStringSD;
  1395. if (ppNewSD) {
  1396. *ppNewSD = pSD;
  1397. }
  1398. if (!ConvertSecurityDescriptorToStringSecurityDescriptor( pSD,
  1399. SDDL_REVISION_1,
  1400. DACL_SECURITY_INFORMATION,
  1401. &szStringSD,
  1402. NULL )) {
  1403. DEBUGMSG((DM_VERBOSE, "FindAndReplaceSD: No string found. Error %d", GetLastError()));
  1404. return;
  1405. }
  1406. szNewStringSD = StringSearchAndReplaceW(szStringSD, ExistingSidString, NewSidString, NULL);
  1407. if (szNewStringSD) {
  1408. if (!ConvertStringSecurityDescriptorToSecurityDescriptor( szNewStringSD,
  1409. SDDL_REVISION_1,
  1410. ppNewSD,
  1411. NULL )) {
  1412. DEBUGMSG((DM_VERBOSE, "FindAndReplaceSD: Fail to convert string to SD. Error %d", GetLastError()));
  1413. if (ppNewSD) {
  1414. *ppNewSD = pSD;
  1415. }
  1416. }
  1417. LocalFree(szNewStringSD);
  1418. }
  1419. LocalFree(szStringSD);
  1420. }
  1421. VOID
  1422. pFixDirReference (
  1423. IN PCWSTR CurrentPath,
  1424. IN PCWSTR ExistingSidString,
  1425. IN PCWSTR NewSidString,
  1426. IN PIGNOREPATH IgnoreDirList OPTIONAL
  1427. )
  1428. /*++
  1429. Routine Description:
  1430. pFixDirReference is a recursive function that renames a directory if it
  1431. matches an existing SID exactly. It also updates the SIDs.
  1432. Arguments:
  1433. CurrentPath - Specifies the full file system path.
  1434. ExistingSidString - Specifies the string version of the SID to find.
  1435. NewSidString - Specifies the SID to rename the directory to when
  1436. ExistingSidString is found.
  1437. IgnoreDirList - Specifies a list of directories to ignore. A NULL
  1438. Path member indicates the end of the list, and the
  1439. ExpandedPath member must be filled by the caller.
  1440. Return Value:
  1441. None.
  1442. --*/
  1443. {
  1444. WIN32_FIND_DATA fd;
  1445. HANDLE hFind;
  1446. WCHAR SubPath[MAX_PATH];
  1447. WCHAR NewPath[MAX_PATH];
  1448. BOOL bIgnoreDir;
  1449. UINT u;
  1450. UINT cbNeededLen;
  1451. PSECURITY_DESCRIPTOR pSecurityDesc = NULL;
  1452. PSECURITY_DESCRIPTOR pNewSecurityDesc = NULL;
  1453. HRESULT hr;
  1454. if ((lstrlenW (CurrentPath) + lstrlenW (ExistingSidString) + 2) >= MAX_PATH) {
  1455. return;
  1456. }
  1457. if (*CurrentPath == 0) {
  1458. return;
  1459. }
  1460. hr = StringCchPrintf(SubPath, ARRAYSIZE(SubPath), L"%s\\*", CurrentPath);
  1461. if (FAILED(hr)) {
  1462. return;
  1463. }
  1464. hFind = FindFirstFile (SubPath, &fd);
  1465. if (hFind != INVALID_HANDLE_VALUE) {
  1466. do {
  1467. //
  1468. // Ignore dot and dot-dot
  1469. //
  1470. if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  1471. if (!lstrcmpi (fd.cFileName, L".") || !lstrcmpi (fd.cFileName, L"..")) {
  1472. continue;
  1473. }
  1474. }
  1475. //
  1476. // Rename file/directory or recurse on directory
  1477. //
  1478. if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  1479. hr = StringCchPrintf(SubPath, ARRAYSIZE(SubPath), L"%s\\%s", CurrentPath, fd.cFileName);
  1480. if (FAILED(hr)) {
  1481. continue;
  1482. }
  1483. bIgnoreDir = FALSE;
  1484. if (IgnoreDirList) {
  1485. //
  1486. // Check if this path is to be ignored
  1487. //
  1488. for (u = 0 ; IgnoreDirList[u].Path ; u++) {
  1489. if (IgnoreDirList[u].ExpandedPath[0]) {
  1490. if (IsPatternMatchW (IgnoreDirList[u].ExpandedPath, SubPath)) {
  1491. bIgnoreDir = TRUE;
  1492. }
  1493. }
  1494. }
  1495. }
  1496. //
  1497. // If this path is not to be ignored, recursively fix it
  1498. //
  1499. if (!bIgnoreDir) {
  1500. //
  1501. // Check for reparse point
  1502. //
  1503. if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
  1504. {
  1505. DEBUGMSG((DM_WARNING, "pFixDirReference: Found a reparse point <%s>, will not recurse into it!", SubPath));
  1506. }
  1507. else // Recurse into it
  1508. {
  1509. pFixDirReference (SubPath,
  1510. ExistingSidString,
  1511. NewSidString,
  1512. IgnoreDirList);
  1513. }
  1514. } else {
  1515. //
  1516. // This path is to be ignored
  1517. //
  1518. DEBUGMSG((DM_VERBOSE, "pFixDirReference: Ignoring path %s", SubPath));
  1519. continue;
  1520. }
  1521. }
  1522. if (!lstrcmpi (fd.cFileName, ExistingSidString)) {
  1523. //
  1524. // Rename the SID referenced in the file system
  1525. //
  1526. hr = StringCchPrintf(SubPath, ARRAYSIZE(SubPath), L"%s\\%s", CurrentPath, ExistingSidString);
  1527. if (FAILED(hr)) {
  1528. continue;
  1529. }
  1530. hr = StringCchPrintf(NewPath, ARRAYSIZE(NewPath), L"%s\\%s", CurrentPath, NewSidString);
  1531. if (FAILED(hr)) {
  1532. continue;
  1533. }
  1534. if (MoveFile (SubPath, NewPath)) {
  1535. DEBUGMSG((DM_VERBOSE, "pFixDirReference: Moved %s to %s", SubPath, NewPath));
  1536. }
  1537. else {
  1538. DEBUGMSG((DM_WARNING, "pFixDirReference: Faile to move %s to %s. Error %d", SubPath, NewPath, GetLastError()));
  1539. }
  1540. //
  1541. // Obtain security descriptor of the file/dir and replace it if necessary
  1542. //
  1543. if (!GetFileSecurity (NewPath,
  1544. DACL_SECURITY_INFORMATION,
  1545. NULL,
  1546. 0,
  1547. &cbNeededLen )) {
  1548. pSecurityDesc = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, cbNeededLen);
  1549. if (!pSecurityDesc) {
  1550. continue;
  1551. }
  1552. if (!GetFileSecurity (NewPath,
  1553. DACL_SECURITY_INFORMATION,
  1554. pSecurityDesc,
  1555. cbNeededLen,
  1556. &cbNeededLen )) {
  1557. DEBUGMSG((DM_WARNING, "pFixDirReference: Path %s has no security descriptor", SubPath));
  1558. LocalFree(pSecurityDesc);
  1559. continue;
  1560. }
  1561. FindAndReplaceSD(pSecurityDesc, &pNewSecurityDesc, ExistingSidString, NewSidString);
  1562. if (!SetFileSecurity (NewPath,
  1563. DACL_SECURITY_INFORMATION,
  1564. pNewSecurityDesc) ) {
  1565. DEBUGMSG((DM_WARNING, "pFixDirReference: Fail to update security for %s. Error %d", SubPath, GetLastError()));
  1566. }
  1567. else {
  1568. DEBUGMSG((DM_VERBOSE, "pFixDirReference: Updating security for %s", SubPath));
  1569. }
  1570. if (pNewSecurityDesc && pSecurityDesc != pNewSecurityDesc) {
  1571. LocalFree(pNewSecurityDesc);
  1572. pNewSecurityDesc = NULL;
  1573. }
  1574. if (pSecurityDesc) {
  1575. LocalFree(pSecurityDesc);
  1576. pSecurityDesc = NULL;
  1577. }
  1578. } else {
  1579. DEBUGMSG((DM_VERBOSE, "pFixDirReference: Path %s has no security descriptor", SubPath));
  1580. }
  1581. }
  1582. } while (FindNextFile (hFind, &fd));
  1583. FindClose (hFind);
  1584. }
  1585. }
  1586. BOOL
  1587. pOurExpandEnvironmentStrings (
  1588. IN PCWSTR String,
  1589. OUT PWSTR OutBuffer,
  1590. IN UINT cchBuffer,
  1591. IN PCWSTR UserProfile, OPTIONAL
  1592. IN HKEY UserHive OPTIONAL
  1593. )
  1594. /*++
  1595. Routine Description:
  1596. pOurExpandEnvironmentStrings expands standard environment variables,
  1597. implementing special cases for the variables that have different values
  1598. than what the profmap.dll environment has. In particular, %APPDATA% and
  1599. %USERPROFILE% are obtained by quering the registry.
  1600. Because this routine is private, certain assumptions are made, such as
  1601. the %APPDATA% or %USERPROFILE% environment variables must appear only
  1602. at the begining of String.
  1603. Arguments:
  1604. String - Specifies the string that might contain one or more
  1605. environment variables.
  1606. OutBuffer - Receivies the expanded string
  1607. cchBuffer - Buffer size
  1608. UserProfile - Specifies the root to the user's profile
  1609. UserHive - Specifies the handle of the root to the user's registry hive
  1610. Return Value:
  1611. TRUE if the string was expanded, or FALSE if it is longer than MAX_PATH.
  1612. OutBuffer is always valid upon return. Note that it might be an empty
  1613. string.
  1614. --*/
  1615. {
  1616. WCHAR TempBuf1[MAX_PATH*2];
  1617. WCHAR TempBuf2[MAX_PATH*2];
  1618. PCWSTR CurrentString;
  1619. DWORD Size;
  1620. HKEY Key;
  1621. LONG rc;
  1622. HRESULT hr;
  1623. CurrentString = String;
  1624. //
  1625. // Special case -- replace %APPDATA% with the app data from the user hive
  1626. //
  1627. if (UserHive && pHasPrefix(L"%APPDATA%", CurrentString)) {
  1628. rc = RegOpenKeyEx (
  1629. UserHive,
  1630. L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders",
  1631. 0,
  1632. KEY_READ,
  1633. &Key
  1634. );
  1635. if (rc == ERROR_SUCCESS) {
  1636. Size = MAX_PATH - lstrlen (CurrentString + 1);
  1637. rc = RegQueryValueEx (
  1638. Key,
  1639. L"AppData",
  1640. NULL,
  1641. NULL,
  1642. (PBYTE) TempBuf1,
  1643. &Size
  1644. );
  1645. RegCloseKey (Key);
  1646. }
  1647. if (rc != ERROR_SUCCESS) {
  1648. //
  1649. // In case of an error, use a wildcard
  1650. //
  1651. hr = StringCchCopy(TempBuf1, ARRAYSIZE(TempBuf1), UserProfile);
  1652. if (FAILED(hr)) {
  1653. goto Exit;
  1654. }
  1655. hr = StringCchCat(TempBuf1, ARRAYSIZE(TempBuf1), L"\\*");
  1656. if (FAILED(hr)) {
  1657. goto Exit;
  1658. }
  1659. } else {
  1660. DEBUGMSG ((DM_VERBOSE, "Got AppData path from user hive: %s", TempBuf1));
  1661. }
  1662. hr = StringCchCat(TempBuf1, ARRAYSIZE(TempBuf1), CurrentString + 9);
  1663. if (FAILED(hr)) {
  1664. goto Exit;
  1665. }
  1666. CurrentString = TempBuf1;
  1667. }
  1668. //
  1669. // Special case -- replace %USERPROFILE% with ProfileRoot, because
  1670. // our environment is for another user
  1671. //
  1672. if (UserProfile && pHasPrefix(L"%USERPROFILE%", CurrentString)) {
  1673. hr = StringCchCopy(TempBuf2, ARRAYSIZE(TempBuf2), UserProfile);
  1674. if (FAILED(hr)) {
  1675. goto Exit;
  1676. }
  1677. hr = StringCchCat(TempBuf2, ARRAYSIZE(TempBuf2), CurrentString + 13);
  1678. if (FAILED(hr)) {
  1679. goto Exit;
  1680. }
  1681. CurrentString = TempBuf2;
  1682. }
  1683. //
  1684. // Now replace other environment variables
  1685. //
  1686. Size = ExpandEnvironmentStrings (CurrentString, OutBuffer, cchBuffer);
  1687. if (Size && Size <= cchBuffer) {
  1688. return TRUE;
  1689. }
  1690. Exit:
  1691. *OutBuffer = 0;
  1692. return FALSE;
  1693. }
  1694. typedef struct {
  1695. HKEY hRoot;
  1696. PCWSTR szKey;
  1697. } REGPATH, *PREGPATH;
  1698. VOID
  1699. pFixSomeSidReferences (
  1700. PSID ExistingSid,
  1701. PSID NewSid
  1702. )
  1703. /*++
  1704. Routine Description:
  1705. pFixSomeSidReferences adjusts important parts of the system that use SIDs.
  1706. When a SID changes, this function adjusts the system, so the new SID is
  1707. used and no settings are lost. This function adjusts the registry and file
  1708. system. It does not attempt to adjust SID use whereever a SID might be
  1709. used.
  1710. For Win2k, this code deliberately ignores crypto sid directories, because
  1711. the original SID is used as part of the recovery encryption key. In future
  1712. versions, proper migration of these settings is expected.
  1713. This routine also blows away the ProtectedRoots subkey for the crypto APIs.
  1714. The ProtectedRoots key has an ACL, and when we delete the key, the cyrpto
  1715. APIs will rebuild it with the proper ACL.
  1716. WARNING: We know there is a risk in loss of access to data that was encrypted
  1717. using the SID. Normally the original account will not be removed,
  1718. so the SID will exist on the system, and that (in theory) allows the
  1719. original data to be recovered. But because the cyrpto code gets the
  1720. SID from the file system, there is no way for the user to decrypt
  1721. their data. The future crypto migration code should fix this issue.
  1722. Arguments:
  1723. ExistingSid - Specifies the SID that potentially has settings somewhere on
  1724. the system.
  1725. NewSid - Specifies the SID that is replacing ExistingSid.
  1726. Return Value:
  1727. None.
  1728. --*/
  1729. {
  1730. PWSTR ExistingSidString = NULL;
  1731. PWSTR NewSidString = NULL;
  1732. UINT u;
  1733. WCHAR ExpandedRoot[MAX_PATH];
  1734. WCHAR ProfileRoot[MAX_PATH];
  1735. HKEY UserHive = NULL;
  1736. WCHAR HivePath[MAX_PATH + 14];
  1737. LONG rc;
  1738. HRESULT hr;
  1739. REGPATH RegRoots[] = {
  1740. { HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Protected Storage System Provider" },
  1741. { HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\EventSystem" },
  1742. { HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Installer\\Managed" },
  1743. { HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon" },
  1744. { NULL, NULL }
  1745. };
  1746. PCWSTR DirList[] = {
  1747. L"%SYSTEMROOT%\\system32\\appmgmt",
  1748. NULL
  1749. };
  1750. IGNOREPATH IgnoreDirList[] = {
  1751. {L"%APPDATA%\\Microsoft\\Crypto", L""},
  1752. {L"%APPDATA%\\Microsoft\\Protect", L""},
  1753. {NULL, L""}
  1754. };
  1755. //
  1756. // Get the SIDs in text format
  1757. //
  1758. if (!OurConvertSidToStringSid (ExistingSid, &ExistingSidString)) {
  1759. goto Exit;
  1760. }
  1761. if (!OurConvertSidToStringSid (NewSid, &NewSidString)) {
  1762. goto Exit;
  1763. }
  1764. //
  1765. // Initialize directory strings and load the user hive
  1766. //
  1767. if (!GetProfileRoot(NewSid, ProfileRoot, ARRAYSIZE(ProfileRoot))) {
  1768. goto Exit;
  1769. }
  1770. DEBUGMSG ((DM_VERBOSE, "ProfileRoot (NewSid): %s", ProfileRoot));
  1771. hr = StringCchPrintf(HivePath, ARRAYSIZE(HivePath), L"%s\\ntuser.dat", ProfileRoot);
  1772. if (FAILED(hr)) {
  1773. goto Exit;
  1774. }
  1775. DEBUGMSG ((DM_VERBOSE, "User hive: %s", HivePath));
  1776. rc = RegLoadKey (HKEY_LOCAL_MACHINE, REMAP_KEY_NAME, HivePath);
  1777. if (rc == ERROR_SUCCESS) {
  1778. rc = RegOpenKeyEx (
  1779. HKEY_LOCAL_MACHINE,
  1780. REMAP_KEY_NAME,
  1781. 0,
  1782. KEY_READ|KEY_WRITE,
  1783. &UserHive
  1784. );
  1785. if (rc != ERROR_SUCCESS) {
  1786. RegUnLoadKey (HKEY_LOCAL_MACHINE, REMAP_KEY_NAME);
  1787. DEBUGMSG ((DM_WARNING, "pFixSomeSidReferences: Can't open user hive root, rc=%u", rc));
  1788. UserHive = NULL;
  1789. goto Exit;
  1790. }
  1791. } else {
  1792. DEBUGMSG ((DM_WARNING, "RemapAndMoveUserW: Can't load user's hive, rc=%u", rc));
  1793. goto Exit;
  1794. }
  1795. for (u = 0 ; IgnoreDirList[u].Path ; u++) {
  1796. pOurExpandEnvironmentStrings (
  1797. IgnoreDirList[u].Path,
  1798. IgnoreDirList[u].ExpandedPath,
  1799. ARRAYSIZE(IgnoreDirList[u].ExpandedPath),
  1800. ProfileRoot,
  1801. UserHive
  1802. );
  1803. DEBUGMSG((DM_VERBOSE, "pFixSomeSidReferences: Ignoring %s", IgnoreDirList[u].ExpandedPath));
  1804. }
  1805. //
  1806. // Search and replace select parts of the registry where SIDs are used
  1807. //
  1808. for (u = 0 ; RegRoots[u].hRoot ; u++) {
  1809. RegistrySearchAndReplaceW( RegRoots[u].hRoot,
  1810. RegRoots[u].szKey,
  1811. ExistingSidString,
  1812. NewSidString );
  1813. }
  1814. //
  1815. // Test for directories and rename them
  1816. //
  1817. for (u = 0 ; DirList[u] ; u++) {
  1818. if (pOurExpandEnvironmentStrings (DirList[u], ExpandedRoot, ARRAYSIZE(ExpandedRoot), ProfileRoot, UserHive)) {
  1819. pFixDirReference (ExpandedRoot,
  1820. ExistingSidString,
  1821. NewSidString,
  1822. IgnoreDirList );
  1823. }
  1824. }
  1825. //
  1826. // Fix profile directory
  1827. //
  1828. pFixDirReference (ProfileRoot,
  1829. ExistingSidString,
  1830. NewSidString,
  1831. IgnoreDirList );
  1832. //
  1833. // Crypto special case -- blow away ProtectedRoots key (413828)
  1834. //
  1835. DEBUGMSG ((DM_WARNING, "Can't remove protected roots key, code is currently disabled"));
  1836. if (UserHive) {
  1837. rc = RegDelnode (UserHive, L"Software\\Microsoft\\SystemCertificates\\Root\\ProtectedRoots");
  1838. if (rc != ERROR_SUCCESS) {
  1839. DEBUGMSG ((DM_WARNING, "Can't remove protected roots key, GLE=%u", rc));
  1840. }
  1841. } else {
  1842. DEBUGMSG ((DM_WARNING, "Can't remove protected roots key because user hive could not be opened"));
  1843. }
  1844. Exit:
  1845. //
  1846. // Cleanup
  1847. //
  1848. if (UserHive) {
  1849. RegCloseKey (UserHive);
  1850. RegUnLoadKey (HKEY_LOCAL_MACHINE, REMAP_KEY_NAME);
  1851. }
  1852. if (ExistingSidString) {
  1853. DeleteSidString (ExistingSidString);
  1854. }
  1855. if (NewSidString) {
  1856. DeleteSidString (NewSidString);
  1857. }
  1858. }