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.

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